refactoring the creation of custom contents

This commit is contained in:
did 2011-12-22 14:28:12 -08:00
parent 2068fe060d
commit 9130544516
19 changed files with 183 additions and 245 deletions

View File

@ -13,8 +13,8 @@ gem 'bson_ext', '~> 1.4.0'
gem 'mongoid', '~> 2.3.3' gem 'mongoid', '~> 2.3.3'
gem 'locomotive_mongoid_acts_as_tree', :git => 'git@github.com:locomotivecms/mongoid_acts_as_tree.git' gem 'locomotive_mongoid_acts_as_tree', :git => 'git@github.com:locomotivecms/mongoid_acts_as_tree.git'
# gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git' # gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git'
# gem 'custom_fields', :path => '../gems/custom_fields' # DEV gem 'custom_fields', :path => '../gems/custom_fields' # DEV
gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => 'experimental' # gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => 'experimental'
gem 'kaminari' gem 'kaminari'
gem 'haml', '~> 3.1.3' gem 'haml', '~> 3.1.3'

View File

@ -0,0 +1,11 @@
class Locomotive.Models.Content extends Backbone.Model
paramRoot: 'content'
urlRoot: "#{Locomotive.mount_on}/content_type/:slug/contents"
class Locomotive.Models.ContentsCollection extends Backbone.Collection
model: Locomotive.Models.Content
url: "#{Locomotive.mount_on}/content_type/:slug/contents"

View File

@ -12,8 +12,6 @@ class Locomotive.Views.ContentTypes.FormView extends Locomotive.Views.Shared.For
initialize: -> initialize: ->
@model = new Locomotive.Models.ContentType(@options.content_type) @model = new Locomotive.Models.ContentType(@options.content_type)
window.foo = @model
Backbone.ModelBinding.bind @ Backbone.ModelBinding.bind @
render: -> render: ->

View File

@ -0,0 +1,21 @@
#= require ../shared/form_view
Locomotive.Views.Contents ||= {}
class Locomotive.Views.Contents.FormView extends Locomotive.Views.Shared.FormView
el: '#content'
events:
'submit': 'save'
initialize: ->
@model = new Locomotive.Models.Content(@options.content)
Backbone.ModelBinding.bind @
render: ->
super()
return @

View File

@ -0,0 +1,6 @@
Locomotive.Views.Contents ||= {}
class Locomotive.Views.Contents.EditView extends Locomotive.Views.Contents.FormView
save: (event) ->
@save_in_ajax event

View File

@ -0,0 +1,8 @@
Locomotive.Views.Contents ||= {}
class Locomotive.Views.Contents.NewView extends Locomotive.Views.Contents.FormView
save: (event) ->
@save_in_ajax event,
on_success: (response, xhr) ->
window.location.href = xhr.getResponseHeader('location')

View File

@ -11,36 +11,44 @@ module Locomotive
before_filter :authorize_content before_filter :authorize_content
helper_method :breadcrumb_root, :breadcrumb_url, :back_url
def index def index
@contents = @content_type.list_or_group_contents @contents = @content_type.contents
respond_with @contents
end end
def new def new
new! { @content.attributes = params[:content] } @content = @content_type.contents.build
respond_with @content
end end
def create def create
create! { after_create_or_update_url } @content = @content_type.contents.create(params[:content])
respond_with @content, :location => edit_content_url(@content_type.slug, @content._id)
end end
def edit def edit
edit! { @content.attributes = params[:content] } @content = @content_type.contents.find(params[:id])
respond_with @content
end end
def update def update
update! { after_create_or_update_url } @content = @content_type.contents.find(params[:id])
@content.update_attributes(params[:content])
respond_with @content, :location => edit_content_url(@content_type.slug, @content._id)
end end
def sort def sort
@content_type.sort_contents!(params[:children]) # TODO
# @content_type.sort_contents!(params[:children])
respond_with(@content_type, :location => contents_url(@content_type.slug)) # @page = current_site.pages.find(params[:id])
# @page.sort_children!(params[:children])
respond_with @content_type
end end
def destroy def destroy
destroy! { contents_url(@content_type.slug) } @content = @content_type.contents.find(params[:id])
@content.destroy
respond_with @content, :location => pages_url
end end
protected protected
@ -49,35 +57,9 @@ module Locomotive
@content_type ||= current_site.content_types.where(:slug => params[:slug]).first @content_type ||= current_site.content_types.where(:slug => params[:slug]).first
end end
def begin_of_association_chain
set_content_type
end
def after_create_or_update_url
if params[:breadcrumb_alias].blank?
edit_content_url(@content_type.slug, @content.id)
else
self.breadcrumb_url
end
end
def authorize_content def authorize_content
authorize! params[:action].to_sym, ContentInstance authorize! params[:action].to_sym, ContentInstance
end end
def breadcrumb_root
return nil if params[:breadcrumb_alias].blank?
@breadcrumb_root ||= resource.send(params[:breadcrumb_alias].to_sym)
end
def breadcrumb_url
edit_content_url(self.breadcrumb_root._parent.slug, self.breadcrumb_root)
end
def back_url
self.breadcrumb_root ? self.breadcrumb_url : contents_url(@content_type.slug)
end
end end
end end

View File

@ -36,13 +36,13 @@ module Locomotive::BaseHelper
end end
end end
def collection_to_js(collection, options = {}) # def collection_to_js(collection, options = {}) # FIXME: DEPRECATED
js = collection.collect { |object| object.to_json } # js = collection.collect { |object| object.to_json }
#
options_to_js = ActiveSupport::JSON.encode(options).gsub(/^\{/, '').gsub(/\}$/, '') # options_to_js = ActiveSupport::JSON.encode(options).gsub(/^\{/, '').gsub(/\}$/, '')
#
"new Object({ \"collection\": [#{js.join(', ')}], #{options_to_js} })" # "new Object({ \"collection\": [#{js.join(', ')}], #{options_to_js} })"
end # end
def flash_message def flash_message
if not flash.empty? if not flash.empty?

View File

@ -8,25 +8,24 @@ module Locomotive
include Extensions::Shared::Seo include Extensions::Shared::Seo
## fields (dynamic fields) ## ## fields (dynamic fields) ##
field :_highlighted_field # field :_highlighted_field
field :_slug field :_slug
field :_position_in_list, :type => Integer, :default => 0 field :_position_in_list, :type => Integer, :default => 0
field :_visible, :type => Boolean, :default => true field :_visible, :type => Boolean, :default => true
## validations ## ## validations ##
validate :require_highlighted_field # validate :require_highlighted_field
validates :_slug, :presence => true, :uniqueness => { :scope => :content_type_id } validates :_slug, :presence => true, :uniqueness => { :scope => :content_type_id }
## associations ## ## associations ##
belongs_to :site belongs_to :site
belongs_to :content_type, :class_name => 'Locomotive::ContentType', :inverse_of => :contents belongs_to :content_type, :class_name => 'Locomotive::ContentType', :inverse_of => :contents
# embedded_in :content_type, :class_name => 'Locomotive::ContentType', :inverse_of => :contents
## callbacks ## ## callbacks ##
before_validation :set_slug before_validation :set_slug
before_save :set_visibility # before_save :set_visibility
before_create :add_to_list_bottom before_create :add_to_list_bottom
after_create :send_notifications # after_create :send_notifications
## named scopes ## ## named scopes ##
scope :visible, :where => { :_visible => true } scope :visible, :where => { :_visible => true }
@ -34,21 +33,15 @@ module Locomotive
## methods ## ## methods ##
# delegate :site, :to => :content_type
alias :visible? :_visible? alias :visible? :_visible?
alias :_permalink :_slug alias :_permalink :_slug
alias :_permalink= :_slug= alias :_permalink= :_slug=
# def site_id # needed by the uploader of custom fields # def highlighted_field_value
# self.content_type.site_id # self.send(self.content_type.highlighted_field_name)
# end # end
#
def highlighted_field_value # alias :_label :highlighted_field_value
self.send(self.content_type.highlighted_field_name)
end
alias :_label :highlighted_field_value
def visible? def visible?
self._visible || self._visible.nil? self._visible || self._visible.nil?
@ -78,72 +71,81 @@ module Locomotive
Locomotive::Liquid::Drops::Content.new(self) Locomotive::Liquid::Drops::Content.new(self)
end end
def to_presenter
Locomotive::ContentPresenter.new(self)
end
def as_json(options = {})
self.to_presenter.as_json
end
protected protected
# Sets the slug of the instance by using the value of the highlighted field # 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 # (if available). If a sibling content instance has the same permalink then a
# unique one will be generated # unique one will be generated
def set_slug def set_slug
self._slug = highlighted_field_value.dup if _slug.blank? && highlighted_field_value.present? # self._slug = highlighted_field_value.dup if _slug.blank? && highlighted_field_value.present?
#
if _slug.present? # if _slug.present?
self._slug.permalink! # self._slug.permalink!
self._slug = next_unique_slug if slug_already_taken? # self._slug = next_unique_slug if slug_already_taken?
end # end
end end
# Return the next available unique slug as a string # Return the next available unique slug as a string
def next_unique_slug def next_unique_slug
slug = _slug.gsub(/-\d*$/, '') # slug = _slug.gsub(/-\d*$/, '')
last_slug = _parent.contents.where(:_id.ne => _id, :_slug => /^#{slug}-?\d*?$/i).order_by(:_slug).last._slug # 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 # next_number = last_slug.scan(/-(\d)$/).flatten.first.to_i + 1
#
[slug, next_number].join('-') # [slug, next_number].join('-')
end end
def slug_already_taken? # def slug_already_taken?
_parent.contents.where(:_id.ne => _id, :_slug => _slug).any? # _parent.contents.where(:_id.ne => _id, :_slug => _slug).any?
end # end
def set_visibility # def set_visibility
%w(visible active).map(&:to_sym).each do |_alias| # %w(visible active).map(&:to_sym).each do |_alias|
if self.methods.include?(_alias) # if self.methods.include?(_alias)
self._visible = self.send(_alias) # self._visible = self.send(_alias)
return # return
end # end
end # end
# field = self.content_type.contents_custom_fields.detect { |f| %w{visible active}.include?(f._alias) } # # field = self.content_type.contents_custom_fields.detect { |f| %w{visible active}.include?(f._alias) }
# self._visible = self.send(field._name) rescue true # # self._visible = self.send(field._name) rescue true
end # end
def add_to_list_bottom def add_to_list_bottom
self._position_in_list = self.content_type.contents.size # TODO
# self._position_in_list = self.content_type.contents.size
end end
def require_highlighted_field # def require_highlighted_field
_alias = self.highlighted_field_alias # _alias = self.highlighted_field_alias
if self.send(_alias).blank? # if self.send(_alias).blank?
self.errors.add(_alias, :blank) # self.errors.add(_alias, :blank)
end # end
end # end
#
def highlighted_field_alias # def highlighted_field_alias
self.content_type.highlighted_field._alias.to_sym # self.content_type.highlighted_field._alias.to_sym
end # end
#
def send_notifications # def send_notifications
return unless self.content_type.api_enabled? && !self.content_type.api_accounts.blank? # return unless self.content_type.api_enabled? && !self.content_type.api_accounts.blank?
#
accounts = self.content_type.site.accounts.to_a # accounts = self.content_type.site.accounts.to_a
#
self.content_type.api_accounts.each do |account_id| # self.content_type.api_accounts.each do |account_id|
next if account_id.blank? # next if account_id.blank?
#
account = accounts.detect { |a| a.id.to_s == account_id.to_s } # account = accounts.detect { |a| a.id.to_s == account_id.to_s }
#
Locomotive::Notifications.new_content_instance(account, self).deliver # Locomotive::Notifications.new_content_instance(account, self).deliver
end # end
end # end
end end
end end

View File

@ -0,0 +1,15 @@
module Locomotive
class ContentPresenter < BasePresenter
# delegate :name, :description, :slug, :order_by, :order_direction, :highlighted_field_name, :group_by_field_name, :api_accounts, :to => :source
# def contents_custom_fields
# self.source.ordered_contents_custom_fields.collect(&:as_json)
# end
#
# def included_methods
# super + %w(name description slug order_by order_direction highlighted_field_name group_by_field_name api_accounts contents_custom_fields)
# end
end
end

View File

@ -6,22 +6,33 @@
{ content_type: #{@content_type.persisted? ? @content_type.to_json : 'null'} } { content_type: #{@content_type.persisted? ? @content_type.to_json : 'null'} }
= f.inputs :name => :information do = f.inputs :name => :information do
= f.input :name, :wrapper_html => { :class => 'highlighted' } = f.input :name, :wrapper_html => { :class => 'highlighted' }
= f.input :slug = f.input :slug
= f.input :description = f.input :description
= f.inputs :name => :custom_fields, :class => "inputs foldable" do = f.inputs :name => :custom_fields, :class => "inputs foldable" do
= f.input :contents_custom_fields, :as => :'Locomotive::Empty', :label => false, :wrapper_html => { :id => 'custom_fields_input' } = f.input :contents_custom_fields, :as => :'Locomotive::Empty', :label => false, :wrapper_html => { :id => 'custom_fields_input' }
- if @content_type.persisted? - if @content_type.persisted?
= f.inputs :name => :presentation, :class => "inputs foldable #{'folded' if inputs_folded?(@content_type)}" do = f.inputs :name => :presentation, :class => "inputs foldable #{'folded' if inputs_folded?(@content_type)}" do
= f.input :highlighted_field_id, :as => :select, :collection => options_for_highlighted_field(@content_type), :include_blank => false = f.input :highlighted_field_id, :as => :select, :collection => options_for_highlighted_field(@content_type), :include_blank => false
= f.input :group_by_field_id, :as => :select, :collection => options_for_group_by_field(@content_type) = f.input :group_by_field_id, :as => :select, :collection => options_for_group_by_field(@content_type)
= f.input :raw_item_template, :as => :'Locomotive::SmallCode' = f.input :raw_item_template, :as => :'Locomotive::SmallCode'
= f.inputs :name => :options, :class => "inputs foldable #{'folded' if inputs_folded?(@content_type)}" do = f.inputs :name => :options, :class => "inputs foldable #{'folded' if inputs_folded?(@content_type)}" do
= f.input :order_by, :as => :select, :collection => options_for_order_by(@content_type), :include_blank => false = f.input :order_by, :as => :select, :collection => options_for_order_by(@content_type), :include_blank => false
= f.input :order_direction, :as => :select, :collection => options_for_order_direction, :include_blank => false, :wrapper_html => { :style => "#{'display: none' if @content_type.order_manually?}" } = f.input :order_direction, :as => :select, :collection => options_for_order_direction, :include_blank => false, :wrapper_html => { :style => "#{'display: none' if @content_type.order_manually?}" }
= f.input :public_form_enabled, :as => :'Locomotive::Toggle' = f.input :public_form_enabled, :as => :'Locomotive::Toggle'
= f.input :public_form_accounts, :as => :select, :collection => options_for_account, :include_blank => false, :multiple => true, :wrapper_html => { :class => 'multiple', :style => (@content_type.public_form_enabled? ? '' : 'display: none') } = f.input :public_form_accounts, :as => :select, :collection => options_for_account, :include_blank => false, :multiple => true, :wrapper_html => { :class => 'multiple', :style => (@content_type.public_form_enabled? ? '' : 'display: none') }

View File

@ -1,10 +1,10 @@
- content_for :head do - content_for :backbone_view_data do
= include_javascripts :edit_custom_fields, :contents :plain
= include_stylesheets :fancybox { content: #{@content.persisted? ? @content.to_json : 'null'} }
= render 'locomotive/custom_fields/custom_form', :form => f, :title => :attributes, :parent => @content_type / = render 'locomotive/custom_fields/custom_form', :form => f, :title => :attributes, :parent => @content_type
= f.foldable_inputs :name => :advanced_options do = f.inputs :name => :advanced_options, :class => "inputs foldable #{'folded' if inputs_folded?(@content)}" do
= f.input :_slug = f.input :_slug
= f.input :seo_title = f.input :seo_title

View File

@ -1,7 +1,4 @@
/ - if breadcrumb_root - title t('.title', :type => @content_type.name.capitalize)
/ - title t('.title.breadcrumb', :root => link_to(breadcrumb_root._label, breadcrumb_url), :type => @content_type.name.capitalize)
/ - else
- title t('.title.default', :type => @content_type.name.capitalize)
- content_for :submenu do - content_for :submenu do
= render 'locomotive/shared/menu/contents' = render 'locomotive/shared/menu/contents'
@ -17,8 +14,8 @@
%p= @content_type.description %p= @content_type.description
= semantic_form_for @content, :as => :content, :url => content_url(@content_type.slug, @content), :html => { :multipart => true, :class => 'save-with-shortcut' } do |form| = semantic_form_for @content, :as => :content, :url => content_url(@content_type.slug, @content), :html => { :multipart => true } do |form|
= render 'form', :f => form = render 'form', :f => form
= render 'locomotive/shared/form_actions', :back_url => back_url, :button_label => :update = render 'locomotive/shared/form_actions', :back_url => contents_url(@content_type.slug), :button_label => :update

View File

@ -1,7 +1,4 @@
- if breadcrumb_root - title t('.title', :type => @content_type.name.capitalize)
- title t('.title.breadcrumb', :root => link_to(breadcrumb_root._label, breadcrumb_url), :type => @content_type.name.capitalize)
- else
- title t('.title.default', :type => @content_type.name.capitalize)
- content_for :submenu do - content_for :submenu do
= render 'locomotive/shared/menu/contents' = render 'locomotive/shared/menu/contents'
@ -19,4 +16,4 @@
= render 'form', :f => form = render 'form', :f => form
= render 'locomotive/shared/form_actions', :back_url => back_url, :button_label => :create = render 'locomotive/shared/form_actions', :back_url => contents_url(@content_type.slug), :button_label => :create

View File

@ -1,17 +0,0 @@
%script{ :id => 'category-tmpl', :type => 'text/x-mustache-template', :'data-base-input-name' => "custom_field[category_items_attributes]" }
%li{ :class => "item {{behaviour_flag}} {{new_record_flag}} {{errors_flag}} {{required_flag}}" }
%span.handle
= image_tag 'admin/form/icons/drag.png'
{{#if_existing_record}}
%input{ :name => '{{base_name}}[id]', :value => '{{{id}}}', :type => 'hidden', :'data-field' => 'id' }
%input{ :name => '{{base_name}}[_destroy]', :value => '0', :type => 'hidden', :'data-field' => '_destroy' }
{{/if_existing_record}}
%input{ :name => '{{base_name}}[position]', :value => '{{{position}}}', :type => 'hidden', :'data-field' => 'position' }
%input{ :name => '{{base_name}}[name]', :value => '{{{name}}}', :type => 'text', :'data-field' => 'name' }
%span.actions
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('locomotive.messages.confirm')
%button{ :class => 'button light add', :type => 'button' }
%span!= t('locomotive.buttons.new_item')

View File

@ -1,20 +0,0 @@
.fancybox-wrapper
#edit-custom-field
%h2!= t('.title')
= form_tag '#', :class => 'formtastic' do
= fields_for CustomFields::Field.new, :builder => Formtastic::SemanticFormHelper.builder do |g|
= g.inputs :name => :attributes do
= g.input :_alias
= g.input :hint
= g.input :text_formatting, :as => 'select', :collection => options_for_text_formatting, :include_blank => false, :wrapper_html => { :style => 'display: none' }
= g.input :target, :as => 'select', :collection => options_for_association_target, :include_blank => false, :wrapper_html => { :style => 'display: none' }
= g.input :reverse_lookup, :as => 'select', :collection => [], :include_blank => true, :wrapper_html => { :style => 'display: none' }
.popup-actions
%p
%button.button.light{ :type => 'submit' }
%span= t('locomotive.shared.form_actions.update')
%script{ :type => 'text/javascript', :name => 'reverse_lookups' }
!= collection_to_js(options_for_reverse_lookups(content_type))

View File

@ -1,52 +0,0 @@
- collection_name = "#{collection_name.singularize}_custom_fields"
- custom_fields = form.object.send(collection_name.to_sym)
- ordered_custom_fields = form.object.send(:"ordered_#{collection_name}")
- field_klass = "#{form.object.class.name}#{collection_name.classify}".gsub(/CustomField$/, 'Field').constantize
= form.foldable_inputs :name => defined?(form_name) ? form_name : :custom_fields, :class => 'editable-list fields' do
%script{ :type => 'text/x-mustache-template', :name => 'template', :'data-base-input-name' => "#{form.object.class.name.underscore}[#{collection_name}_attributes]" }
%li{ :class => "item {{behaviour_flag}} {{new_record_flag}} {{errors_flag}} {{required_flag}}" }
%span.handle
= image_tag 'admin/form/icons/drag.png'
{{#if_existing_record}}
%input{ :name => '{{base_name}}[id]', :value => '{{{id}}}', :type => 'hidden', :'data-field' => 'id' }
%input{ :name => '{{base_name}}[_destroy]', :value => '0', :type => 'hidden', :'data-field' => '_destroy' }
{{/if_existing_record}}
%input{ :name => '{{base_name}}[position]', :value => '{{{position}}}', :type => 'hidden', :'data-field' => 'position' }
%input{ :name => '{{base_name}}[_alias]', :value => '{{{_alias}}}', :type => 'hidden', :'data-field' => '_alias' }
%input{ :name => '{{base_name}}[hint]', :value => '{{{hint}}}', :type => 'hidden', :'data-field' => 'hint' }
%input{ :name => '{{base_name}}[text_formatting]', :value => '{{{text_formatting}}}', :type => 'hidden', :'data-field' => 'text_formatting' }
%input{ :name => '{{base_name}}[target]', :value => '{{{target}}}', :type => 'hidden', :'data-field' => 'target' }
%input{ :name => '{{base_name}}[reverse_lookup]', :value => '{{{reverse_lookup}}}', :type => 'hidden', :'data-field' => 'reverse_lookup' }
%input{ :name => '{{base_name}}[label]', :value => '{{{label}}}', :type => 'text', :'data-field' => 'label' }
&mdash;
%em.editable {{kind_name}}
= select_tag '{{base_name}}[kind]', options_for_select(options_for_field_kind), :'data-field' => 'kind'
&nbsp;
%input{ :name => '{{base_name}}[required]', :value => '0', :type => 'hidden', :'data-field' => 'hidden_required' }
%input{ :name => '{{base_name}}[required]', :'{{required_checked}}' => '{{required_checked}}', :value => '1', :type => 'checkbox', :'data-field' => 'required', :id => '{{base_dom_id}}_required' }
%label{ :for => "{{{base_dom_id}}}_required" }= t('.is_required')
%span.actions
= link_to image_tag('admin/form/pen.png'), '#edit-custom-field', :class => 'edit first'
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove', :confirm => t('locomotive.messages.confirm')
%button{ :class => 'button light add', :type => 'button' }
%span!= t('locomotive.buttons.new_item')
%script{ :type => 'text/javascript', :name => 'data' }
!= collection_to_js(ordered_custom_fields, :template => field_klass.new(:label => t('.default_label'), :_alias => '', :kind => 'string').to_hash)

View File

@ -1,17 +0,0 @@
#edit-custom-field-category
.inner
%h2!= t('.title')
%p!= t('.help')
= semantic_form_for @field, :as => :custom_field, :url => custom_field_url(@parent.class.model_name.underscore, @parent.slug, @field) do |f|
= f.foldable_inputs :name => t('.collection_label'), :class => 'editable-list off'
%script{ :type => 'text/javascript', :name => 'data' }
!= "var categories = #{collection_to_js(@field.ordered_category_items, :template => CustomFields::Types::Category::Item.new)};"
.popup-actions
%p
%button.button.light{ :type => 'submit' }
%span= t('locomotive.shared.form_actions.update')

View File

@ -248,24 +248,20 @@ en:
title: 'Listing "%{type}"' title: 'Listing "%{type}"'
edit: edit model edit: edit model
destroy: remove model destroy: remove model
download: download items download: download entries
new: new item new: new entry
category_noname: "No name" category_noname: "No name"
lastest_items: "Lastest items" lastest_entries: "Lastest entries"
updated_at: "Updated at" updated_at: "Updated at"
list: list:
no_items: "There are no items for now. Just click <a href=\"%{url}\">here</a> to create the first one." no_entries: "There are no entries for now. Just click <a href=\"%{url}\">here</a> to create the first one."
new: new:
title: title: '%{type} &mdash; new entry'
default: '%{type} &mdash; new item'
breadcrumb: '%{root} &raquo; %{type} &mdash; new item'
edit: edit:
title: title: '%{type} &mdash; editing entry'
default: '%{type} &mdash; editing item'
breadcrumb: '%{root} &raquo; %{type} &mdash; editing item'
form: form:
has_many: has_many:
new_item: New item new_entry: New entry
image_picker: image_picker:
link: Insert an image into the code link: Insert an image into the code