listing all entries (done) + fix bugs + tweak ui

This commit is contained in:
did 2012-01-04 02:07:53 +01:00
parent 81fc524fdc
commit 0f1b4f59d3
33 changed files with 290 additions and 269 deletions

View File

@ -31,7 +31,7 @@ gem 'flash_cookie_session', '~> 1.1.1'
gem 'locomotive_liquid', '2.2.2', :require => 'liquid'
gem 'formtastic', '~> 2.0.2'
gem 'responders', '~> 0.6.0'
gem 'responders', '~> 0.6.4'
gem 'cells', '~> 3.7.0'
gem 'RedCloth', '~> 4.2.8'
gem 'sanitize', '~> 2.0.3'

View File

@ -341,7 +341,7 @@ DEPENDENCIES
rails (~> 3.1.3)
rails-backbone (= 0.5.4)
rake (= 0.9.2)
responders (~> 0.6.0)
responders (~> 0.6.4)
rmagick (= 2.12.2)
rspec-cells
rspec-rails (= 2.6.1)

View File

@ -20,6 +20,7 @@ class Locomotive.Models.ContentEntry extends Backbone.Model
toJSON: ->
_.tap super, (hash) =>
hash['_slug'] = '' if hash['_slug'] == null # avoid empty hash
_.each _.keys(hash), (key) =>
unless _.include(@get('safe_attributes'), key)
delete hash[key]

View File

@ -0,0 +1,36 @@
Locomotive.Views.ContentEntries ||= {}
class Locomotive.Views.ContentEntries.IndexView extends Backbone.View
el: '#content'
render: ->
@make_sortable()
return @
make_sortable: ->
self = @
@$('ul#entries-list.sortable').sortable
handle: 'span.handle'
items: 'li.item'
axis: 'y'
update: (event, ui) -> self.call_sort $(@)
call_sort: (folder) ->
$.rails.ajax
url: folder.attr('data-url')
type: 'post'
dataType: 'json'
data:
entries: (_.map folder.sortable('toArray'), (el) -> el.replace('entry-', ''))
_method: 'put'
success: @.on_successful_sort
error: @.on_failed_sort
on_successful_sort: (data, status, xhr) ->
$.growl('success', xhr.getResponseHeader('X-Message'))
on_failed_sort: (data, status, xhr) ->
$.growl('error', xhr.getResponseHeader('X-Message'))

View File

@ -1,4 +1,4 @@
Locomotive.Views.Contents ||= {}
Locomotive.Views.ContentEntries ||= {}
class Locomotive.Views.ContentEntries.NewView extends Locomotive.Views.ContentEntries.FormView

View File

@ -33,7 +33,6 @@ class Locomotive.Views.Pages.ListView extends Backbone.View
success: @.on_successful_sort
error: @.on_failed_sort
# on_sort: (data) ->
on_successful_sort: (data, status, xhr) ->
$.growl('success', xhr.getResponseHeader('X-Message'))

View File

@ -5,7 +5,7 @@ class Locomotive.Views.Shared.ListItemView extends Backbone.View
tagName: 'li'
events:
'click a.remove': 'remove_snippet'
'click a.remove': 'remove_item'
template: ->
# please overide template
@ -15,7 +15,7 @@ class Locomotive.Views.Shared.ListItemView extends Backbone.View
return @
remove_snippet: (event) ->
remove_item: (event) ->
event.stopPropagation() & event.preventDefault()
if confirm $(event.target).attr('data-confirm')

View File

@ -58,7 +58,7 @@
height: 30px;
line-height: 30px;
padding: 0 10px;
padding: 0 13px;
background: #fff;
@include border-radius(16px);
@ -70,6 +70,14 @@
font-weight: normal;
}
span.handle {
display: none;
position: relative;
top: 3px;
margin-right: 5px;
cursor: move;
}
strong {
color: #17171D;
font-size: 14px;
@ -100,7 +108,7 @@
div.more, span.actions {
position: absolute;
top: 0px;
right: 10px;
right: 13px;
color: #8B8D9A;
font-size: 11px;
@ -137,6 +145,14 @@
}
} // li
&.sortable {
li {
span.handle {
display: inline-block;
}
}
}
> hr {
margin: 10px 0px;
height: 1px;

View File

@ -37,7 +37,7 @@
@mixin light-button {
display: inline-block;
font-family: "Lucida Grande";
// font-family: "Lucida Grande";
position: relative;
cursor: pointer;
@ -65,6 +65,20 @@
}
}
@mixin red-button {
@include light-button;
@include background-image(linear-gradient(top, #EE5F5B, #C43C35));
@include box-shadow(rgba(0, 0, 0, 0.4) 1px 1px 0px 0px);
@include text-shadow(rgba(0, 0, 0, 0.7) 0px -1px 0px);
color: #fff;
&:hover, &.hover {
background: #C43C35;
}
}
@mixin gray-button {
position: relative;
padding: 2px 10px 3px 31px;

View File

@ -102,36 +102,6 @@ ul.list {
outline: none;
}
// em {
// display: block;
// float: left;
// height: 31px;
// width: 18px;
// }
// strong {
// display: block;
// height: 31px;
// margin-left: 18px;
//
// a {
// position: relative;
// top: 2px;
// left: 15px;
//
// text-decoration: none;
// color: #1f82bc;
// font-size: 13px;
// @include single-text-shadow(#fff, 1px, 1px, 1px);
//
// &:hover {
// text-decoration: underline;
// }
// }
// }
// &.sortable li strong a { left: 10px; }
div.more {
position: absolute;
top: 0px;
@ -146,41 +116,68 @@ ul.list {
margin-left: 10px;
}
// span.handle {
// position: relative;
// top: 5px;
// margin: 0 0 0 15px;
// cursor: move;
// }
} // li div.more
} // ul.list li
}
// ul.theme-assets {
// margin-left: 40px;
//
// li.hidden strong a { font-style: italic; color: #8B8D9A; font-weight: normal; }
// }
#entries-list.list {
// #contents-list li {
// background: none;
//
// li {
// em {
// background-position: left -31px;
// cursor: move;
// }
//
// strong {
// display: block;
// height: 31px;
//
// margin-left: 18px;
//
// background: transparent image-url("locomotive/list/item-right.png") no-repeat right 0;
// }
// }
// }
li.item {
a {
@include single-text-shadow(#fff, 1px, 1px, 1px);
}
span.handle {
display: none;
}
.more {
a.remove {
display: inline-block;
width: 16px;
height: 16px;
margin-left: 7px;
outline: none;
background: transparent image-url("locomotive/list/icons/trash_off.png") repeat 0 0;
text-indent: -9999px;
&:hover {
background-image: image-url("locomotive/list/icons/trash.png");
}
}
} // li div.more
} // li.item
&.sortable {
li.item {
span.handle {
display: block;
position: absolute;
top: 5px;
left: 0px;
height: 22px;
width: 18px;
text-indent: -9999px;
background: transparent image-url("locomotive/list/item-left.png") no-repeat 0 0;
cursor: move;
}
a {
margin-left: 26px;
}
} // .sortable li.item
} // .list.sortable
} // #entries-list.list
#pages-list {
margin: 0px 0 20px 0;

View File

@ -336,6 +336,13 @@ form.formtastic {
}
&.no-label {
> textarea, .CodeMirror, .CodeMirror-scroll {
margin-top: 12px;
width: 868px;
}
}
} // li.code
&.toggle {
@ -423,10 +430,14 @@ form.formtastic {
input[type=text] {
width: 868px;
}
}
div.inline-errors {
margin-left: 0px;
}
> .inline-hints {
margin-left: 0px !important;
}
> .inline-errors {
margin-left: 0px !important;
}
} // li.no-label

View File

@ -93,7 +93,7 @@ body {
> div.inner {
position: relative;
margin: 0px 8px;
padding: 10px 15px 20px 15px;
padding: 10px 15px 66px 15px;
// z-index: 100;
@ -102,7 +102,7 @@ body {
@include box-shadow(rgba(0, 0, 0, 0.3) 0px 0px 6px 4px);
@include border-bottom-radius(4px);
min-height: 150px;
min-height: 290px;
h2 {
@include locomotive-link;
@ -120,11 +120,9 @@ body {
} // > div.inner h2
h3 {
background: transparent image-url("locomotive/list/item.png") no-repeat 0 0;
padding: 7px 0 10px 20px;
font-size: 13px;
font-size: 14px;
font-weight: bold;
color: #1e1f26;
@include single-text-shadow(#fff, 1px, 1px, 1px);
@ -149,9 +147,10 @@ body {
} // #content #local-actions-bar
#local-actions-bottom-bar {
position: relative;
top: 20px;
left: -15px;
position: absolute;
bottom: 0px;
left: 0px;
height: 62px;
width: 950px;
background: #8b8d9a;
@ -162,8 +161,6 @@ body {
padding: 15px;
margin: 0px;
line-height: 13px;
a {
position: relative;
top: 4px;
@ -172,7 +169,10 @@ body {
color: #fff;
font-size: 12px;
&.remove { color: #ff092c; }
&.remove {
@include red-button;
top: 0px;
}
}
}

View File

@ -12,7 +12,7 @@ module Locomotive
before_filter :authorize_content
def index
@content_entries = @content_type.entries
@content_entries = @content_type.list_or_group_entries
respond_with @content_entries
end
@ -38,17 +38,14 @@ module Locomotive
end
def sort
# TODO
# @content_type.sort_contents!(params[:children])
# @page = current_site.pages.find(params[:id])
# @page.sort_children!(params[:children])
@content_type.klass_with_custom_fields(:entries).sort_entries!(params[:entries])
respond_with @content_type
end
def destroy
@content_entry = @content_type.entries.find(params[:id])
@content_entry.destroy
respond_with @content_entry, :location => pages_url
respond_with @content_entry, :location => content_entries_url(@content_type.slug)
end
protected

View File

@ -18,6 +18,22 @@ module Locomotive::ContentTypesHelper
end
end
def entry_label(content_type, entry)
if content_type.raw_item_template.blank?
entry._label # default one
else
assigns = { 'site' => current_site, 'entry' => entry }
registers = {
:controller => self,
:site => current_site,
:current_locomotive_account => current_locomotive_account
}
preserve(content_type.item_template.render(::Liquid::Context.new({}, assigns, registers)))
end
end
# MAX_DISPLAYED_CONTENTS = 4
#
# def fetch_content_types

View File

@ -23,7 +23,7 @@ module Locomotive
before_validation :set_slug
before_save :set_visibility
before_create :add_to_list_bottom
# after_create :send_notifications
after_create :send_notifications
## named scopes ##
scope :visible, :where => { :_visible => true }
@ -51,8 +51,17 @@ module Locomotive
next_or_previous :lt
end
def self.sort_entries!(ids)
list = self.any_in(:_id => ids.map { |id| BSON::ObjectId.from_string(id.to_s) }).to_a
ids.each_with_index do |id, position|
if entry = list.detect { |e| e._id.to_s == id.to_s }
entry.update_attributes :_position => position
end
end
end
def to_liquid
Locomotive::Liquid::Drops::Content.new(self)
Locomotive::Liquid::Drops::ContentEntry.new(self)
end
def to_presenter
@ -66,37 +75,35 @@ module Locomotive
protected
def next_or_previous(matcher = :gt)
attribute = self.content_type.order_by.to_sym
direction = self.content_type.order_direction || 'asc'
order_by = self.content_type.order_by_definition
criterion = attribute.send(matcher)
self.class.where(criterion => self.send(attribute)).order_by([[attribute, direction]]).limit(1).first
self.class.where(criterion => self.send(attribute)).order_by([order_by]).limit(1).first
end
# Sets the slug of the instance by using the value of the highlighted field
# (if available). If a sibling content instance has the same permalink then a
# unique one will be generated
def set_slug
# self._slug = highlighted_field_value.dup if _slug.blank? && highlighted_field_value.present?
#
# if _slug.present?
# self._slug.permalink!
# self._slug = next_unique_slug if slug_already_taken?
# end
self._slug = self._label.dup if self._slug.blank? && self._label.present?
if self._slug.present?
self._slug.permalink!
self._slug = self.next_unique_slug if self.slug_already_taken?
end
end
# Return the next available unique slug as a string
def next_unique_slug
# slug = _slug.gsub(/-\d*$/, '')
# last_slug = _parent.contents.where(:_id.ne => _id, :_slug => /^#{slug}-?\d*?$/i).order_by(:_slug).last._slug
# next_number = last_slug.scan(/-(\d)$/).flatten.first.to_i + 1
#
# [slug, next_number].join('-')
slug = self._slug.gsub(/-\d*$/, '')
last_slug = self.class.where(:_id.ne => self._id, :_slug => /^#{slug}-?\d*?$/i).order_by(:_slug).last._slug
next_number = last_slug.scan(/-(\d)$/).flatten.first.to_i + 1
[slug, next_number].join('-')
end
# def slug_already_taken?
# _parent.contents.where(:_id.ne => _id, :_slug => _slug).any?
# end
def slug_already_taken?
self.class.where(:_id.ne => self._id, :_slug => self._slug).any?
end
def set_visibility
[:visible, :active].each do |meth|
@ -111,19 +118,15 @@ module Locomotive
self._position = self.class.max(:_position).to_i + 1
end
# def send_notifications
# return unless self.content_type.api_enabled? && !self.content_type.api_accounts.blank?
#
# accounts = self.content_type.site.accounts.to_a
#
# self.content_type.api_accounts.each do |account_id|
# next if account_id.blank?
#
# account = accounts.detect { |a| a.id.to_s == account_id.to_s }
#
# Locomotive::Notifications.new_content_instance(account, self).deliver
# end
# end
def send_notifications
return if !self.content_type.public_form_enabled? || self.content_type.public_form_accounts.blank?
self.content_type.site.accounts.each do |account|
next unless self.content_type.public_form_accounts.include?(account._id.to_s)
Locomotive::Notifications.new_content_entry(account, self).deliver
end
end
end
end

View File

@ -44,20 +44,33 @@ module Locomotive
## methods ##
# def groupable?
# self.group_by_field && group_by_field.category?
# end
def group_by_field
self.entries_custom_fields.find(self.group_by_field_id)
end
def order_manually?
self.order_by == '_position'
end
def asc_order?
self.order_direction.blank? || self.order_direction == 'asc'
def order_by_definition
direction = self.order_manually? ? 'asc' : self.order_direction || 'asc'
[order_by_attribute, direction]
end
def ordered_entries
self.entries.order_by([order_by_definition])
end
def groupable?
!!self.group_by_field && group_by_field.type == 'select'
end
def group_by_field
self.entries_custom_fields.find(self.group_by_field_id) rescue nil
end
def list_or_group_entries
if self.groupable?
self.entries.group_by_select_option(self.group_by_field.name, self.order_by_definition)
else
self.ordered_entries
end
end
def to_presenter
@ -135,6 +148,11 @@ module Locomotive
protected
def order_by_attribute
return self.order_by if %w(created_at updated_at _position).include?(self.order_by)
self.entries_custom_fields.find(self.order_by).name rescue 'created_at'
end
def set_default_values
self.order_by ||= 'created_at'
field = self.entries_custom_fields.find(self.label_field_id) rescue self.entries_custom_fields.first

View File

@ -38,7 +38,7 @@ module Locomotive
end
def accounts
Account.criteria.in(:_id => self.memberships.collect(&:account_id))
Account.criteria.in(:_id => self.memberships.map(&:account_id))
end
def admin_memberships

View File

@ -1,15 +1,15 @@
- if contents.empty?
%p.no-items!= t('.no_items', :url => new_content_entry_url(@content_type.slug))
- if entries.empty?
%p.no-items!= t('.no_items', :url => new_content_entry_url(content_type.slug))
- else
%ul{ :id => 'contents-list', :class => "list #{'sortable' if @content_type.order_by == '_position'}", :'data-url' => sort_admin_contents_path(@content_type.slug, :json) }
- contents.each do |content|
%li.content{ :id => "content-#{content._id}" }
%em
%strong
= link_to content_label_for(content), edit_admin_content_path(@content_type.slug, content)
%ul{ :id => 'entries-list', :class => "#{'list' unless content_type.groupable?} #{'sortable' if content_type.order_manually?}", :'data-url' => sort_content_entries_url(content_type.slug, :json) }
- entries.each do |entry|
%li.item{ :id => "entry-#{entry._id}" }
%span.handle
= image_tag 'locomotive/form/icons/drag.png'
%strong= link_to entry_label(content_type, entry), edit_content_entry_url(content_type.slug, entry)
.more
%span
!= t('locomotive.contents.index.updated_at')
= l content.updated_at, :format => :short rescue 'n/a'
!= t('locomotive.content_entries.index.updated_at')
= l entry.updated_at, :format => :short rescue 'n/a'
= link_to image_tag('admin/list/icons/trash.png', :alt => t('locomotive.buttons.delete')), admin_content_path(@content_type.slug, content), :class => 'remove', :confirm => t('locomotive.messages.confirm'), :method => :delete
= link_to 'x', content_entry_url(content_type.slug, entry), :class => 'remove', :confirm => t('locomotive.messages.confirm'), :method => :delete

View File

@ -6,9 +6,6 @@
- content_for :actions do
= render 'locomotive/shared/actions/contents'
- content_for :head do
= include_javascripts :contents
- content_for :buttons do
- if can?(:manage, Locomotive::ContentType)
= local_action_button :edit, edit_content_type_url(@content_type), :class => 'edit'
@ -19,14 +16,15 @@
%p= @content_type.description
- if @content_type.groupable?
- @contents.each do |group|
%h3= group[:name] || t('.category_noname')
= render 'list', :contents => group[:items]
%br
- @content_entries.each do |group|
.box
%h3= group[:name] || t('.category_noname')
.inner
= render 'list', :content_type => @content_type, :entries => group[:entries]
- else
= render 'list', :contents => @contents
= render 'list', :content_type => @content_type, :entries => @content_entries
- if can?(:manage, Locomotive::ContentType)
#local-actions-bottom-bar
%p.tleft
= link_to(content_tag(:em, escape_once(' ')) + t('.destroy'), content_type_url(@content_type), :confirm => t('locomotive.messages.confirm'), :method => :delete, :class => 'button small remove')
= link_to t('.destroy'), content_type_url(@content_type), :confirm => t('locomotive.messages.confirm'), :method => :delete, :class => 'button remove'

View File

@ -30,4 +30,4 @@
- if can?(:manage, current_site)
= f.inputs :name => :robots_txt, :class => "inputs foldable #{'folded' if inputs_folded?(@site)}" do
= f.input :robots_txt, :as => :'Locomotive::Code', :picker => false, :wrapper_html => { :class => 'small' }
= f.input :robots_txt, :as => :'Locomotive::Code', :picker => false, :wrapper_html => { :class => 'small no-label' }

View File

@ -2,7 +2,7 @@
!!!
%html{ :xmlns => 'http://www.w3.org/1999/xhtml' }
%head
%title= yield(:head_title) || escape_once("#{Locomotive.config.name} — #{current_site.name}")
%title= title || escape_once("#{Locomotive.config.name} — #{current_site.name}")
= javascript_include_tag 'jquery.js'
= stylesheet_link_tag 'locomotive/not_logged_in', :media => 'screen'

View File

@ -250,7 +250,7 @@ en:
lastest_entries: "Lastest entries"
updated_at: "Updated at"
list:
no_entries: "There are no entries for now. Just click <a href=\"%{url}\">here</a> to create the first one."
no_items: "There are no entries for now. Just click <a href=\"%{url}\">here</a> to create the first one."
new:
title: '%{type} &mdash; new entry'
edit:

View File

@ -94,7 +94,7 @@ en:
content_type:
name: "We suggest you to type the plural form of the model. Ex: Projects, Recipes, Posts, Articles, ...etc"
slug: "It will be used as the name of the collection in the liquid templates. Ex: <span class='code'>{{ contents.my_projects }}</span>"
raw_item_template: "You can customize the text displayed for each item in the list. Simply use Liquid. Ex: <span class='code'>{{ content.name }})</span>"
raw_item_template: "You can customize the text displayed for each item in the list. Simply use Liquid. Ex: <span class='code'>{{ entry.name }})</span>"
public_form_enabled: "It is used to let people from outside to create new instances (example: messages in a contact form)"
public_form_accounts: "A notification email will be sent to each of the accounts listed above when a new instance is created"
"custom_fields/field":

View File

@ -79,6 +79,6 @@ es:
samples: "Si se activa, la importación también copiará contenido multimedia y páginas"
reset: "Si se activa, toda la información del sitio será eliminada antes de importar"
content_type:
raw_item_template: "Puede personalizar el texto mostrado para cada elemento de la lista. Simplemente utilice Liquid. Ej: {{ content.name }})"
raw_item_template: "Puede personalizar el texto mostrado para cada elemento de la lista. Simplemente utilice Liquid. Ej: {{ entry.name }})"
api_enabled: "Se utiliza para dejar crear elementos a gente externa a la aplicación (ejemplo: mensajes en un formulario de contacto)"
api_accounts: "Un email de notificación será enviado a las cuentas listadas más arriba cuando un nuevo elemento se cree"

View File

@ -94,6 +94,6 @@ fr:
reset: "Si activé, toutes les données de votre site seront détruites avant l'import du nouveau site"
content_type:
slug: Nom utilisé dans les templates liquid afin d'accéder aux enregistrements de ce modèle
item_template: "Personnaliser le texte affiché pour chaque élément de la liste. Utilisez simplement du code Liquid. Ex: {{ content.name }}"
item_template: "Personnaliser le texte affiché pour chaque élément de la liste. Utilisez simplement du code Liquid. Ex: {{ entry.name }}"
api_enabled: "Utilisé pour autoriser la création de nouvelles instances de l'extérieur (ex.: les messages dans un formulaire de contact)"
api_accounts: "Un email de notification sera envoyé à chaque compte listé ci-dessus lors de la création d'une nouvelle instance"

View File

@ -93,7 +93,7 @@ it:
samples: "Se attivato, il processo di import copierà anche contenuti e risorse"
reset: "Se attivato, tutti i dati del tuo sito saranno cancellati prima di importare il nuovo sito"
content_type:
item_template: "Puoi personalizzare il testo visualizzato per ciascun elemento della lista semplicemente usando Liquid. Ex: {{ content.name }})"
item_template: "Puoi personalizzare il testo visualizzato per ciascun elemento della lista semplicemente usando Liquid. Ex: {{ entry.name }})"
api_enabled: "E' usato per lasciare che i visitatori, da fuori, possano creare nuove istanze (per esempio: i messaggi da un form 'contattaci')"
api_accounts: "Quando viene creata una nuova istanza, una mail di notifica verrà inviata a tutti gli account elencati qui sopra"

View File

@ -79,7 +79,7 @@ nl:
samples: "Het importeren zal ook de contents en bronbestanden copiëren als deze optie aanstaat"
reset: "Alle data van uw website zal verwijderd worden voordat de nieuwe website wordt geimporteerd als deze optie aanstaat"
content_type:
raw_item_template: "U kunt de tekst voor elk item in de lijst aanpassen. Ie: {{ content.name }})"
raw_item_template: "U kunt de tekst voor elk item in de lijst aanpassen. Ie: {{ entry.name }})"
api_enabled: "It is used to let people from outside to create new instances (example: messages in a contact form)"
api_enabled: "Wordt gebruikt om mensen van buiten nieuwe instanties te laten creëren (voorbeeld: berichten in een contactformulier)"
api_accounts: "Een e-mail met een aankondiging zal worden verstuurd naar de bovenstaande accounts wanneer een nieuwe instantie wordt gecreëerd"

View File

@ -93,7 +93,7 @@
samples: "Vil gjøre at importen kopierer innhold og assets"
reset: "Vil gjøre at alle data slettes for den nye siden importeres."
content_type:
item_template: "Du kan justere teksten som vises for hvert element i listen. Bruk liquid, f.eks: {{ content.name }}"
item_template: "Du kan justere teksten som vises for hvert element i listen. Bruk liquid, f.eks: {{ entry.name }}"
api_enabled: "Brukes for å la sidebrukere opprette ny elementer (f.eks: meldinger i et kontaktskjema)"
api_accounts: "En varslingsepost vil bli sendt til alle kontoene ovenfor når et nytt element blir opprettet."

View File

@ -93,7 +93,7 @@ ru:
samples: "Если включено, процесс импорта также скопирует содержимое и файлы"
reset: "Если включено, все данные вашего сайта будут уничтожены перед импортом нового сайта"
content_type:
item_template: "Вы можете задавать текст, отображаемый для каждого элемента в списке. Просто используйте Liquid. Пр.: {{ content.name }})"
item_template: "Вы можете задавать текст, отображаемый для каждого элемента в списке. Просто используйте Liquid. Пр.: {{ entry.name }})"
api_enabled: "Это используется для того, чтобы дать людям извне возможность создавать новые экземляры (пример: сообщения в форме контакта)"
api_accounts: "Письмо с уведомлением будет отправлено на каждый аккаунт из списка выше, когда создан новый экземпляр"

View File

@ -34,7 +34,7 @@ x edit my site
x site picker
- content types
x move content instances into their own collection
- manage custom_fields
x manage custom_fields
x automatic name
x required
x editable plugin: add class depending on the type => surrounding span instead
@ -50,17 +50,17 @@ x edit my site
x select: add/edit/remove options
x text: formatting
- change in main menu
- manage contents
- list (highlighted field)
- slugify
- crud
x manage contents
x list (highlighted field)
x slugify
x crud
x new
x date
x checkbox
x html
x file
x edit
- destroy
x destroy
- public_form (previously api something)
- disallow to click twice on the submit form button (spinner ?)

View File

@ -1,14 +1,14 @@
module Locomotive
module Liquid
module Drops
class Content < Base
class ContentEntry < Base
delegate :seo_title, :meta_keywords, :meta_description, :to => '_source'
delegate :_slug, :_permalink, :seo_title, :meta_keywords, :meta_description, :to => '_source'
def _id
self._source._id.to_s
end
# Returns the next content for the parent content type.
# If no content is found, nil is returned.
#
@ -21,7 +21,7 @@ module Locomotive
def next
self._source.next.to_liquid
end
# Returns the previous content for the parent content type.
# If no content is found, nil is returned.
#
@ -43,8 +43,8 @@ module Locomotive
end
end
def highlighted_field_value
self._source.highlighted_field_value
def _label
self._label
end
end

View File

@ -15,91 +15,6 @@ end
# Limit feature for embedded documents
module Mongoid #:nodoc:
module Criterion #:nodoc:
module Exclusion
def only(*args)
clone.tap do |crit|
crit.options[:fields] ||= {}
crit.options[:fields][:only] = args.flatten if args.any?
end
end
end
module Optional
# Adds a criterion to the +Criteria+ that specifies the maximum number of
# results to return. This is mostly used in conjunction with <tt>skip()</tt>
# to handle paginated results.
#
# Options:
#
# value: An +Integer+ specifying the max number of results. Defaults to 20.
# hash: A +Hash+ specifying the max number of results for the embedded collections.
#
# Example:
#
# <tt>criteria.limit(100)</tt>
# <tt>criteria.limit(100, { :contents => 5 })</tt>
# <tt>criteria.limit(:contents => 5)</tt>
#
# Returns: <tt>self</tt>
def limit(*args)
clone.tap do |crit|
arguments = args.first || 20
fields = nil # hash of embedded collections
case arguments
when Integer
crit.options[:limit] = arguments
fields = args[1] if args.size > 1
when Hash
fields = arguments
end
if fields
crit.options[:fields] ||= {}
crit.options[:fields][:limit] = fields
end
end
end
end
end
module Contexts #:nodoc:
class Mongo
# Filters the field list. If no fields have been supplied, then it will be
# empty. If fields have been defined then _type will be included as well.
def process_options
fields = options[:fields]
only = fields.delete(:only) if fields
limits = fields.delete(:limit) if fields
# only ?
if only && only.size > 0
only << :_type if !only.include?(:_type)
only.each do |field|
options[:fields].merge!(field => 1)
end
end
# limit for embedded collections ?
if limits && limits.size > 0
limits.each do |field, limit|
next if limit.blank?
options[:fields][field] = { '$slice' => limit }
end
end
options.dup
end
end
end
# without callback feature
module Callbacks

View File

@ -46,16 +46,16 @@ describe Locomotive::Import::Job do
content_type = @site.content_types.where(:slug => 'projects').first
content_type.entries.size.should == 5
content = content_type.entries.first
content._permalink.should == 'locomotivecms'
content.seo_title.should == 'My open source CMS'
content.meta_description.should == 'bla bla bla'
content.meta_keywords.should == 'cms ruby engine mongodb'
content.name.should == 'Locomotive App'
content.thumbnail.url.should_not be_nil
content.featured.should == true
content.client.name.should == 'My client #1'
content.team.first.name.should == 'Michael Scott'
entry = content_type.entries.first
entry._permalink.should == 'locomotivecms'
entry.seo_title.should == 'My open source CMS'
entry.meta_description.should == 'bla bla bla'
entry.meta_keywords.should == 'cms ruby engine mongodb'
entry.name.should == 'Locomotive App'
entry.thumbnail.url.should_not be_nil
entry.featured.should == true
entry.client.name.should == 'My client #1'
entry.team.first.name.should == 'Michael Scott'
end
it 'inserts theme assets' do