adding contents works + add spec + enhance content types

This commit is contained in:
dinedine 2010-05-25 02:32:12 +02:00
parent 61958d9452
commit 4534a11ce4
20 changed files with 257 additions and 49 deletions

View File

@ -18,7 +18,7 @@ module Admin
end
def create
@content = @content_type.contents.build(params[:content])
@content = @content_type.contents.build(params[:content_instance])
if @content.save
flash_success!
@ -32,7 +32,7 @@ module Admin
def update
@content = @content_type.contents.find(params[:id])
if @content.update_attributes(params[:content])
if @content.update_attributes(params[:content_instance])
flash_success!
redirect_to edit_admin_content_url(@content_type.slug, @content)
else
@ -41,6 +41,10 @@ module Admin
end
end
def sort
end
def destroy
@content = @content_type.contents.find(params[:id])

View File

@ -1,13 +1,5 @@
module Admin::CustomFieldsHelper
# def options_for_field_kind(selected = nil)
# # %w{String Text Boolean Email File Date}
# options = %w{String Text}.map do |kind|
# [t("admin.custom_fields.kind.#{kind.downcase}"), kind]
# end
# options_for_select(options, selected)
# end
def options_for_field_kind(selected = nil)
# %w{String Text Boolean Email File Date}
options = %w{String Text}.map do |kind|
@ -15,4 +7,18 @@ module Admin::CustomFieldsHelper
end
end
def options_for_order_by(content_type, collection_name)
options = %w{updated_at position}.map do |type|
[t("admin.content_types.form.order_by.#{type.gsub(/^_/, '')}"), type]
end
options + options_for_highlighted_field(content_type, collection_name)
end
def options_for_highlighted_field(content_type, collection_name)
custom_fields_collection_name = "ordered_#{collection_name.singularize}_custom_fields".to_sym
collection = content_type.send(custom_fields_collection_name)
collection.delete_if { |f| f.label == 'field name' }
collection.map { |field| [field.label, field._name] }
end
end

View File

@ -2,7 +2,24 @@ class ContentInstance
include Mongoid::Document
include Mongoid::Timestamps
# fields ##
field :name
## fields (dynamic fields) ##
field :position, :type => Integer, :default => 0
## validations ##
validate :require_highlighted_field
## associations ##
embedded_in :content_type, :inverse_of => :contents
## methods ##
protected
def require_highlighted_field
_alias = self.content_type.highlighted_field._alias.to_sym
if self.send(_alias).blank?
self.errors.add(_alias, :blank)
end
end
end

View File

@ -1,13 +1,14 @@
class ContentType
include Mongoid::Document
include Mongoid::Timestamps
# include Mongoid::CustomFields
include Mongoid::CustomFields
## fields ##
field :name
field :description
field :slug
field :order_by
field :highlighted_field_name
## associations ##
belongs_to_related :site
@ -21,10 +22,26 @@ class ContentType
validates_uniqueness_of :slug, :scope => :site
## behaviours ##
# custom_fields_for :contents
custom_fields_for :contents
## methods ##
def ordered_contents
self.contents.sort { |a, b| (a.position || 0) <=> (b.position || 0) }
end
def contents_order
self.ordered_contents.collect(&:id).join(',')
end
def contents_order=(order)
@contents_order = order
end
def highlighted_field
self.content_custom_fields.detect { |f| f._name == self.highlighted_field_name }
end
protected
def normalize_slug

View File

@ -1,7 +1,7 @@
- title link_to(@collection.name.blank? ? @collection.name_was : @collection.name, '#', :rel => 'asset_collection_name', :title => t('.ask_for_name'), :class => 'editable')
- content_for :head do
= javascript_include_tag 'admin/asset_collections.js', 'admin/custom_fields'
= javascript_include_tag 'admin/asset_collections', 'admin/custom_fields'
- content_for :submenu do
= render 'admin/shared/menu/assets'
@ -25,6 +25,6 @@
= f.input :name
= f.input :slug, :required => false
= render 'custom_fields', :f => f
= render 'admin/shared/custom_fields', :f => f, :collection_name => 'assets'
= render 'admin/shared/form_actions', :delete_button => link_to(content_tag(:span, t('.destroy')), admin_asset_collection_url(@collection), :confirm => t('admin.messages.confirm'), :method => :delete, :class => 'button small remove'), :button_label => :update

View File

@ -1,4 +1,13 @@
- content_for :head do
= javascript_include_tag 'admin/custom_fields'
= f.inputs :name => :information do
= f.input :name
= f.input :slug, :required => false
= f.input :description, :as => 'text'
= f.input :slug
= f.input :description
= render 'admin/shared/custom_fields', :f => f, :collection_name => 'contents'
= f.foldable_inputs :name => :options, :class => 'switchable' do
= f.input :highlighted_field_name, :as => :select, :collection => options_for_highlighted_field(f.object, 'contents'), :include_blank => false
= f.input :order_by, :as => :select, :collection => options_for_order_by(f.object, 'contents'), :include_blank => false

View File

@ -0,0 +1,10 @@
- highlighted_field_name = @content.content_type.highlighted_field_name
= f.inputs :name => :other_fields do
- @content.custom_fields.each do |field|
- required = highlighted_field_name == field._name
- if field.string?
= f.input field._alias.to_sym, :label => field.label, :required => required
- elsif field.text?
= f.input field._alias.to_sym, :label => field.label, :as => :text, :required => required

View File

@ -0,0 +1,15 @@
- title t('.title', :type => @content_type.name.capitalize)
- content_for :submenu do
= render 'admin/shared/menu/contents'
- content_for :buttons do
= admin_button_tag t('admin.contents.index.edit'), edit_admin_content_type_url(@content_type), :class => 'edit'
%p= @content_type.description
= semantic_form_for @content, :url => admin_content_url(@content_type.slug, @content), :html => { :multipart => true } do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :update

View File

@ -1,8 +1,11 @@
- title t('.title', :type => @content_type.name)
- title t('.title', :type => @content_type.name.capitalize)
- content_for :submenu do
= render 'admin/shared/menu/contents'
- content_for :head do
= javascript_include_tag 'admin/contents'
- content_for :buttons do
= admin_button_tag :edit, edit_admin_content_type_url(@content_type), :class => 'edit'
= admin_button_tag :download, '#', :class => 'download'
@ -14,4 +17,21 @@
- if @contents.empty?
%p.no-items= t('.no_items', :url => new_admin_content_url(@content_type.slug))
- else
foo bar
%ul{ :id => 'contents-list', :class => "list #{'sortable' if @content_type.order_by == 'position'}" }
- @contents.each do |content|
%li.content{ :id => "content-#{content._id}" }
- if @content_type.order_by == 'position'
%em
%strong
= link_to content.send(@content_type.highlighted_field_name), edit_admin_content_path(@content_type.slug, content)
.more
%span
= t('admin.contents.index.updated_at')
= l content.updated_at, :format => :short rescue 'n/a'
= link_to image_tag('admin/list/icons/trash.png'), admin_content_path(@content_type.slug, content), :class => 'remove', :confirm => t('admin.messages.confirm'), :method => :delete
= form_tag sort_admin_contents_path(@content_type.slug), :method => :put, :class => 'formtastic' do
= hidden_field_tag :order
= render 'admin/shared/form_actions', :delete_button => link_to(content_tag(:span, t('.destroy')), admin_content_type_url(@content_type), :confirm => t('admin.messages.confirm'), :method => :delete, :class => 'button small remove'), :button_label => :update

View File

@ -0,0 +1,15 @@
- title t('.title', :type => @content_type.name.capitalize)
- content_for :submenu do
= render 'admin/shared/menu/contents'
- content_for :buttons do
= admin_button_tag t('admin.contents.index.edit'), edit_admin_content_type_url(@content_type), :class => 'edit'
%p= @content_type.description
= semantic_form_for @content, :url => admin_contents_url(@content_type.slug), :html => { :multipart => true } do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :create

View File

@ -1,6 +1,10 @@
- collection_name = "#{collection_name.singularize}_custom_fields"
- custom_fields = f.object.send(collection_name.to_sym)
- ordered_custom_fields = f.object.send(:"ordered_#{collection_name}")
= f.foldable_inputs :name => :custom_fields, :class => 'editable-list fields' do
- f.object.ordered_asset_custom_fields.each do |field|
= f.fields_for :asset_custom_fields, field, :child_index => field._index do |g|
- ordered_custom_fields.each do |field|
= f.fields_for collection_name.to_sym, field, :child_index => field._index do |g|
%li{ :class => "item added #{'error' unless field.errors.empty?}"}
%span.handle
= image_tag 'admin/form/icons/drag.png'
@ -20,7 +24,7 @@
%span.actions
= link_to image_tag('admin/form/icons/trash.png'), '#', :class => 'remove first', :confirm => t('admin.messages.confirm')
= f.fields_for :asset_custom_fields, @collection.asset_custom_fields.build(:label => 'field name'), :child_index => '-1' do |g|
= f.fields_for collection_name.to_sym, custom_fields.build(:label => 'field name'), :child_index => '-1' do |g|
%li{ :class => 'item template' }
%span.handle
= image_tag 'admin/form/icons/drag.png'

View File

@ -150,6 +150,10 @@ en:
help: "Your model should have one field at least. The items created from this content type would have their first field mandatory."
show_items: show items
new_item: new item
form:
order_by:
updated_at: 'By "updated at" date'
position: Manually
contents:
index:
@ -158,6 +162,11 @@ en:
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."
new:
title: '{{type}} &mdash; new item'
edit:
title: '{{type}} &mdash; editing item'
formtastic:
titles:

View File

@ -45,8 +45,9 @@ Locomotive::Application.routes.draw do |map|
resources :content_types
resources :contents, :path => "content_types/:slug/contents"
resources :contents, :path => "content_types/:slug/contents" do
put :sort, :on => :collection
end
end
# magic urls

View File

@ -1,6 +1,9 @@
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
BACKLOG:
- liquid rendering engine

View File

@ -0,0 +1,16 @@
$(document).ready(function() {
var updateContentsOrder = function() {
var list = $('ul#contents-list.sortable');
var ids = jQuery.map(list.sortable('toArray'), function(e) {
return e.match(/content-(\w+)/)[1];
}).join(',');
$('#order').val(ids || '');
}
$('ul#contents-list.sortable').sortable({
items: 'li.content',
stop: function(event, ui) { updateContentsOrder(); }
});
});

View File

@ -54,6 +54,14 @@ ul.list li {
background: transparent url(/images/admin/list/item.png) no-repeat 0 0;
}
ul.list li em {
display: block;
float: left;
background: transparent url(/images/admin/list/item-left.png) no-repeat left 0;
height: 31px;
width: 18px;
}
ul.list li strong a {
position: relative;
top: 2px;
@ -151,6 +159,22 @@ div#asset-uploader { display: inline-block; margin-left: 10px; }
div#asset-uploader span.spinner { position: relative; top: -3px; display: none; }
div#uploadAssetsInputQueue { display: none; }
/* ___ contents ___ */
#contents-list li { background: none; }
#contents-list li em {
background-position: left -31px;
cursor: move;
}
#contents-list li strong {
margin-left: 18px;
display: block;
height: 31px;
background: transparent url(/images/admin/list/item-right.png) no-repeat right 0;
}
/* ___ pages ___ */

View File

@ -0,0 +1,33 @@
require 'spec_helper'
describe ContentInstance do
before(:each) do
Site.any_instance.stubs(:create_default_pages!).returns(true)
@content_type = Factory.build(:content_type)
@content_type.content_custom_fields.build :label => 'Title', :kind => 'String'
@content_type.content_custom_fields.build :label => 'Description', :kind => 'Text'
@content_type.highlighted_field_name = 'custom_field_1'
end
context 'when validating' do
it 'should be valid' do
build_content.should be_valid
end
# Validations ##
it 'should validate presence of title' do
content = build_content :title => nil
content.should_not be_valid
content.errors[:title].should == ["can't be blank"]
end
end
def build_content(options = {})
@content_type.contents.build({ :title => 'Locomotive', :description => 'Lorem ipsum....' }.merge(options))
end
end

View File

@ -6,6 +6,8 @@ describe ContentType do
Site.any_instance.stubs(:create_default_pages!).returns(true)
end
context 'when validating' do
it 'should have a valid factory' do
Factory.build(:content_type).should be_valid
end
@ -32,4 +34,6 @@ describe ContentType do
content_type.errors[:slug].should == ["is already taken"]
end
end
end

View File

@ -6,6 +6,7 @@ module Mongoid #:nodoc:
parentize_without_custom_fields(object, association_name)
if self.custom_fields?(object, association_name)
puts "[parentize_with_custom_fields] association_name = #{association_name} / #{self.custom_fields_association_name(association_name)}"
self.class.send(:define_method, :custom_fields) do
fields = object.send(self.custom_fields_association_name(association_name))
fields.sort { |a, b| (a.position || 0) <=> (b.position || 0) }