fix bug about page parts when creating or modifying layout + begin to work on the custom content types feature (50% done)

This commit is contained in:
dinedine 2010-05-24 02:18:23 +02:00
parent 1ed613ede8
commit 61958d9452
23 changed files with 356 additions and 58 deletions

View File

@ -0,0 +1,52 @@
module Admin
class ContentTypesController < BaseController
sections 'contents'
def new
@content_type = current_site.content_types.build
end
def edit
@content_type = current_site.content_types.find(params[:id])
end
def create
@content_type = current_site.content_types.build(params[:content_type])
if @content_type.save
flash_success!
redirect_to edit_admin_content_type_url(@content_type)
else
flash_error!
render :action => 'new'
end
end
def update
@content_type = current_site.content_types.find(params[:id])
if @content_type.update_attributes(params[:content_type])
flash_success!
redirect_to edit_admin_content_type_url(@content_type)
else
flash_error!
render :action => "edit"
end
end
def destroy
@content_type = current_site.content_types.find(params[:id])
begin
@content_type.destroy
flash_success!
rescue Exception => e
flash[:error] = e.to_s
end
redirect_to admin_content_types_url
end
end
end

View File

@ -0,0 +1,64 @@
module Admin
class ContentsController < BaseController
sections 'contents'
before_filter :set_content_type
def index
@contents = @content_type.contents
end
def new
@content = @content_type.contents.build
end
def edit
@content = @content_type.contents.find(params[:id])
end
def create
@content = @content_type.contents.build(params[:content])
if @content.save
flash_success!
redirect_to edit_admin_content_url(@content_type.slug, @content)
else
flash_error!
render :action => 'new'
end
end
def update
@content = @content_type.contents.find(params[:id])
if @content.update_attributes(params[:content])
flash_success!
redirect_to edit_admin_content_url(@content_type.slug, @content)
else
flash_error!
render :action => "edit"
end
end
def destroy
@content = @content_type.contents.find(params[:id])
begin
@content.destroy
flash_success!
rescue Exception => e
flash[:error] = e.to_s
end
redirect_to admin_contents_url(@content_type.slug)
end
protected
def set_content_type
@content_type = current_site.content_types.where(:slug => params[:slug]).first
end
end
end

View File

@ -13,10 +13,10 @@ module Admin::BaseHelper
end
def admin_submenu_item(name, url, options = {}, &block)
default_options = { :i18n => true, :css => '' }
default_options = { :i18n => true, :css => name.dasherize.downcase }
default_options.merge!(options)
css = "#{name.dasherize.downcase} #{'on' if name == sections(:sub)} #{'links' if block_given?} #{options[:css]}"
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?

View File

@ -0,0 +1,8 @@
class ContentInstance
include Mongoid::Document
include Mongoid::Timestamps
# fields ##
field :name
end

View File

@ -0,0 +1,35 @@
class ContentType
include Mongoid::Document
include Mongoid::Timestamps
# include Mongoid::CustomFields
## fields ##
field :name
field :description
field :slug
field :order_by
## associations ##
belongs_to_related :site
embeds_many :contents, :class_name => 'ContentInstance'
## callbacks ##
before_validate :normalize_slug
## validations ##
validates_presence_of :site, :name, :slug
validates_uniqueness_of :slug, :scope => :site
## behaviours ##
# custom_fields_for :contents
## methods ##
protected
def normalize_slug
self.slug = self.name.clone if self.slug.blank? && self.name.present?
self.slug.slugify! if self.slug.present?
end
end

View File

@ -17,24 +17,23 @@ class Layout < LiquidTemplate
def build_parts_from_value
if self.value_changed? || self.new_record?
self.parts.clear
body = nil
self.value.scan(Locomotive::Regexps::CONTENT_FOR).each do |part|
part[1].strip!
part[1] = nil if part[1].empty?
self.value.scan(Locomotive::Regexps::CONTENT_FOR).each do |attributes|
slug = attributes[0].strip.downcase
name = attributes[1].strip.gsub("\"", '')
name = nil if name.empty?
name ||= I18n.t('attributes.defaults.page_parts.name') if slug == 'layout'
slug = part[0].strip.downcase
if slug == 'layout'
body = PagePart.new :slug => slug, :name => I18n.t('attributes.defaults.page_parts.name')
if part = self.parts.detect { |p| p.slug == slug }
part.name = name if name.present?
else
self.parts.build :slug => slug, :name => (part[1] || slug).gsub("\"", '')
end
self.parts.build :slug => slug, :name => name || slug
end
end
self.parts.insert(0, body) if body
# body always first
body = self.parts.detect { |p| p.slug == 'layout' }
self.parts.delete(body)
self.parts.insert(0, body)
@_update_pages = true if self.value_changed?
end

View File

@ -15,7 +15,7 @@ class LiquidTemplate
## validations ##
validates_presence_of :site, :name, :slug, :value
validates_uniqueness_of :slug, :scope => [:site_id, :_type]
validates_uniqueness_of :slug, :scope => :site_id #[:site_id, :_type]
protected

View File

@ -13,6 +13,7 @@ class Site
has_many_related :snippets
has_many_related :theme_assets
has_many_related :asset_collections
has_many_related :content_types
embeds_many :memberships
## validations ##
@ -83,7 +84,7 @@ class Site
end
def destroy_in_cascade!
%w{pages layouts snippets theme_assets asset_collections}.each do |association|
%w{pages layouts snippets theme_assets asset_collections content_types}.each do |association|
self.send(association).destroy_all
end
end

View File

@ -0,0 +1,4 @@
= f.inputs :name => :information do
= f.input :name
= f.input :slug, :required => false
= f.input :description, :as => 'text'

View File

@ -0,0 +1,16 @@
- title t('.title')
- content_for :submenu do
= render 'admin/shared/menu/contents'
- content_for :buttons do
= admin_button_tag :show_items, admin_contents_url(@content_type.slug), :class => 'show'
= admin_button_tag :new_item, new_admin_content_url(@content_type.slug), :class => 'show'
%p= t('.help')
= semantic_form_for @content_type, :url => admin_content_type_url(@content_type) do |form|
= render 'form', :f => form
= render 'admin/shared/form_actions', :back_url => admin_contents_url(@content_type.slug), :button_label => :update

View File

@ -0,0 +1,15 @@
- title t('.title')
- content_for :head do
= javascript_include_tag 'admin/content_types.js'
- content_for :submenu do
= render 'admin/shared/menu/contents'
%p= t('.help')
= semantic_form_for @content_type, :url => admin_content_types_url do |f|
= render 'form', :f => f
= render 'admin/shared/form_actions', :back_url => admin_pages_url, :button_label => :create

View File

@ -0,0 +1,17 @@
- title t('.title', :type => @content_type.name)
- content_for :submenu do
= render 'admin/shared/menu/contents'
- 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?
%p= @content_type.description
- if @contents.empty?
%p.no-items= t('.no_items', :url => new_admin_content_url(@content_type.slug))
- else
foo bar

View File

@ -1,2 +1,9 @@
%ul
= admin_submenu_item 'pages', admin_pages_url
- 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' : '')
.action
= link_to content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url

View File

@ -28,11 +28,11 @@ module Mongoid #:nodoc:
conditions[scoped_attr] = document.attributes[scoped_attr]
end
end
# Rails.logger.debug "conditions = #{conditions.inspect} / #{options[:scope].inspect}"
Rails.logger.debug "conditions = #{conditions.inspect} / #{options[:scope].inspect}"
return if document.class.where(conditions).empty?
# if document.new_record? || key_changed?(document)
document.errors.add(attribute, :taken, :default => options[:message], :value => value)
# end

View File

@ -138,7 +138,26 @@ en:
assets:
new:
title: New asset
content_types:
index:
new: new model
new:
title: New model
help: "Create your own data model (Projects, People, ...etc). Your model should have one field at least. The items created from this content type would have their first field mandatory."
edit:
title: Editing model
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
contents:
index:
title: 'Listing "{{type}}"'
edit: edit 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."
formtastic:
titles:

View File

@ -41,7 +41,11 @@ Locomotive::Application.routes.draw do |map|
resources :asset_collections
resources :assets, :path => "asset_collections/:collection_id"
resources :assets, :path => "asset_collections/:collection_id/assets"
resources :content_types
resources :contents, :path => "content_types/:slug/contents"
end

View File

@ -1,3 +1,25 @@
BOARD:
- content types / models (CRUD)
- contents (CRUD)
BACKLOG:
- liquid rendering engine
- theme assets
- assets collection
- custom models
- asset collections: custom resizing if image
- devise messages in French
- localize devise emails
- refactoring admin crud (pages + layouts + snippets)
- refactoring page.rb => create module pagetree
- refactoring: CustomFields::CustomField => CustomFields::Field
BUGS:
- theme assets: disable version if not image
- assets uploader: remove old files if new one
DONE:
x admin layout
x logout button
x slugify page
@ -34,22 +56,18 @@ x create 404 + index pages once a site is created
x can not delete index + 404 pages
x validates_uniqueness_of :slug, :scope => :id
x domain scoping when authenticating
- theme assets
x theme assets
x create / update
x slug
x filename from slug
x can not replace a javascript by a stylesheet
- disable version if not image
- asset collections
x asset collections
x create / update
x sort assets
x removing assets
- assets
- destroy
- custom resizing
- assets uploader:
- remove old files if new one
- custom fields:
x assets
x destroy
x custom fields:
x renaming fields
x extract a plugin from custom fields
x ui
@ -57,18 +75,4 @@ x domain scoping when authenticating
x rename asset_field
x nested attributes
x keep tracks of all custom fields (adding / editing assets) + order them
x duplicate fields
- apply in asset_field (in order to handle Date, File, ...etc)
BACKLOG:
- liquid rendering engine
- theme assets
- assets collection
- custom models
- devise messages in French
- localize devise emails
- refactoring admin crud (pages + layouts + snippets)
- refactoring page.rb => create module pagetree
x duplicate fields

View File

@ -1,6 +1,6 @@
$(document).ready(function() {
// automatic slug from snippet name
// automatic slug from collection name
$('#asset_collection_name').keypress(function() {
var input = $(this);
var slug = $('#asset_collection_slug');
@ -21,7 +21,7 @@ $(document).ready(function() {
var ids = jQuery.map(list.sortable('toArray'), function(e) {
return e.match(/asset-(\w+)/)[1];
}).join(',');
$('#asset_collection_assets_order').val(ids || '0');
$('#asset_collection_assets_order').val(ids || '');
}
$('ul.assets.sortable').sortable({

View File

@ -0,0 +1,16 @@
$(document).ready(function() {
// automatic slug from name
$('#content_type_name').keypress(function() {
var input = $(this);
var slug = $('#content_type_slug');
if (!slug.hasClass('filled')) {
setTimeout(function() {
slug.val(input.val().replace(/\s/g, '_').toLowerCase());
}, 50);
}
});
$('#content_type_slug').keypress(function() { $(this).addClass('filled'); });
});

View File

@ -63,8 +63,15 @@ Factory.define :theme_asset do |a|
a.association :site
end
## Asset collection ##
## Asset collections ##
Factory.define :asset_collection do |s|
s.association :site, :factory => :site
s.name 'Trip to Chicago'
end
## Content types ##
Factory.define :content_type do |t|
t.association :site, :factory => :site
t.name 'My project'
end

View File

@ -9,6 +9,7 @@ describe AssetCollection do
describe 'custom fields (beta)' do
before(:each) do
Site.any_instance.stubs(:create_default_pages!).returns(true)
site = Factory.build(:site)
Site.stubs(:find).returns(site)
@collection = Factory.build(:asset_collection, :site => site)
@ -139,13 +140,7 @@ describe AssetCollection do
end
context 'managing from hash' do
# before(:each) do
# site = Factory.build(:site)
# Site.stubs(:find).returns(site)
# @collection.site = site
# end
it 'should add new field' do
@collection.asset_custom_fields.clear
@collection.asset_custom_fields.build :label => 'Title'

View File

@ -0,0 +1,35 @@
require 'spec_helper'
describe ContentType do
before(:each) do
Site.any_instance.stubs(:create_default_pages!).returns(true)
end
it 'should have a valid factory' do
Factory.build(:content_type).should be_valid
end
# Validations ##
%w{site name}.each do |field|
it "should validate presence of #{field}" do
content_type = Factory.build(:content_type, field.to_sym => nil)
content_type.should_not be_valid
content_type.errors[field.to_sym].should == ["can't be blank"]
end
end
it 'should validate presence of slug' do
content_type = Factory.build(:content_type, :name => nil, :slug => nil)
content_type.should_not be_valid
content_type.errors[:slug].should == ["can't be blank"]
end
it 'should validate uniqueness of slug' do
content_type = Factory(:content_type)
(content_type = Factory.build(:content_type, :site => content_type.site)).should_not be_valid
content_type.errors[:slug].should == ["is already taken"]
end
end

View File

@ -38,7 +38,7 @@ describe Layout do
@layout.pages << page
@layout.save
end
it 'should add parts to pages if layout changes' do
@layout.value = @layout.value + "..."
page = Factory.build(:page, :layout => @layout, :site => nil)