diff --git a/app/controllers/admin/contents_controller.rb b/app/controllers/admin/contents_controller.rb
index c4e805cf..55fb518f 100644
--- a/app/controllers/admin/contents_controller.rb
+++ b/app/controllers/admin/contents_controller.rb
@@ -6,7 +6,7 @@ module Admin
before_filter :set_content_type
def index
- @contents = @content_type.contents
+ @contents = @content_type.ordered_contents
end
def new
@@ -42,7 +42,11 @@ module Admin
end
def sort
+ @content_type.sort_contents!(params[:order])
+ flash_success!
+
+ redirect_to admin_contents_url(@content_type.slug)
end
def destroy
diff --git a/app/helpers/admin/base_helper.rb b/app/helpers/admin/base_helper.rb
index e26eecc5..983d6d95 100644
--- a/app/helpers/admin/base_helper.rb
+++ b/app/helpers/admin/base_helper.rb
@@ -19,15 +19,13 @@ module Admin::BaseHelper
css = "#{'on' if name == sections(:sub)} #{'links' if block_given?} #{options[:css]}"
label_link = default_options[:i18n] ? t("admin.shared.menu.#{name}") : name
- # if block_given?
- # popup = content_tag(:div, capture(&block), :class => 'popup', :style => 'display: none')
- # link = link_to(content_tag(:span, label_link + content_tag(:em)), url)
- # concat(content_tag(:li, link + popup, :class => css))
- # else
- # html = content_tag(:li, link_to(content_tag(:span, label_link), url), :class => css)
- # end
-
- content_tag(:li, link_to(content_tag(:span, label_link), url), :class => css)
+ if block_given?
+ popup = content_tag(:div, capture(&block), :class => 'popup', :style => 'display: none')
+ link = link_to(content_tag(:span, preserve(label_link + content_tag(:em))), url)
+ content_tag(:li, link + popup, :class => css)
+ else
+ content_tag(:li, link_to(content_tag(:span, label_link), url), :class => css)
+ end
end
end
\ No newline at end of file
diff --git a/app/helpers/admin/custom_fields_helper.rb b/app/helpers/admin/custom_fields_helper.rb
index 240405ee..1c2dc7e1 100644
--- a/app/helpers/admin/custom_fields_helper.rb
+++ b/app/helpers/admin/custom_fields_helper.rb
@@ -8,7 +8,7 @@ module Admin::CustomFieldsHelper
end
def options_for_order_by(content_type, collection_name)
- options = %w{updated_at position}.map do |type|
+ options = %w{updated_at _position_in_list}.map do |type|
[t("admin.content_types.form.order_by.#{type.gsub(/^_/, '')}"), type]
end
options + options_for_highlighted_field(content_type, collection_name)
diff --git a/app/models/content_instance.rb b/app/models/content_instance.rb
index c6c5472b..049a37d1 100644
--- a/app/models/content_instance.rb
+++ b/app/models/content_instance.rb
@@ -3,7 +3,7 @@ class ContentInstance
include Mongoid::Timestamps
## fields (dynamic fields) ##
- field :position, :type => Integer, :default => 0
+ field :_position_in_list, :type => Integer, :default => 0
## validations ##
validate :require_highlighted_field
@@ -11,6 +11,9 @@ class ContentInstance
## associations ##
embedded_in :content_type, :inverse_of => :contents
+ ## named scopes ##
+ named_scope :latest_updated, :order_by => [[:updated_at, :desc]], :limit => Locomotive.config.lastest_items_nb
+
## methods ##
protected
diff --git a/app/models/content_type.rb b/app/models/content_type.rb
index 946361e5..c6753b9a 100644
--- a/app/models/content_type.rb
+++ b/app/models/content_type.rb
@@ -16,10 +16,12 @@ class ContentType
## callbacks ##
before_validate :normalize_slug
+ before_save :set_default_values
## validations ##
validates_presence_of :site, :name, :slug
validates_uniqueness_of :slug, :scope => :site
+ validates_size_of :content_custom_fields, :minimum => 1, :message => :array_too_short
## behaviours ##
custom_fields_for :contents
@@ -27,15 +29,15 @@ class ContentType
## methods ##
def ordered_contents
- self.contents.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
+ column = self.order_by.to_sym
+ self.contents.sort { |a, b| (a.send(column) || 0) <=> (b.send(column) || 0) }
end
- def contents_order
- self.ordered_contents.collect(&:id).join(',')
- end
-
- def contents_order=(order)
- @contents_order = order
+ def sort_contents!(order)
+ order.split(',').each_with_index do |id, position|
+ self.contents.find(id)._position_in_list = position
+ end
+ self.save
end
def highlighted_field
@@ -44,6 +46,11 @@ class ContentType
protected
+ def set_default_values
+ self.order_by ||= 'updated_at'
+ self.highlighted_field_name ||= self.content_custom_fields.first._name
+ end
+
def normalize_slug
self.slug = self.name.clone if self.slug.blank? && self.name.present?
self.slug.slugify! if self.slug.present?
diff --git a/app/models/page.rb b/app/models/page.rb
index 6ddf5c1e..bd3022fc 100644
--- a/app/models/page.rb
+++ b/app/models/page.rb
@@ -33,6 +33,7 @@ class Page
validates_exclusion_of :slug, :in => Locomotive.config.reserved_slugs, :if => Proc.new { |p| p.depth == 0 }
## named scopes ##
+ named_scope :latest_updated, :order_by => [[:updated_at, :desc]], :limit => Locomotive.config.lastest_items_nb
## behaviours ##
acts_as_tree :order => ['position', 'asc']
diff --git a/app/views/admin/contents/_form.html.haml b/app/views/admin/contents/_form.html.haml
index 1015c6f0..142ff0ea 100644
--- a/app/views/admin/contents/_form.html.haml
+++ b/app/views/admin/contents/_form.html.haml
@@ -1,5 +1,7 @@
- highlighted_field_name = @content.content_type.highlighted_field_name
+- puts "current custom fields = #{@content.custom_fields.inspect}"
+
= f.inputs :name => :other_fields do
- @content.custom_fields.each do |field|
- required = highlighted_field_name == field._name
diff --git a/app/views/admin/contents/edit.html.haml b/app/views/admin/contents/edit.html.haml
index c0526753..52af2b85 100644
--- a/app/views/admin/contents/edit.html.haml
+++ b/app/views/admin/contents/edit.html.haml
@@ -3,8 +3,9 @@
- content_for :submenu do
= render 'admin/shared/menu/contents'
-- content_for :buttons do
+- content_for :buttons do
= admin_button_tag t('admin.contents.index.edit'), edit_admin_content_type_url(@content_type), :class => 'edit'
+ = admin_button_tag t('admin.contents.index.new'), new_admin_content_url(@content_type.slug), :class => 'new'
%p= @content_type.description
diff --git a/app/views/admin/contents/index.html.haml b/app/views/admin/contents/index.html.haml
index be2f2c84..f249d23b 100644
--- a/app/views/admin/contents/index.html.haml
+++ b/app/views/admin/contents/index.html.haml
@@ -8,7 +8,6 @@
- content_for :buttons do
= admin_button_tag :edit, edit_admin_content_type_url(@content_type), :class => 'edit'
- = admin_button_tag :download, '#', :class => 'download'
= admin_button_tag :new, new_admin_content_url(@content_type.slug), :class => 'new'
- if @content_type.description.present?
@@ -17,11 +16,10 @@
- if @contents.empty?
%p.no-items= t('.no_items', :url => new_admin_content_url(@content_type.slug))
- else
- %ul{ :id => 'contents-list', :class => "list #{'sortable' if @content_type.order_by == 'position'}" }
+ %ul{ :id => 'contents-list', :class => "list #{'sortable' if @content_type.order_by == '_position_in_list'}" }
- @contents.each do |content|
%li.content{ :id => "content-#{content._id}" }
- - if @content_type.order_by == 'position'
- %em
+ %em
%strong
= link_to content.send(@content_type.highlighted_field_name), edit_admin_content_path(@content_type.slug, content)
.more
diff --git a/app/views/admin/shared/menu/_contents.html.haml b/app/views/admin/shared/menu/_contents.html.haml
index 67049d6a..5838660c 100644
--- a/app/views/admin/shared/menu/_contents.html.haml
+++ b/app/views/admin/shared/menu/_contents.html.haml
@@ -1,9 +1,27 @@
%ul
- = admin_submenu_item 'pages', admin_pages_url
+ = admin_submenu_item 'pages', admin_pages_url do
+ .header
+ %p= link_to t('admin.pages.index.new'), new_admin_page_url
+ .inner
+ %h2= t('admin.pages.index.lastest_items')
+ %ul
+ - current_site.pages.latest_updated.each do |page|
+ %li
+ = link_to truncate(page.title, :length => 40), edit_admin_page_url(page)
+ %span= time_ago_in_words(page.updated_at)
- current_site.content_types.each do |content_type|
- item_on = (content_type.slug == @content_type.slug) rescue nil
- = admin_submenu_item content_type.name, admin_contents_url(content_type.slug), :i18n => false, :css => (item_on ? 'on' : '')
-
+ = admin_submenu_item content_type.name, admin_contents_url(content_type.slug), :i18n => false, :css => (item_on ? 'on' : '') do
+ .header
+ %p= link_to t('admin.contents.index.new'), new_admin_content_url(content_type.slug)
+ .inner
+ %h2= t('admin.contents.index.lastest_items')
+ %ul
+ - content_type.contents.latest_updated.each do |content|
+ %li
+ = link_to truncate(content.send(content_type.highlighted_field_name), :length => 40), edit_admin_content_path(content_type.slug, content)
+ %span= time_ago_in_words(content.updated_at)
+
.action
= link_to content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url
\ No newline at end of file
diff --git a/config/initializers/locomotive.rb b/config/initializers/locomotive.rb
index 0f49ed88..527dcac3 100644
--- a/config/initializers/locomotive.rb
+++ b/config/initializers/locomotive.rb
@@ -3,6 +3,8 @@ require 'lib/core_ext.rb'
Locomotive.configure do |config|
config.default_domain = 'example.com'
+
+ config.lastest_items_nb = 5
end
# TODO: embed them in Locomotive right after configure
diff --git a/config/locales/admin_ui_en.yml b/config/locales/admin_ui_en.yml
index 7e7f298b..c1215c54 100644
--- a/config/locales/admin_ui_en.yml
+++ b/config/locales/admin_ui_en.yml
@@ -60,6 +60,7 @@ en:
title: Listing pages
no_items: "There are no pages for now. Just click here to create the first one."
new: new page
+ lastest_items: Lastest pages
page:
updated_at: updated at
edit:
@@ -153,15 +154,17 @@ en:
form:
order_by:
updated_at: 'By "updated at" date'
- position: Manually
+ position_in_list: Manually
contents:
index:
title: 'Listing "{{type}}"'
edit: edit model
+ destroy: remove model
download: download items
new: new item
no_items: "There are no items for now. Just click here to create the first one."
+ lastest_items: "Lastest items"
new:
title: '{{type}} — new item'
edit:
diff --git a/config/locales/default_en.yml b/config/locales/default_en.yml
index b5fbd1da..00e59f61 100644
--- a/config/locales/default_en.yml
+++ b/config/locales/default_en.yml
@@ -7,6 +7,7 @@ en:
needs_admin_account: "One admin account is required at least"
protected_page: "You can not remove index or 404 pages"
extname_changed: "New file does not have the original extension"
+ array_too_short: "is too small (minimum element number is {{count}})"
attributes:
defaults:
diff --git a/doc/TODO b/doc/TODO
index 652fda7e..cf320f5e 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -1,9 +1,5 @@
BOARD:
- - content types / models (CRUD)
- - require a custom field at least
- - pre-select the first custom field as the highlighted one
- - contents (CRUD)
- - sort contents
+ - contents sub menu => BUG
BACKLOG:
- liquid rendering engine
@@ -78,4 +74,9 @@ x custom fields:
x rename asset_field
x nested attributes
x keep tracks of all custom fields (adding / editing assets) + order them
- x duplicate fields
\ No newline at end of file
+ x duplicate fields
+x content types / models (CRUD)
+ x require a custom field at least
+ x pre-select the first custom field as the highlighted one
+x contents (CRUD)
+ x sort contents
\ No newline at end of file
diff --git a/public/javascripts/admin/application.js b/public/javascripts/admin/application.js
index ce85514a..2bc5faca 100644
--- a/public/javascripts/admin/application.js
+++ b/public/javascripts/admin/application.js
@@ -44,6 +44,18 @@ var addCodeMirrorEditor = function(type, el, parser) {
$(document).ready(function() {
I18nLocale = $('meta[name=locale]').attr('content');
+ // sub menu links
+ $('#submenu ul li.links').hover(function() {
+ $(this).addClass('hover');
+ $(this).find('.popup').show();
+ }, function() {
+ $(this).removeClass('hover');
+ $(this).find('.popup').hide();
+ });
+
+ if ((css = $('#submenu > ul').attr('class')) != '')
+ $('#submenu > ul > li.' + css).addClass('on');
+
// form
$('.formtastic li input, .formtastic li textarea').focus(function() {
$('.formtastic li.error p.inline-errors').fadeOut(200);
diff --git a/public/javascripts/admin/contents.js b/public/javascripts/admin/contents.js
index e3d463e7..2a1ff4fb 100644
--- a/public/javascripts/admin/contents.js
+++ b/public/javascripts/admin/contents.js
@@ -9,6 +9,7 @@ $(document).ready(function() {
}
$('ul#contents-list.sortable').sortable({
+ handle: 'em',
items: 'li.content',
stop: function(event, ui) { updateContentsOrder(); }
});
diff --git a/public/stylesheets/admin/application.css b/public/stylesheets/admin/application.css
index fd96afea..6e79df0f 100644
--- a/public/stylesheets/admin/application.css
+++ b/public/stylesheets/admin/application.css
@@ -163,7 +163,7 @@ div#uploadAssetsInputQueue { display: none; }
#contents-list li { background: none; }
-#contents-list li em {
+#contents-list.sortable li em {
background-position: left -31px;
cursor: move;
}
diff --git a/spec/models/content_type_spec.rb b/spec/models/content_type_spec.rb
index 85547e02..ec0f16da 100644
--- a/spec/models/content_type_spec.rb
+++ b/spec/models/content_type_spec.rb
@@ -9,7 +9,9 @@ describe ContentType do
context 'when validating' do
it 'should have a valid factory' do
- Factory.build(:content_type).should be_valid
+ content_type = Factory.build(:content_type)
+ content_type.content_custom_fields.build :label => 'anything', :kind => 'String'
+ content_type.should be_valid
end
# Validations ##
@@ -29,11 +31,19 @@ describe ContentType do
end
it 'should validate uniqueness of slug' do
- content_type = Factory(:content_type)
+ content_type = Factory.build(:content_type)
+ content_type.content_custom_fields.build :label => 'anything', :kind => 'String'
+ content_type.save
(content_type = Factory.build(:content_type, :site => content_type.site)).should_not be_valid
content_type.errors[:slug].should == ["is already taken"]
end
+ it 'should validate size of content custom fields' do
+ content_type = Factory.build(:content_type)
+ content_type.should_not be_valid
+ content_type.errors[:content_custom_fields].should == ["is too small (minimum element number is 1)"]
+ end
+
end
end
\ No newline at end of file