sort contents within a conten type + make content type validation more robust + find a bug when dealing with 2 content types
This commit is contained in:
parent
4534a11ce4
commit
1a2467acf4
@ -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
|
||||
|
@ -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
|
@ -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)
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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']
|
||||
|
@ -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
|
||||
|
@ -5,6 +5,7 @@
|
||||
|
||||
- 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
|
||||
|
||||
|
@ -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
|
||||
|
@ -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
|
@ -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
|
||||
|
@ -60,6 +60,7 @@ en:
|
||||
title: Listing pages
|
||||
no_items: "There are no pages for now. Just click <a href=\"{{url}}\">here</a> 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 <a href=\"{{url}}\">here</a> to create the first one."
|
||||
lastest_items: "Lastest items"
|
||||
new:
|
||||
title: '{{type}} — new item'
|
||||
edit:
|
||||
|
@ -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:
|
||||
|
11
doc/TODO
11
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
|
||||
@ -79,3 +75,8 @@ x custom fields:
|
||||
x nested attributes
|
||||
x keep tracks of all custom fields (adding / editing assets) + order them
|
||||
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
|
@ -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);
|
||||
|
@ -9,6 +9,7 @@ $(document).ready(function() {
|
||||
}
|
||||
|
||||
$('ul#contents-list.sortable').sortable({
|
||||
handle: 'em',
|
||||
items: 'li.content',
|
||||
stop: function(event, ui) { updateContentsOrder(); }
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
Loading…
Reference in New Issue
Block a user