templatized page can now use models from the main app + add new liquid filters

This commit is contained in:
Didier Lafforgue 2012-02-10 00:57:57 +01:00
parent 39d9c354da
commit 62eaeb10f5
20 changed files with 334 additions and 279 deletions

View File

@ -27,7 +27,7 @@ PATH
carrierwave-mongoid (~> 0.1.3) carrierwave-mongoid (~> 0.1.3)
cells (~> 3.8.0) cells (~> 3.8.0)
codemirror-rails (~> 2.21) codemirror-rails (~> 2.21)
custom_fields (~> 2.0.0.rc3) custom_fields (~> 2.0.0.rc4)
devise (~> 1.5.3) devise (~> 1.5.3)
dragonfly (~> 0.9.8) dragonfly (~> 0.9.8)
flash_cookie_session (~> 1.1.1) flash_cookie_session (~> 1.1.1)
@ -56,7 +56,7 @@ PATH
PATH PATH
remote: ../gems/custom_fields remote: ../gems/custom_fields
specs: specs:
custom_fields (2.0.0.rc3) custom_fields (2.0.0.rc4)
activesupport (~> 3.2.1) activesupport (~> 3.2.1)
carrierwave-mongoid (~> 0.1.3) carrierwave-mongoid (~> 0.1.3)
mongoid (~> 2.4.3) mongoid (~> 2.4.3)
@ -123,7 +123,7 @@ GEM
childprocess (0.3.0) childprocess (0.3.0)
ffi (~> 1.0.6) ffi (~> 1.0.6)
chunky_png (1.2.5) chunky_png (1.2.5)
codemirror-rails (2.21) codemirror-rails (2.21.1)
railties (~> 3.0) railties (~> 3.0)
coffee-rails (3.2.2) coffee-rails (3.2.2)
coffee-script (>= 2.2.0) coffee-script (>= 2.2.0)

View File

@ -21,4 +21,7 @@ class Locomotive.Models.Page extends Backbone.Model
delete hash['editable_elements'] delete hash['editable_elements']
hash.editable_elements = @get('editable_elements').toJSONForSave() if @get('editable_elements')? && @get('editable_elements').length > 0 hash.editable_elements = @get('editable_elements').toJSONForSave() if @get('editable_elements')? && @get('editable_elements').length > 0
delete hash['target_klass_name']
hash.target_klass_name = @get('target_klass_name') if @get('templatized') == true
class Locomotive.Models.PagesCollection extends Backbone.Collection class Locomotive.Models.PagesCollection extends Backbone.Collection

View File

@ -111,9 +111,9 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
@_enable_checkbox 'templatized', @_enable_checkbox 'templatized',
features: ['slug', 'redirect', 'listed'] features: ['slug', 'redirect', 'listed']
on_callback: => on_callback: =>
@$('li#page_content_type_id_input').show() @$('li#page_target_klass_name_input').show()
off_callback: => off_callback: =>
@$('li#page_content_type_id_input').hide() @$('li#page_target_klass_name_input').hide()
enable_redirect_checkbox: -> enable_redirect_checkbox: ->
@_enable_checkbox 'redirect', @_enable_checkbox 'redirect',

View File

@ -15,22 +15,12 @@ module Locomotive
skip_before_filter :verify_authenticity_token skip_before_filter :verify_authenticity_token
skip_load_and_authorize_resource
self.responder = Locomotive::ActionController::Responder # custom responder self.responder = Locomotive::ActionController::Responder # custom responder
respond_to :json, :xml respond_to :json, :xml
rescue_from CanCan::AccessDenied do |exception|
::Locomotive.log "[CanCan::AccessDenied] #{exception.inspect}"
if request.xhr?
render :json => { :error => exception.message }
else
flash[:alert] = exception.message
redirect_to pages_url
end
end
protected protected
def current_ability def current_ability

View File

@ -32,6 +32,13 @@ module Locomotive
list list
end end
def options_for_target_klass_name
base_models = current_site.content_types.map do |type|
[type.name.humanize, type.klass_with_custom_fields(:entries)]
end
base_models + Locomotive.config.models_for_templatization.map { |name| [name.underscore.humanize, name] }
end
def options_for_page_cache_strategy def options_for_page_cache_strategy
[ [
[t('.cache_strategy.none'), 'none'], [t('.cache_strategy.none'), 'none'],

View File

@ -57,6 +57,10 @@ module Locomotive
next_or_previous :lt next_or_previous :lt
end end
def self.find_by_permalink(permalink)
self.where(:_slug => permalink).first
end
def self.sort_entries!(ids) def self.sort_entries!(ids)
list = self.any_in(:_id => ids.map { |id| BSON::ObjectId.from_string(id.to_s) }).to_a list = self.any_in(:_id => ids.map { |id| BSON::ObjectId.from_string(id.to_s) }).to_a
ids.each_with_index do |id, position| ids.each_with_index do |id, position|

View File

@ -18,8 +18,6 @@ module Locomotive
accepts_nested_attributes_for :editable_elements accepts_nested_attributes_for :editable_elements
end end
module InstanceMethods
def disable_parent_editable_elements(block) def disable_parent_editable_elements(block)
self.editable_elements.each { |el| el.disabled = true if el.from_parent? && el.block == block } self.editable_elements.each { |el| el.disabled = true if el.from_parent? && el.block == block }
end end
@ -116,8 +114,6 @@ module Locomotive
end end
end end
end
end end
end end
end end

View File

@ -20,8 +20,6 @@ module Locomotive
scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } } scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } }
end end
module InstanceMethods
def template def template
@template ||= Marshal.load(self.serialized_template.to_s) rescue nil @template ||= Marshal.load(self.serialized_template.to_s) rescue nil
end end
@ -109,8 +107,6 @@ module Locomotive
end end
end end
end
end end
end end
end end

View File

@ -7,21 +7,54 @@ module Locomotive
included do included do
belongs_to :content_type, :class_name => 'Locomotive::ContentType'
field :templatized, :type => Boolean, :default => false field :templatized, :type => Boolean, :default => false
field :target_klass_name
field :content_type_visible_column ## validations ##
validates_presence_of :target_klass_name, :if => :templatized?
validate :ensure_target_klass_name_security
## callbacks ##
before_validation :set_slug_if_templatized before_validation :set_slug_if_templatized
before_validation :ensure_target_klass_name_security
end end
module InstanceMethods def target_klass
target_klass_name.constantize
end
def target_entry_name
if self.target_klass_name =~ /^Locomotive::Entry([a-z0-9]+)$/
@content_type ||= self.site.content_types.find($1)
@content_type.slug.singularize
else
self.target_klass_name.underscore
end
end
def fetch_target_entry(permalink)
target_klass.find_by_permalink(permalink)
end
protected
def set_slug_if_templatized def set_slug_if_templatized
self.slug = 'content_type_template' if self.templatized? self.slug = 'content_type_template' if self.templatized?
end end
def ensure_target_klass_name_security
return if !self.templatized? || self.target_klass_name.blank?
if self.target_klass_name =~ /^Locomotive::Entry([a-z0-9]+)$/
content_type = Locomotive::ContentType.find($1)
if content_type.site_id != self.site_id
self.errors.add :target_klass_name, :security
end
elsif !Locomotive.config.models_for_templatization.include?(self.target_klass_name)
self.errors.add :target_klass_name, :security
end
end end
end end

View File

@ -7,8 +7,12 @@ module Locomotive
self.source.ordered_entries_custom_fields.collect(&:as_json) self.source.ordered_entries_custom_fields.collect(&:as_json)
end end
def klass_name
self.source.klass_with_custom_fields(:entries).to_s
end
def included_methods def included_methods
super + %w(name description slug order_by order_direction highlighted_field_name group_by_field_name api_accounts entries_custom_fields) super + %w(name description slug order_by order_direction highlighted_field_name group_by_field_name api_accounts entries_custom_fields klass_name)
end end
end end

View File

@ -30,10 +30,10 @@
= f.inputs :name => :advanced_options, :id => 'advanced-options', :class => "inputs foldable #{'folded' if inputs_folded?(@page)}" do = f.inputs :name => :advanced_options, :id => 'advanced-options', :class => "inputs foldable #{'folded' if inputs_folded?(@page)}" do
= f.input :content_type_id, :as => :select, :collection => current_site.content_types.all.to_a, :include_blank => false, :wrapper_html => { :style => "#{'display: none' unless @page.templatized?}" }
= f.input :templatized, :as => :'Locomotive::Toggle', :style => "#{'display: none' if @page.redirect?}" = f.input :templatized, :as => :'Locomotive::Toggle', :style => "#{'display: none' if @page.redirect?}"
= f.input :target_klass_name, :as => :select, :collection => options_for_target_klass_name, :include_blank => false, :wrapper_html => { :style => "#{'display: none' unless @page.templatized?}" }
= f.input :published, :as => :'Locomotive::Toggle' = f.input :published, :as => :'Locomotive::Toggle'
= f.input :listed, :as => :'Locomotive::Toggle' = f.input :listed, :as => :'Locomotive::Toggle'

View File

@ -33,7 +33,7 @@ fr:
extname_changed: "Nouveau fichier n'a pas l'extension original" extname_changed: "Nouveau fichier n'a pas l'extension original"
array_too_short: "est trop petit (le nombre minimum d'éléments est %{count})" array_too_short: "est trop petit (le nombre minimum d'éléments est %{count})"
liquid_syntax: "Erreur de syntaxe ('%{error}')" liquid_syntax: "Erreur de syntaxe ('%{error}')"
invalid_theme_file: "doit être rempli ou n'est pas un fichier zip" security: "présente un problème de sécurité"
page: page:
liquid_syntax: "Erreur de syntaxe dans les sections de page, veuillez vérifier la syntaxe ('%{error}'/'%{fullpath}')" liquid_syntax: "Erreur de syntaxe dans les sections de page, veuillez vérifier la syntaxe ('%{error}'/'%{fullpath}')"
liquid_extend: "La page '%{fullpath}' étend le contenu d'une page qui n'existe pas" liquid_extend: "La page '%{fullpath}' étend le contenu d'une page qui n'existe pas"

View File

@ -54,6 +54,7 @@ en:
password_confirmation: New password confirmation password_confirmation: New password confirmation
page: page:
seo_title: Title seo_title: Title
target_klass_name: Model
site: site:
locales: Languages locales: Languages
@ -63,7 +64,7 @@ en:
cache_strategy: "Cache the page for better performance. The 'Simple' choice is a good compromise." cache_strategy: "Cache the page for better performance. The 'Simple' choice is a good compromise."
templatized: "Use the page as a template for a model you defined." templatized: "Use the page as a template for a model you defined."
listed: "Control whether to show the page from generated menus." listed: "Control whether to show the page from generated menus."
content_type_id: "The type of content this page will be a template for." target_klass_name: "The type of content this page will be a template for."
seo_title: "Define a page title which should be used as the value for the title tag in the head section. Leave it empty if you want to use the default value from the site settings." seo_title: "Define a page title which should be used as the value for the title tag in the head section. Leave it empty if you want to use the default value from the site settings."
meta_keywords: "Overrides the site's meta keywords used within the head tag of the page. They are separated by a comma." meta_keywords: "Overrides the site's meta keywords used within the head tag of the page. They are separated by a comma."
meta_description: "Overrides the site's meta description used within the head tag of the page." meta_description: "Overrides the site's meta description used within the head tag of the page."

View File

@ -51,7 +51,7 @@ Locomotive.configure do |config|
# tell if logs are enabled. Useful for debug purpose. # tell if logs are enabled. Useful for debug purpose.
config.enable_logs = true config.enable_logs = true
# Configure the e-mail address which will be shown in the DeviseMailer, NotificationMailer, ...etc # configure the e-mail address which will be shown in the DeviseMailer, NotificationMailer, ...etc
# if you do not put the domain name in the email, Locomotive will take the default domain name depending # if you do not put the domain name in the email, Locomotive will take the default domain name depending
# on your deployment target (server, Heroku, Bushido, ...etc) # on your deployment target (server, Heroku, Bushido, ...etc)
# #
@ -65,6 +65,9 @@ Locomotive.configure do |config|
# follow the Dependency Injection pattern # follow the Dependency Injection pattern
# config.context_assign_extensions = {} # config.context_assign_extensions = {}
# add extra classes other than the defined content types among a site which will potentially used by the templatized pages.
# config.models_for_templatization = %w(Product)
# Rack-cache settings, mainly used for the inline resizing image module. Default options: # Rack-cache settings, mainly used for the inline resizing image module. Default options:
# config.rack_cache = { # config.rack_cache = {
# :verbose => true, # :verbose => true,

View File

@ -43,7 +43,6 @@ module Locomotive
end end
def localized? def localized?
Rails.logger.debug "localized? #{@locomotive_localized.inspect}"
!!@locomotive_localized !!@locomotive_localized
end end

View File

@ -27,7 +27,8 @@ module Locomotive
:entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body") :entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
}, },
:devise_modules => [:rememberable, :database_authenticatable, :token_authenticatable, :recoverable, :trackable, :validatable, :encryptable, { :encryptor => :sha1 }], :devise_modules => [:rememberable, :database_authenticatable, :token_authenticatable, :recoverable, :trackable, :validatable, :encryptable, { :encryptor => :sha1 }],
:context_assign_extensions => { } :context_assign_extensions => { },
:models_for_templatization => []
} }
cattr_accessor :settings cattr_accessor :settings

View File

@ -11,6 +11,10 @@ module Locomotive
input.to_s.gsub(' ', '-').gsub('/', '-').dasherize input.to_s.gsub(' ', '-').gsub('/', '-').dasherize
end end
def multi_line(input)
input.to_s.gsub("\n", '<br/>')
end
def concat(input, *args) def concat(input, *args)
result = input.to_s result = input.to_s
args.flatten.each { |a| result << a.to_s } args.flatten.each { |a| result << a.to_s }
@ -21,6 +25,14 @@ module Locomotive
(index.to_i + 1) % modulo == 0 ? word : '' (index.to_i + 1) % modulo == 0 ? word : ''
end end
def first(input)
input.first
end
def last(input)
input.last
end
end end
::Liquid::Template.register_filter(Misc) ::Liquid::Template.register_filter(Misc)

View File

@ -3,8 +3,6 @@ module Locomotive
extend ActiveSupport::Concern extend ActiveSupport::Concern
module InstanceMethods
protected protected
def render_locomotive_page def render_locomotive_page
@ -45,7 +43,7 @@ module Locomotive
page = nil page = nil
else else
if page.templatized? if page.templatized?
@content_entry = page.content_type.entries.where(:_slug => File.basename(path.first)).first @content_entry = page.fetch_target_entry(File.basename(path.first))
if @content_entry.nil? || (!@content_entry.visible? && current_locomotive_account.nil?) # content instance not found or not visible if @content_entry.nil? || (!@content_entry.visible? && current_locomotive_account.nil?) # content instance not found or not visible
page = nil page = nil
@ -80,7 +78,7 @@ module Locomotive
if @page.templatized? # add instance from content type if @page.templatized? # add instance from content type
assigns['entry'] = @content_entry assigns['entry'] = @content_entry
assigns[@page.content_type.slug.singularize] = @content_entry # just here to help to write readable liquid code assigns[@page.target_entry_name] = @content_entry # just here to help to write readable liquid code
end end
registers = { registers = {
@ -124,6 +122,4 @@ module Locomotive
end end
end end
end
end end

View File

@ -0,0 +1,7 @@
class Foo
include Mongoid::Document
field :name
end

View File

@ -66,6 +66,9 @@ Locomotive.configure do |config|
# follow the Dependency Injection pattern # follow the Dependency Injection pattern
# config.context_assign_extensions = {} # config.context_assign_extensions = {}
# add extra classes other than the defined content types among a site which will potentially used by the templatized pages.
config.models_for_templatization = %w(Foo)
# Rack-cache settings, mainly used for the inline resizing image module. Default options: # Rack-cache settings, mainly used for the inline resizing image module. Default options:
# config.rack_cache = { # config.rack_cache = {
# :verbose => true, # :verbose => true,