Compare commits

...

10 Commits

Author SHA1 Message Date
Mario Visic
177f1e6c82 Merge pull request #416 from miskander/wildcards
Merged master into wildcards
2012-05-24 03:45:35 -07:00
Marko Iskander
02ea6913e3 Merge branch 'master' into wildcards
Conflicts:
	Gemfile.lock
	app/assets/javascripts/locomotive/models/page.js.coffee
	app/assets/javascripts/locomotive/views/pages/_form_view.js.coffee
	app/models/locomotive/extensions/site/locales.rb
	app/models/locomotive/page.rb
	app/views/locomotive/pages/_form.html.haml
	config/initializers/haml.rb
	config/locales/default.ru.yml
	config/locales/formtastic.ru.yml
	doc/TODO
	lib/locomotive/haml.rb
	script/upgrade_v1.rb
	spec/dummy/config/initializers/haml.rb
2012-05-22 20:39:30 -04:00
Didier Lafforgue
62e2a5029d use the last version of FOG 2012-04-11 03:40:00 +02:00
Didier Lafforgue
63fd77f950 handle is unique within the scope of a site 2012-04-11 00:21:20 +02:00
Didier Lafforgue
14596a5443 fix a couple of annoying engine bugs + implement the first version of the find tag 2012-04-10 17:44:13 +02:00
did
4d1b986f1b fix broken tests + make the editing of locales more robust 2012-04-10 04:55:59 -07:00
Didier Lafforgue
e26febdaf6 refactoring (wip) 2012-04-06 12:23:11 +02:00
Didier Lafforgue
260f9ae4ef one way to avoid autosave 2012-04-05 18:02:16 +02:00
Didier Lafforgue
64ea41cbcd one way to avoid autosave 2012-04-05 18:01:16 +02:00
Didier Lafforgue
64168cad37 wip 2012-04-05 17:12:26 +02:00
62 changed files with 910 additions and 466 deletions

2
1 Normal file
View File

@ -0,0 +1,2 @@
Haml::Template.options[:format] = :html5
Haml::Template.options[:ugly] = true # improve performance in dev

View File

@ -8,6 +8,7 @@ gemspec # Include gemspec dependencies
# The rest of the dependencies are for use when in the locomotive development environment # The rest of the dependencies are for use when in the locomotive development environment
group :development do group :development do
# gem 'locomotive-mongoid-tree', :path => '../gems/custom_fields' # for Developers
# gem 'custom_fields', :path => '../gems/custom_fields' # for Developers # gem 'custom_fields', :path => '../gems/custom_fields' # for Developers
# gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => '2.0.0.rc' # Branch on Github # gem 'custom_fields', :git => 'git://github.com/locomotivecms/custom_fields.git', :branch => '2.0.0.rc' # Branch on Github

View File

@ -16,12 +16,9 @@ class Locomotive.Models.Page extends Backbone.Model
toJSON: -> toJSON: ->
_.tap super, (hash) => _.tap super, (hash) =>
_.each ['fullpath', 'localized_fullpaths', 'templatized_from_parent', 'target_klass_name_text', 'content_type_id_text', 'edit_url', 'parent_id_text', 'response_type_text'], (key) => delete hash[key] _.each ['fullpath', 'localized_fullpaths', 'edit_url', 'parent_id_text', 'response_type_text'], (key) => delete hash[key]
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'] class Locomotive.Models.PagesCollection extends Backbone.Collection
hash.target_klass_name = @get('target_klass_name') if @get('templatized') == true
class Locomotive.Models.PagesCollection extends Backbone.Collection

View File

@ -41,7 +41,7 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
@enable_response_type_select() @enable_response_type_select()
# enable check boxes # enable check boxes
@enable_templatized_checkbox() @enable_wildcard_checkbox()
@enable_redirect_checkbox() @enable_redirect_checkbox()
@ -72,7 +72,7 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
@editor = CodeMirror.fromTextArea input.get()[0], @editor = CodeMirror.fromTextArea input.get()[0],
mode: 'liquid' mode: 'liquid'
autoMatchParens: false autoMatchParens: false
lineNumbers: false lineNumbers: true
passDelay: 50 passDelay: 50
tabMode: 'shift' tabMode: 'shift'
theme: 'default' theme: 'default'
@ -109,14 +109,9 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
url: @$('#page_slug').attr('data-url') url: @$('#page_slug').attr('data-url')
type: 'get' type: 'get'
dataType: 'json' dataType: 'json'
data: { parent_id: @$('#page_parent_id').val(), slug: @$('#page_slug').val() } data: { parent_id: @$('#page_parent_id').val(), slug: @$('#page_slug').val(), wildcard: @model.get('wildcard') }
success: (data) => success: (data) =>
@$('#page_slug_input .inline-hints').html(data.url).effect('highlight') @$('#page_slug_input .inline-hints').html(data.url).effect('highlight')
if data.templatized_parent
@$('li#page_slug_input').show()
@$('li#page_templatized_input, li#page_target_klass_name_input').hide()
else
@$('li#page_templatized_input').show() unless @model.get('redirect')
enable_response_type_select: -> enable_response_type_select: ->
@$('li#page_response_type_input').change (event) => @$('li#page_response_type_input').change (event) =>
@ -126,19 +121,13 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
@model.set redirect: false @model.set redirect: false
@$('li#page_redirect_input, li#page_redirect_url_input').hide() @$('li#page_redirect_input, li#page_redirect_url_input').hide()
enable_templatized_checkbox: -> enable_wildcard_checkbox: ->
@_enable_checkbox 'templatized', @_enable_checkbox 'wildcard',
features: ['slug', 'redirect', 'listed'] features: ['redirect', 'listed']
on_callback: =>
@$('li#page_target_klass_name_input').show()
off_callback: =>
@$('li#page_target_klass_name_input').hide()
@$('li#page_templatized_input').hide() if @model.get('templatized_from_parent') == true
enable_redirect_checkbox: -> enable_redirect_checkbox: ->
@_enable_checkbox 'redirect', @_enable_checkbox 'redirect',
features: ['templatized', 'cache_strategy'] features: ['wildcard', 'cache_strategy']
on_callback: => on_callback: =>
@$('li#page_redirect_url_input').show() @$('li#page_redirect_url_input').show()
off_callback: => off_callback: =>
@ -146,4 +135,4 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
enable_other_checkboxes: -> enable_other_checkboxes: ->
_.each ['published', 'listed'], (exp) => _.each ['published', 'listed'], (exp) =>
@$('li#page_' + exp + '_input input[type=checkbox]').checkToggle() @$('li#page_' + exp + '_input input[type=checkbox]').checkToggle()

View File

@ -271,7 +271,7 @@ ul.list {
cursor: move; cursor: move;
} }
&.templatized > .inner > a { &.wildcard > .inner > a {
padding-right: 24px; padding-right: 24px;
background: transparent image-url("locomotive/list/icons/template.png") no-repeat right 2px; background: transparent image-url("locomotive/list/icons/template.png") no-repeat right 2px;
} }

View File

@ -53,10 +53,10 @@ module Locomotive
end end
def get_path def get_path
page = current_site.pages.build(:parent => current_site.pages.find(params[:parent_id]), :slug => params[:slug].permalink).tap do |p| page = current_site.pages.build(:parent => current_site.pages.find(params[:parent_id]), :slug => params[:slug].permalink, :wildcard => params[:wildcard]).tap do |p|
p.valid?; p.send(:build_fullpath) p.valid?; p.send(:build_fullpath)
end end
render :json => { :url => public_page_url(page), :slug => page.slug, :templatized_parent => page.templatized_from_parent? } render :json => { :url => public_page_url(page), :slug => page.slug }
end end
end end

View File

@ -99,13 +99,13 @@ module Locomotive
Locomotive.config.multi_sites? Locomotive.config.multi_sites?
end end
def public_page_url(page, options = {}) # def public_page_url(page, options = {})
if content = options.delete(:content) # if content = options.delete(:content)
File.join(current_site_public_url, page.fullpath.gsub('content_type_template', ''), content._slug) # File.join(current_site_public_url, page.fullpath.gsub('content_type_template', ''), content._slug)
else # else
File.join(current_site_public_url, page.fullpath) # File.join(current_site_public_url, page.fullpath)
end # end
end # end
# memberships # memberships

View File

@ -2,7 +2,7 @@ module Locomotive
module PagesHelper module PagesHelper
def css_for_page(page) def css_for_page(page)
%w(index not_found templatized redirect).inject([]) do |memo, state| %w(index not_found wildcard redirect).inject([]) do |memo, state|
memo << state.dasherize if page.send(:"#{state}?") memo << state.dasherize if page.send(:"#{state}?")
memo memo
end.join(' ') end.join(' ')
@ -32,13 +32,6 @@ 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

@ -0,0 +1,196 @@
module Locomotive
module Extensions
module Page
module Fullpath
extend ActiveSupport::Concern
included do
## fields ##
field :fullpath, :localize => true
field :wildcard, :type => Boolean, :default => false
field :wildcards, :type => Array
## callbacks ##
before_validation :get_wildcards_from_parent
before_validation :add_slug_to_wildcards
before_save :build_fullpath
after_update :propagate_fullpath_changes
# before_save :set_children_autosave
# before_rearrange :foo #propagate_fullpath_changes
# after_save :propagate_fullpath_changes
# after_save { |p| puts "[after_save] #{p.fullpath} / #{p.wildcards.inspect} / #{p.wildcard?}" }
## scopes ##
# scope :fullpath, lambda { |fullpath| { :where => { :fullpath => fullpath } } } # used ?
## virtual attributes ##
attr_accessor :wildcards_hash
end
# def foo
# Rails.logger.debug "----> rearranging #{self.slug}.......\n\n"
# puts "[rearranging]"
# end
# This method returns true if the fullpath is enhanced
# by wildcards. It is different from the wildcard? method
# because it includes the ancestors when determining if
# the current page has wildcards or not.
#
def has_wildcards?
self.wildcard? || !self.fullpath.try(:index, '*').nil?
end
# It returns a pretty output of the fullpath. The "*" characters
# are replaced by the following pattern ":<slug>" like the ones you can find
# in the Ruby on Rails routes.
#
def pretty_fullpath
return self.fullpath unless self.has_wildcards?
index = 0
self.fullpath.split('/').map do |segment|
if segment == '*'
":#{self.wildcards[index]}".tap { index += 1 }
else
segment
end
end.join('/')
end
# It returns the fullpath with wildcard segments replaced by the values
# specified in the first argument.
#
# @param [ Hash ] values The map assigning to a wildcard name its value
# @return [ String ] The compiled fullpath
#
def compiled_fullpath(values)
return self.fullpath unless self.has_wildcards?
index = 0
self.fullpath.split('/').map do |segment|
if segment == '*'
"#{values[self.wildcards[index]]}".tap { index += 1 }
else
segment
end
end.join('/')
end
# It builds the map associating the name of a wildcard
# with its value within the path.
# The map is also stored in the wildcards_hash attribute
# of the page.
#
# @param [ String ] path the path from the HTTP request
# @return [ Hash ] The map
#
def match_wildcards(path)
self.wildcards_hash, wildcard_index, segments = {}, 0, self.fullpath.split('/')
path.split('/').each_with_index do |segment, index|
if segments[index] == '*'
self.wildcards_hash[self.wildcards[wildcard_index].underscore] = segment
wildcard_index += 1
end
end
self.wildcards_hash
end
protected
# def set_children_autosave
# @autosave_for_children = !must_propagate_fullpath_changes?
# true
# end
def get_wildcards_from_parent
return true if self.parent.nil?
if self.parent.has_wildcards?
# puts "[get_wildcards_from_parent] #{self.slug} - #{self.parent.wildcards.inspect}"
self.wildcards = self.parent.wildcards.clone
else
# puts "[get_wildcards_from_parent] #{self.slug} - reset wildcards"
self.wildcards = []
end
end
def add_slug_to_wildcards
# puts "[add_slug_to_wildcards] #{self.slug} / #{self.wildcard?}"
(self.wildcards ||= []) << self.slug if self.wildcard?
end
def build_fullpath
if self.index? || self.not_found?
self.fullpath = self.slug
else
segments = (self.parent.fullpath.try(:split, '/') || [nil]) + [self.wildcard? ? '*' : self.slug]
segments.shift if segments.first == 'index'
self.fullpath = File.join segments.compact
end
end
def propagate_fullpath_changes
if self.fullpath_changed? || self.wildcards_changed?
self.rearrange_children!
end
end
# def must_propagate_fullpath_changes?
# self.wildcard_changed? || self.slug_changed?
# end
#
# def propagate_fullpath_changes
# return true unless must_propagate_fullpath_changes?
#
# parent_identities = { self._id => self }
#
# Rails.logger.debug "[propagate_fullpath_changes] BEGIN page #{self.slug} #{self.fullpath} / #{self.wildcards.inspect} / #{self._parent.try(:has_wildcards?).inspect}"
# puts "[propagate_fullpath_changes] BEGIN page #{self.slug} #{self.fullpath} / #{self.wildcards.inspect} / #{self._parent.try(:has_wildcards?).inspect}"
#
# self.descendants.order_by([[:depth, :asc]]).each do |page|
# _parent = parent_identities[page.parent_id]
# _fullpath = {}
# _wildcards = nil
#
# puts "[propagate_fullpath_changes] #{page.fullpath} / #{page.wildcards.inspect} / #{page._parent.try(:has_wildcards?).inspect}"
#
# if _parent.has_wildcards?
# _wildcards = _parent.wildcards + (page.wildcard? ? [page.slug] : [])
# end
#
# self.site.locales.each do |locale|
# base_fullpath = _parent.fullpath_translations[locale]
# slug = page.wildcard? ? '*' : page.slug_translations[locale]
#
# next if base_fullpath.blank?
#
# _fullpath[locale] = File.join(base_fullpath, slug)
# end
#
# selector = { 'id' => page._id }
# operations = {
# '$set' => {
# 'wildcards' => _wildcards,
# 'fullpath' => _fullpath
# }
# }
# self.collection.update selector, operations
#
# parent_identities[page._id] = page
# end
# end
end
end
end
end

View File

@ -22,7 +22,8 @@ module Locomotive
validate :template_must_be_valid validate :template_must_be_valid
## scopes ## ## scopes ##
scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } } scope :pages, lambda { |domain| { :any_in => { :domains => [*domain] } } }
scope :dependent_from, lambda { |id| { :where => { :template_dependencies.in => [id] } } }
end end
def template def template

View File

@ -11,11 +11,10 @@ module Locomotive
module ClassMethods module ClassMethods
# Given both a site and a path, this method tries # Given both a site and a path, this method retrieves
# to get the matching page. # the matching page if it exists.
# If the page is templatized, the related content entry is # If the found page owns wildcards in its fullpath, then
# associated to the page (page.content_entry stores the entry). # assigns the value for each wildcard and store the result within the page.
# If no page is found, then it returns the 404 one instead.
# #
# @param [ Site ] site The site where to find the page # @param [ Site ] site The site where to find the page
# @param [ String ] path The fullpath got from the request # @param [ String ] path The fullpath got from the request
@ -33,17 +32,7 @@ module Locomotive
if !_page.published? && !logged_in if !_page.published? && !logged_in
next next
else else
if _page.templatized? _page.match_wildcards(path)
%r(^#{_page.fullpath.gsub('content_type_template', '([^\/]+)')}$) =~ path
permalink = $1
_page.content_entry = _page.fetch_target_entry(permalink)
if _page.content_entry.nil? || (!_page.content_entry.visible? && !logged_in) # content instance not found or not visible
next
end
end
end end
page = _page page = _page
@ -56,9 +45,7 @@ module Locomotive
# Calculate all the combinations possible based on the # Calculate all the combinations possible based on the
# fact that one of the segment of the path could be # fact that one of the segment of the path could be
# a content type from a templatized page. # a wildcard.
# We postulate that there is only one templatized page in a path
# (ie: no nested templatized pages)
# #
# @param [ String ] path The path to the page # @param [ String ] path The path to the page
# #
@ -69,13 +56,13 @@ module Locomotive
end end
#:nodoc: #:nodoc:
def _path_combinations(segments, can_include_template = true) def _path_combinations(segments)
return nil if segments.empty? return nil if segments.empty?
segment = segments.shift segment = segments.shift
(can_include_template ? [segment, 'content_type_template'] : [segment]).map do |_segment| [segment, '*'].map do |_segment|
if (_combinations = _path_combinations(segments.clone, can_include_template && _segment != 'content_type_template')) if (_combinations = _path_combinations(segments.clone))
[*_combinations].map do |_combination| [*_combinations].map do |_combination|
File.join(_segment, _combination) File.join(_segment, _combination)
end end

View File

@ -1,136 +1,136 @@
module Locomotive # module Locomotive
module Extensions # module Extensions
module Page # module Page
module Templatized # module Templatized
#
extend ActiveSupport::Concern # extend ActiveSupport::Concern
#
included do # included do
#
## fields ## # ## fields ##
field :templatized, :type => Boolean, :default => false # field :templatized, :type => Boolean, :default => false
field :templatized_from_parent, :type => Boolean, :default => false # field :templatized_from_parent, :type => Boolean, :default => false
field :target_klass_name # field :target_klass_name
#
## validations ## # ## validations ##
validates_presence_of :target_klass_name, :if => :templatized? # validates_presence_of :target_klass_name, :if => :templatized?
validate :ensure_target_klass_name_security # validate :ensure_target_klass_name_security
#
## callbacks ## # ## callbacks ##
before_validation :get_templatized_from_parent # before_validation :get_templatized_from_parent
before_validation :set_slug_if_templatized # before_validation :set_slug_if_templatized
before_validation :ensure_target_klass_name_security # before_validation :ensure_target_klass_name_security
after_save :propagate_templatized # after_save :propagate_templatized
#
## scopes ## # ## scopes ##
scope :templatized, :where => { :templatized => true } # scope :templatized, :where => { :templatized => true }
#
## virtual attributes ## # ## virtual attributes ##
attr_accessor :content_entry # attr_accessor :content_entry
end # end
#
# Returns the class specified by the target_klass_name property # # Returns the class specified by the target_klass_name property
# # #
# @example # # @example
# # #
# page.target_klass_name = 'Locomotive::Entry12345' # # page.target_klass_name = 'Locomotive::Entry12345'
# page.target_klass = Locomotive::Entry12345 # # page.target_klass = Locomotive::Entry12345
# # #
# @return [ Class ] The target class # # @return [ Class ] The target class
# # #
def target_klass # def target_klass
target_klass_name.constantize # target_klass_name.constantize
end # end
#
# Gives the name which can be used in a liquid template in order # # Gives the name which can be used in a liquid template in order
# to reference an entry. It uses the slug property if the target klass # # to reference an entry. It uses the slug property if the target klass
# is a Locomotive content type or the class name itself for the other classes. # # is a Locomotive content type or the class name itself for the other classes.
# # #
# @example # # @example
# # #
# page.target_klass_name = 'Locomotive::Entry12345' # related to the content type Articles # # page.target_klass_name = 'Locomotive::Entry12345' # related to the content type Articles
# page.target_entry_name = 'article' # # page.target_entry_name = 'article'
# # #
# page.target_klass_name = 'OurProduct' # # page.target_klass_name = 'OurProduct'
# page.target_entry_name = 'our_product' # # page.target_entry_name = 'our_product'
# # #
# @return [ String ] The name in lowercase and underscored # # @return [ String ] The name in lowercase and underscored
# # #
def target_entry_name # def target_entry_name
if self.target_klass_name =~ /^Locomotive::Entry([a-z0-9]+)$/ # if self.target_klass_name =~ /^Locomotive::Entry([a-z0-9]+)$/
@content_type ||= self.site.content_types.find($1) # @content_type ||= self.site.content_types.find($1)
@content_type.slug.singularize # @content_type.slug.singularize
else # else
self.target_klass_name.underscore # self.target_klass_name.underscore
end # end
end # end
#
# Finds the entry both specified by the target klass and identified by the permalink # # Finds the entry both specified by the target klass and identified by the permalink
# # #
# @param [ String ] permalink The permalink of the entry # # @param [ String ] permalink The permalink of the entry
# # #
# @return [ Object ] The document # # @return [ Object ] The document
# # #
def fetch_target_entry(permalink) # def fetch_target_entry(permalink)
target_klass.find_by_permalink(permalink) # target_klass.find_by_permalink(permalink)
end # end
#
protected # protected
#
def get_templatized_from_parent # def get_templatized_from_parent
return if self.parent.nil? # return if self.parent.nil?
#
if self.parent.templatized? # if self.parent.templatized?
self.templatized = self.templatized_from_parent = true # self.templatized = self.templatized_from_parent = true
self.target_klass_name = self.parent.target_klass_name # self.target_klass_name = self.parent.target_klass_name
elsif !self.templatized? # elsif !self.templatized?
self.templatized = self.templatized_from_parent = false # self.templatized = self.templatized_from_parent = false
self.target_klass_name = nil # self.target_klass_name = nil
end # end
end # end
#
def set_slug_if_templatized # def set_slug_if_templatized
self.slug = 'content_type_template' if self.templatized? && !self.templatized_from_parent? # self.slug = 'content_type_template' if self.templatized? && !self.templatized_from_parent?
end # end
#
# Makes sure the target_klass is owned by the site OR # # Makes sure the target_klass is owned by the site OR
# if it belongs to the models allowed by the application # # if it belongs to the models allowed by the application
# thanks to the models_for_templatization option. # # thanks to the models_for_templatization option.
# # #
def ensure_target_klass_name_security # def ensure_target_klass_name_security
return if !self.templatized? || self.target_klass_name.blank? # return if !self.templatized? || self.target_klass_name.blank?
#
if self.target_klass_name =~ /^Locomotive::Entry([a-z0-9]+)$/ # if self.target_klass_name =~ /^Locomotive::Entry([a-z0-9]+)$/
content_type = Locomotive::ContentType.find($1) # content_type = Locomotive::ContentType.find($1)
#
if content_type.site_id != self.site_id # if content_type.site_id != self.site_id
self.errors.add :target_klass_name, :security # self.errors.add :target_klass_name, :security
end # end
elsif !Locomotive.config.models_for_templatization.include?(self.target_klass_name) # elsif !Locomotive.config.models_for_templatization.include?(self.target_klass_name)
self.errors.add :target_klass_name, :security # self.errors.add :target_klass_name, :security
end # end
end # end
#
# Sets the templatized, templatized_from_parent properties of # # Sets the templatized, templatized_from_parent properties of
# the children of the current page ONLY IF the templatized # # the children of the current page ONLY IF the templatized
# attribute got changed. # # attribute got changed.
# # #
def propagate_templatized # def propagate_templatized
return unless self.templatized_changed? # return unless self.templatized_changed?
#
selector = { 'parent_ids' => { '$in' => [self._id] } } # selector = { 'parent_ids' => { '$in' => [self._id] } }
operations = { # operations = {
'$set' => { # '$set' => {
'templatized' => self.templatized, # 'templatized' => self.templatized,
'templatized_from_parent' => self.templatized, # 'templatized_from_parent' => self.templatized,
'target_klass_name' => self.target_klass_name # 'target_klass_name' => self.target_klass_name
} # }
} # }
#
self.collection.update selector, operations, :multi => true # self.collection.update selector, operations, :multi => true
end # end
#
end # end
end # end
end # end
end # end

View File

@ -77,6 +77,10 @@ module Locomotive
end end
# def autosave_for_children?
# @autosave_for_children != false
# end
# Returns the children of this node but with the minimal set of required attributes # Returns the children of this node but with the minimal set of required attributes
# #
# @return [ Array ] The children pages ordered by their position # @return [ Array ] The children pages ordered by their position

View File

@ -56,6 +56,10 @@ module Locomotive
self.locales_was.first || Locomotive.config.site_locales.first self.locales_was.first || Locomotive.config.site_locales.first
end end
def default_locale_changed?
self.default_locale != self.default_locale_was
end
def locale_fallbacks(locale) def locale_fallbacks(locale)
[locale.to_s] + (locales - [locale.to_s]) [locale.to_s] + (locales - [locale.to_s])
end end
@ -93,4 +97,22 @@ module Locomotive
end end
end end
end end
#
# %w(index 404).each do |slug|
# page = self.pages.where("slug.#{self.default_locale}" => slug).first
#
# self.locales.each do |locale|
# if .blank?
# page.collection.update({ :_id => page._id }, { '$set' => { "slug.#{locale}" => slug } })
# end
#
# if page.attributes['title'][locale.to_s].blank?
# page.collection.update({ :_id => page._id }, {
# '$set' => { "title.#{locale}" => }
# })
# end
# end
# end
# end

View File

@ -8,7 +8,7 @@ module Locomotive
include Extensions::Page::EditableElements include Extensions::Page::EditableElements
include Extensions::Page::Parse include Extensions::Page::Parse
include Extensions::Page::Render include Extensions::Page::Render
include Extensions::Page::Templatized include Extensions::Page::Fullpath
include Extensions::Page::Redirect include Extensions::Page::Redirect
include Extensions::Page::Listed include Extensions::Page::Listed
include Extensions::Shared::Seo include Extensions::Shared::Seo
@ -16,7 +16,6 @@ module Locomotive
## fields ## ## fields ##
field :title, :localize => true field :title, :localize => true
field :slug, :localize => true field :slug, :localize => true
field :fullpath, :localize => true
field :handle field :handle
field :raw_template, :localize => true field :raw_template, :localize => true
field :locales, :type => Array field :locales, :type => Array
@ -35,14 +34,13 @@ module Locomotive
## callbacks ## ## callbacks ##
after_initialize :set_default_raw_template after_initialize :set_default_raw_template
before_validation :normalize_slug before_validation :normalize_slug
before_save :build_fullpath
before_save :record_current_locale before_save :record_current_locale
before_destroy :do_not_remove_index_and_404_pages before_destroy :do_not_remove_index_and_404_pages
## validations ## ## validations ##
validates_presence_of :site, :title, :slug validates_presence_of :site, :title, :slug
validates_uniqueness_of :slug, :scope => [:site_id, :parent_id] validates_uniqueness_of :slug, :scope => [:site_id, :parent_id]
validates_uniqueness_of :handle, :allow_blank => true validates_uniqueness_of :handle, :allow_blank => true, :scope => [:site_id]
validates_exclusion_of :slug, :in => Locomotive.config.reserved_slugs, :if => Proc.new { |p| p.depth <= 1 } validates_exclusion_of :slug, :in => Locomotive.config.reserved_slugs, :if => Proc.new { |p| p.depth <= 1 }
## named scopes ## ## named scopes ##
@ -50,10 +48,8 @@ module Locomotive
scope :root, :where => { :slug => 'index', :depth => 0 } scope :root, :where => { :slug => 'index', :depth => 0 }
scope :not_found, :where => { :slug => '404', :depth => 0 } scope :not_found, :where => { :slug => '404', :depth => 0 }
scope :published, :where => { :published => true } scope :published, :where => { :published => true }
scope :fullpath, lambda { |fullpath| { :where => { :fullpath => fullpath } } }
scope :handle, lambda { |handle| { :where => { :handle => handle } } } scope :handle, lambda { |handle| { :where => { :handle => handle } } }
scope :minimal_attributes, lambda { |attrs = []| { :only => (attrs || []) + %w(title slug fullpath position depth published templatized redirect listed response_type parent_id parent_ids site_id created_at updated_at) } } scope :minimal_attributes, lambda { |attrs = []| { :only => (attrs || []) + %w(title slug fullpath position depth published with_wildcards redirect listed wildcard response_type parent_id parent_ids site_id created_at updated_at) } }
scope :dependent_from, lambda { |id| { :where => { :template_dependencies.in => [id] } } }
## methods ## ## methods ##
@ -114,16 +110,6 @@ module Locomotive
self.raw_template ||= ::I18n.t('attributes.defaults.pages.other.body') self.raw_template ||= ::I18n.t('attributes.defaults.pages.other.body')
end end
def build_fullpath
if self.index? || self.not_found?
self.fullpath = self.slug
else
slugs = self.ancestors_and_self.map(&:slug)
slugs.shift unless slugs.size == 1
self.fullpath = File.join slugs.compact
end
end
def record_current_locale def record_current_locale
self.locales ||= [] self.locales ||= []
self.locales << ::Mongoid::Fields::I18n.locale self.locales << ::Mongoid::Fields::I18n.locale

View File

@ -64,20 +64,21 @@ module Locomotive
protected protected
# FIXME: Currently there is no t/translate method on the I18n module # FIXME: Currently there is no t/translate method on the
# Extensions::Site::I18n which is breaking the testing. The # Extensions::Site::I18n module which is breaking the testing. The
# namespaced ::I18n should be changed to just I18n when the t() # namespaced ::I18n should be replaced by simply I18n when the t()
# method is available # method will be available.
def create_default_pages! def create_default_pages!
::Mongoid::Fields::I18n.with_locale(self.default_locale) do %w(index 404).each do |slug|
%w{index 404}.each do |slug| page = self.pages.build(:title => '', :slug => '', :raw_template => '', :published => true)
self.pages.create({
:slug => slug, self.locales.each do |locale|
:title => ::I18n.t("attributes.defaults.pages.#{slug}.title"), page.attributes['slug'][locale] = slug
:raw_template => ::I18n.t("attributes.defaults.pages.#{slug}.body"), page.attributes['title'][locale] = ::I18n.t("attributes.defaults.pages.#{slug}.title", :locale => locale)
:published => true page.attributes['raw_template'][locale] = ::I18n.t("attributes.defaults.pages.#{slug}.body", :locale => locale)
})
end end
page.save
end end
end end

View File

@ -1,7 +1,7 @@
module Locomotive module Locomotive
class PagePresenter < BasePresenter class PagePresenter < BasePresenter
delegate :title, :slug, :fullpath, :handle, :raw_template, :published, :listed, :templatized, :templatized_from_parent, :redirect, :redirect_url, :template_changed, :cache_strategy, :response_type, :to => :source delegate :title, :slug, :fullpath, :handle, :raw_template, :published, :listed, :wildcard, :wildcards, :redirect, :redirect_url, :template_changed, :cache_strategy, :response_type, :to => :source
def escaped_raw_template def escaped_raw_template
h(self.source.raw_template) h(self.source.raw_template)
@ -12,7 +12,7 @@ module Locomotive
end end
def included_methods def included_methods
super + %w(title slug fullpath handle raw_template published listed templatized templatized_from_parent redirect redirect_url cache_strategy response_type template_changed editable_elements localized_fullpaths) super + %w(title slug fullpath handle raw_template published listed wildcard wildcards redirect redirect_url cache_strategy response_type template_changed editable_elements localized_fullpaths)
end end
def localized_fullpaths def localized_fullpaths

View File

@ -16,7 +16,7 @@
- if not @page.index? and not @page.not_found? - if not @page.index? and not @page.not_found?
= f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false = f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false
= f.input :slug, :required => false, :hint => @page.slug.blank? ? t('.empty_slug') : public_page_url(@page), :input_html => { :'data-url' => get_path_pages_url, :disabled => @page.index? || @page.not_found? }, :wrapper_html => { :style => "#{'display: none' if @page.templatized? && !@page.templatized_from_parent?};", :class => 'em-inline-hints' } = f.input :slug, :required => false, :hint => @page.slug.blank? ? t('.empty_slug') : public_page_url(@page), :input_html => { :'data-url' => get_path_pages_url, :disabled => @page.index? || @page.not_found? }, :class => 'em-inline-hints'
= f.inputs :name => :seo, :class => "inputs foldable #{'folded' if inputs_folded?(@page)}" do = f.inputs :name => :seo, :class => "inputs foldable #{'folded' if inputs_folded?(@page)}" do
@ -34,15 +34,13 @@
= f.input :response_type, :as => :select, :collection => options_for_page_response_type, :include_blank => false = f.input :response_type, :as => :select, :collection => options_for_page_response_type, :include_blank => false
= f.input :templatized, :as => :'Locomotive::Toggle', :style => "#{'display: none' if @page.redirect? || @page.templatized_from_parent?}" = f.input :wildcard, :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' if !@page.templatized? || @page.templatized_from_parent?}" }
= f.input :published, :as => :'Locomotive::Toggle' = f.input :published, :as => :'Locomotive::Toggle'
= f.input :listed, :as => :'Locomotive::Toggle' = f.input :listed, :as => :'Locomotive::Toggle', :wrapper_html => { :style => "#{'display: none' if @page.has_wildcards?}" }
= f.input :redirect, :as => :'Locomotive::Toggle', :wrapper_html => { :style => "#{'display: none' if @page.templatized? || !@page.default_response_type?}" } = f.input :redirect, :as => :'Locomotive::Toggle', :wrapper_html => { :style => "#{'display: none' if !@page.default_response_type? || @page.has_wildcards?}" }
= f.input :cache_strategy, :as => :select, :collection => options_for_page_cache_strategy, :include_blank => false, :wrapper_html => { :style => "#{'display: none' if @page.redirect?}" } = f.input :cache_strategy, :as => :select, :collection => options_for_page_cache_strategy, :include_blank => false, :wrapper_html => { :style => "#{'display: none' if @page.redirect?}" }
@ -50,4 +48,4 @@
= f.inputs :name => :raw_template, :class => "inputs foldable #{'folded' if inputs_folded?(@page)}" do = f.inputs :name => :raw_template, :class => "inputs foldable #{'folded' if inputs_folded?(@page)}" do
= f.input :raw_template, :as => :'Locomotive::Code' = f.input :raw_template, :as => :'Locomotive::Code'

View File

@ -3,7 +3,7 @@
%li{ :id => "item-#{page.id}", :class => "page #{css_for_page(page)}" } %li{ :id => "item-#{page.id}", :class => "page #{css_for_page(page)}" }
- children = can?(:manage, page) ? page.children : page.children.find_all { |p| !p.templatized? } - children = page.children
- with_children = !children.empty? - with_children = !children.empty?

View File

@ -8,14 +8,8 @@ xml.urlset "xmlns" => "http://www.sitemaps.org/schemas/sitemap/0.9" do
@pages.each do |page| @pages.each do |page|
if not page.index_or_not_found? if not page.index_or_not_found?
if page.templatized? if page.wildcard?
page.content_type.entries.visible.each do |c| # FIXME (Didier L.) unable for now to generate all the pages
xml.url do
xml.loc page_url(page, { :content => c, :host => true })
xml.lastmod c.updated_at.to_date.to_s('%Y-%m-%d')
xml.priority 0.9
end
end
else else
xml.url do xml.url do
xml.loc page_url(page, { :host => true }) xml.loc page_url(page, { :host => true })

View File

@ -1,2 +1,2 @@
Haml::Template.options[:format] = :html5 Haml::Template.options[:format] = :html5
Haml::Template.options[:ugly] = true # improve performance in dev Haml::Template.options[:ugly] = true # improve performance in dev

View File

@ -57,7 +57,7 @@ de:
title: Titel title: Titel
parent: Parent parent: Parent
slug: Slug slug: Slug
templatized: Templatized wildcard: Wildcard
published: Veröffentlicht published: Veröffentlicht
listed: Im Menü listed: Im Menü
redirect: Umleitung redirect: Umleitung

View File

@ -58,7 +58,7 @@ es:
parent_id: Padre parent_id: Padre
slug: Enlace slug: Enlace
listed: Menu listed: Menu
templatized: Templatizada wildcard: Wildcard
published: Publicada published: Publicada
redirect: Redirección redirect: Redirección
redirect_url: Url de redirección redirect_url: Url de redirección

View File

@ -60,7 +60,7 @@ fr:
parent_id: Dossier parent parent_id: Dossier parent
slug: Raccourci slug: Raccourci
listed: Menu listed: Menu
templatized: Templatisée wildcard: Wildcard
published: Publiée published: Publiée
redirect: Redirection redirect: Redirection
redirect_url: Url de redirection redirect_url: Url de redirection

View File

@ -64,7 +64,7 @@ it:
parent_id: Pagina superiore parent_id: Pagina superiore
slug: Slug slug: Slug
listed: In menù listed: In menù
templatized: Templatized wildcard: Wildcard
published: Pubblicata published: Pubblicata
redirect: Redirezione redirect: Redirezione
redirect_url: Url di redirezione redirect_url: Url di redirezione

View File

@ -221,7 +221,7 @@
parent: Tilhører parent: Tilhører
parent_id: Tilhører parent_id: Tilhører
slug: Slug slug: Slug
templatized: Templatized wildcard: Wildcard
published: Publisert published: Publisert
listed: I meny listed: I meny
redirect: Videresending redirect: Videresending

View File

@ -60,7 +60,7 @@ pt-BR:
title: Título title: Título
parent: Pai parent: Pai
slug: Slug slug: Slug
templatized: Template wildcard: Wildcard
published: Publicado published: Publicado
cache_strategy: Cache cache_strategy: Cache
content_type: content_type:

View File

@ -35,7 +35,7 @@ ru:
title: Имя title: Имя
parent: Родитель parent: Родитель
slug: Ссылка slug: Ссылка
templatized: Шаблонизирована wildcard: Wildcard
published: Опубликована published: Опубликована
listed: Меню listed: Меню
redirect: Перенаправлена redirect: Перенаправлена
@ -89,7 +89,7 @@ ru:
default: "%d.%m.%Y" default: "%d.%m.%Y"
short: "%d %b" short: "%d %b"
long: "%d %B %Y" long: "%d %B %Y"
# Названия дней недели -- контекстные и отдельностоящие # Названия дней недели -- контекстные и отдельностоящие
day_names: [воскресенье, понедельник, вторник, среда, четверг, пятница, суббота] day_names: [воскресенье, понедельник, вторник, среда, четверг, пятница, суббота]
standalone_day_names: [Воскресенье, Понедельник, Вторник, Среда, Четверг, Пятница, Суббота] standalone_day_names: [Воскресенье, Понедельник, Вторник, Среда, Четверг, Пятница, Суббота]
@ -97,19 +97,19 @@ ru:
# Названия месяцев -- сокращенные и полные, плюс отдельностоящие. # Названия месяцев -- сокращенные и полные, плюс отдельностоящие.
# Не забудьте nil в начале массиве (~) # Не забудьте nil в начале массиве (~)
# #
# #
# Don't forget the nil at the beginning; there's no such thing as a 0th month # Don't forget the nil at the beginning; there's no such thing as a 0th month
month_names: [~, января, февраля, марта, апреля, мая, июня, июля, августа, сентября, октября, ноября, декабря] month_names: [~, января, февраля, марта, апреля, мая, июня, июля, августа, сентября, октября, ноября, декабря]
standalone_month_names: [~, Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь] standalone_month_names: [~, Январь, Февраль, Март, Апрель, Май, Июнь, Июль, Август, Сентябрь, Октябрь, Ноябрь, Декабрь]
abbr_month_names: [~, янв., февр., марта, апр., мая, июня, июля, авг., сент., окт., нояб., дек.] abbr_month_names: [~, янв., февр., марта, апр., мая, июня, июля, авг., сент., окт., нояб., дек.]
standalone_abbr_month_names: [~, янв., февр., март, апр., май, июнь, июль, авг., сент., окт., нояб., дек.] standalone_abbr_month_names: [~, янв., февр., март, апр., май, июнь, июль, авг., сент., окт., нояб., дек.]
# Порядок компонентов даты для хелперов # Порядок компонентов даты для хелперов
# #
# #
# Used in date_select and datime_select. # Used in date_select and datime_select.
order: order:
- :day - :day
- :month - :month
- :year - :year
@ -120,7 +120,7 @@ ru:
default: "%a, %d %b %Y, %H:%M:%S %z" default: "%a, %d %b %Y, %H:%M:%S %z"
short: "%d %b, %H:%M" short: "%d %b, %H:%M"
long: "%d %B %Y, %H:%M" long: "%d %B %Y, %H:%M"
# am/pm решено перевести как "утра/вечера" :) # am/pm решено перевести как "утра/вечера" :)
am: "утра" am: "утра"
pm: "вечера" pm: "вечера"
@ -138,7 +138,7 @@ ru:
delimiter: " " delimiter: " "
# Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00) # Number of decimals, behind the separator (the number 1 with a precision of 2 gives: 1.00)
precision: 3 precision: 3
# Used in number_to_currency() # Used in number_to_currency()
currency: currency:
format: format:
@ -152,14 +152,14 @@ ru:
separator: "." separator: "."
delimiter: " " delimiter: " "
precision: 2 precision: 2
# Used in number_to_percentage() # Used in number_to_percentage()
percentage: percentage:
format: format:
# These three are to override number.format and are optional # These three are to override number.format and are optional
# separator: # separator:
delimiter: "" delimiter: ""
# Used in number_to_precision() # Used in number_to_precision()
precision: precision:
format: format:
@ -167,18 +167,18 @@ ru:
# separator: # separator:
delimiter: "" delimiter: ""
# precision: # precision:
# Used in number_to_human_size() # Used in number_to_human_size()
human: human:
format: format:
# These three are to override number.format and are optional # These three are to override number.format and are optional
# separator: # separator:
delimiter: "" delimiter: ""
precision: 1 precision: 1
# Rails 2.2 # Rails 2.2
# storage_units: [байт, КБ, МБ, ГБ, ТБ] # storage_units: [байт, КБ, МБ, ГБ, ТБ]
# Rails 2.3 # Rails 2.3
storage_units: storage_units:
# Storage units output formatting. # Storage units output formatting.
@ -272,7 +272,7 @@ ru:
# Rails 2.2 # Rails 2.2
sentence_connector: "и" sentence_connector: "и"
skip_last_comma: true skip_last_comma: true
# Rails 2.3 # Rails 2.3
words_connector: ", " words_connector: ", "
two_words_connector: " и " two_words_connector: " и "

View File

@ -55,9 +55,7 @@ de:
page: page:
published: "Nur authentifizierte Accounts können nicht publizierte Seiten ansehen." published: "Nur authentifizierte Accounts können nicht publizierte Seiten ansehen."
cache_strategy: "Cache die Seiten, um eine bessere Geschwindigkeit zu erzielen. Die 'Einfach' Option ist ein guter Kompromiss." cache_strategy: "Cache die Seiten, um eine bessere Geschwindigkeit zu erzielen. Die 'Einfach' Option ist ein guter Kompromiss."
templatized: "Nutze diese Seite als Vorlage für einen Baustein, den du erstellt hast."
listed: "Regele, ob die Seite in den generierten Menüs angezeigt werden soll." listed: "Regele, ob die Seite in den generierten Menüs angezeigt werden soll."
content_type_id: "Der Baustein für den diese Seite als Vorlage dienen soll."
seo_title: "Wähle einen Titel für den 'title' Tag im Seiten Header. Lass es leer, wenn der Seitentitel verwendet werden soll." seo_title: "Wähle einen Titel für den 'title' Tag im Seiten Header. Lass es leer, wenn der Seitentitel verwendet werden soll."
meta_keywords: "Meta-Schlagworte, die im HEAD-Bereich der Webseite genutzt werden. Die einzelnen Wörter werden durch eine Leertaste getrennt. Diese werden für die Suchmaschinen benötigt." meta_keywords: "Meta-Schlagworte, die im HEAD-Bereich der Webseite genutzt werden. Die einzelnen Wörter werden durch eine Leertaste getrennt. Diese werden für die Suchmaschinen benötigt."
meta_description: "Meta-Beschreibung, die im HEAD-Bereich der Webseite genutzt wird. Diese wird für die Suchmaschinen benötigt." meta_description: "Meta-Beschreibung, die im HEAD-Bereich der Webseite genutzt wird. Diese wird für die Suchmaschinen benötigt."

View File

@ -57,9 +57,8 @@ en:
handle: "Unique identifier to retrieve this page within an external controller instance" handle: "Unique identifier to retrieve this page within an external controller instance"
published: "Only authenticated accounts can view unpublished pages." published: "Only authenticated accounts can view unpublished pages."
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." wildcard: "Make the page have a wildcard segment in its path. Then the slug will be used as the name of the wildcard in the template"
listed: "Control whether to show the page from generated menus." listed: "Control whether to show the page from generated menus."
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."
@ -84,7 +83,7 @@ en:
source: "The current file is available here %{url}" source: "The current file is available here %{url}"
content_entry: content_entry:
_slug: "Property used to generate the url of a page working as a template for this content type (ex: \"template_page/{{ your_object._permalink }})\"." _slug: "Property used to generate the url of a page working as a template for this content type (ex: \"template_page/{{ your_object._permalink }})\"."
seo_title: "The value you fill in will replace the SEO title of the templatized page related to your model." seo_title: "The value you fill in can be used to replace the SEO title in any pages."
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."
import: import:

View File

@ -49,9 +49,7 @@ es:
page: page:
published: "Las páginas no publicadas son únicamente visibles tras iniciar sesión." published: "Las páginas no publicadas son únicamente visibles tras iniciar sesión."
cache_strategy: "Utilizar una caché de página para un mejor rendimiento. La opción 'Simple' es un buen compromiso." cache_strategy: "Utilizar una caché de página para un mejor rendimiento. La opción 'Simple' es un buen compromiso."
templatized: "Usar la página como plantilla de un modelo."
listed: "Controla si la página se enseña o no en los menús." listed: "Controla si la página se enseña o no en los menús."
content_type_id: "El modelo del cual será una plantilla esta página."
meta_keywords: "Reescribir las palabras clave SEO utilizadas en la cabecera de la página. Deben estar separadas por comas." meta_keywords: "Reescribir las palabras clave SEO utilizadas en la cabecera de la página. Deben estar separadas por comas."
meta_description: "Reescribir la metadescripción que se utiliza en la cabecera de la página." meta_description: "Reescribir la metadescripción que se utiliza en la cabecera de la página."
snippet: snippet:

View File

@ -53,9 +53,8 @@ fr:
page: page:
published: "Seuls les administrateurs authentifiés peuvent voir une page non publiée." published: "Seuls les administrateurs authentifiés peuvent voir une page non publiée."
cache_strategy: "Cache la page pour de meilleure performance. L'option 'Simple' est le meilleur compromis." cache_strategy: "Cache la page pour de meilleure performance. L'option 'Simple' est le meilleur compromis."
templatized: "Utilise la page comme un template pour un modèle défini." wildcard: "Détermine si la page aura la dernière partie de son URL dynamique. Si oui, le slug sera utilisé comme nom du segment dans le template."
listed: "Controle si la page doit être visible depuis les menus automatiquement générés." listed: "Controle si la page doit être visible depuis les menus automatiquement générés."
target_klass_name: "Le type du contenu pour lequel cette page est un template."
seo_title: "Définit un titre de page à mettre dans la balise TITLE de la page. Laissez le blanc pour utiliser la valeur par défaut (voir configuration du site)." seo_title: "Définit un titre de page à mettre dans la balise TITLE de la page. Laissez le blanc pour utiliser la valeur par défaut (voir configuration du site)."
meta_keywords: "Redéfinit les mots-clés du site. Utilisés à l'intérieur de la balise HEAD. Ils sont séparés par une virgule." meta_keywords: "Redéfinit les mots-clés du site. Utilisés à l'intérieur de la balise HEAD. Ils sont séparés par une virgule."
meta_description: "Redéfinit la description du site. Utilisée à l'intérieur de la balise HEAD." meta_description: "Redéfinit la description du site. Utilisée à l'intérieur de la balise HEAD."

View File

@ -55,9 +55,7 @@ it:
page: page:
published: "Solo gli account autenticati possono visualizzare le pagine non pubblicate." published: "Solo gli account autenticati possono visualizzare le pagine non pubblicate."
cache_strategy: "Attiva il cache della pagina per miglirare le prestazioni. L'opzione 'Semplice' è un buon compromesso." cache_strategy: "Attiva il cache della pagina per miglirare le prestazioni. L'opzione 'Semplice' è un buon compromesso."
templatized: "Utilizzare la pagina come template per un modello che hai definito."
listed: "Determina se la pagina deve essere mostrata nei menù generati." listed: "Determina se la pagina deve essere mostrata nei menù generati."
content_type_id: "Il tipo di contenuto di cui questa pagina sarà il template."
seo_title: "Definisce il titolo della pagina inserito nel tag title della sezione head. Lascia vuoto se vuoi che venga utilizzato il titolo definito per il sito." seo_title: "Definisce il titolo della pagina inserito nel tag title della sezione head. Lascia vuoto se vuoi che venga utilizzato il titolo definito per il sito."
meta_keywords: "Sovrascrive per questa pagina le meta keywords definite per il sito all'interno del tag head della pagina. Sono separate da virgola." meta_keywords: "Sovrascrive per questa pagina le meta keywords definite per il sito all'interno del tag head della pagina. Sono separate da virgola."
meta_description: "Sovrascrive per questa pagina la meta description definita per il sito." meta_description: "Sovrascrive per questa pagina la meta description definita per il sito."

View File

@ -49,9 +49,7 @@ nl:
page: page:
published: "Alleen geauthenticeerde accounts kunnen niet gepubliceerde pagina's zien." published: "Alleen geauthenticeerde accounts kunnen niet gepubliceerde pagina's zien."
cache_strategy: "Cache de pagina voor betere prestaties. De 'Simpele' keus is een goed compromis." cache_strategy: "Cache de pagina voor betere prestaties. De 'Simpele' keus is een goed compromis."
templatized: "Gebruik de pagina als template voor een door u gedefinieerd model."
listed: "Controleer of de pagina getoond wordt van de gegenereerde menu's." listed: "Controleer of de pagina getoond wordt van de gegenereerde menu's."
content_type_id: "Het type inhoud waar deze pagina als template voor dient"
meta_keywords: "Overschrijft de meta keywords van de website in de head tag van deze pagina. Gescheiden door een komma" meta_keywords: "Overschrijft de meta keywords van de website in de head tag van deze pagina. Gescheiden door een komma"
meta_description: "Overschrijft de meta description van de website in de head tag van deze pagina." meta_description: "Overschrijft de meta description van de website in de head tag van deze pagina."
snippet: snippet:

View File

@ -55,9 +55,7 @@
page: page:
published: "Kun autoriserte kontoer kan se ikke-publiserte sider" published: "Kun autoriserte kontoer kan se ikke-publiserte sider"
cache_strategy: "Buffre siden for å bedre ytelsen. \"Enkel\" er et bra kompromiss." cache_strategy: "Buffre siden for å bedre ytelsen. \"Enkel\" er et bra kompromiss."
templatized: "Bruk denne siden som mal for en modell."
listed: "Styr om siden skal vises i de genererte menyene." listed: "Styr om siden skal vises i de genererte menyene."
content_type_id: "Innholdstypen denne siden skal være mal for."
seo_title: "Definer en egen tittel for siden. Denne blir benyttet av nettleseren. Hvis denne står tom blir standardverdien fra sideinnstillingene benyttet." seo_title: "Definer en egen tittel for siden. Denne blir benyttet av nettleseren. Hvis denne står tom blir standardverdien fra sideinnstillingene benyttet."
meta_keywords: "Overstyr søkemotor-metadata for denne siden. Separer med komma." meta_keywords: "Overstyr søkemotor-metadata for denne siden. Separer med komma."
meta_description: "Overstyr søkemotorbeskrivelsen for denne siden." meta_description: "Overstyr søkemotorbeskrivelsen for denne siden."

View File

@ -46,7 +46,6 @@ pt-BR:
page: page:
published: "Apenas contas autenticadas podem ver páginas não publicadas." published: "Apenas contas autenticadas podem ver páginas não publicadas."
cache_strategy: 'Faça o Cache da página para obter melhor desempenho. A escolha do "Simples" é um bom compromisso' cache_strategy: 'Faça o Cache da página para obter melhor desempenho. A escolha do "Simples" é um bom compromisso'
templatized: "Utilize a página como template para o modelo definido."
snippet: snippet:
slug: "Você precisa saber a ordem para inserir fragmentos dentro da página." slug: "Você precisa saber a ordem para inserir fragmentos dentro da página."
site: site:

View File

@ -57,7 +57,6 @@ ru:
handle: "Уникальный идентификатор для поиска этой страницы из экземпляра внешнего rails-контроллера" handle: "Уникальный идентификатор для поиска этой страницы из экземпляра внешнего rails-контроллера"
published: "Только аутентифицированным пользователям разрешается просматривать неопубликованные страницы." published: "Только аутентифицированным пользователям разрешается просматривать неопубликованные страницы."
cache_strategy: "Кэшировать страницу для лучшей производительности. Вариант 'Простое' является хорошим компромиссом." cache_strategy: "Кэшировать страницу для лучшей производительности. Вариант 'Простое' является хорошим компромиссом."
templatized: "Используйте страницу в качестве шаблона для определенной вами модели."
listed: "Контролируйте возможность показа страницы из сгенерированных меню." listed: "Контролируйте возможность показа страницы из сгенерированных меню."
target_klass_name: "Тип контента (модели), для которого эта страница будет выступать в качестве шаблона." target_klass_name: "Тип контента (модели), для которого эта страница будет выступать в качестве шаблона."
seo_title: "Определите заголовок страницы, который будет использоваться как значение тэга title в секции head. Оставьте пустым, если вы хотите использовать значение по умолчанию из настроек сайта." seo_title: "Определите заголовок страницы, который будет использоваться как значение тэга title в секции head. Оставьте пустым, если вы хотите использовать значение по умолчанию из настроек сайта."

View File

@ -1,5 +1,14 @@
BOARD: BOARD:
x page with regexp url => wildcards
- with_scope allowed for the find tag
- nested relationships (find tag)
- script to migrate from templatized to wildcard
- seo title (content_entry should be available)
- write more tests
- find_spec
- features
- clean code
x namespace assets x namespace assets
x bugs: x bugs:
x toggler x toggler
@ -421,4 +430,4 @@ x export: problems with templatized pages (source => multi levels pages)
x do not rename files for fonts x do not rename files for fonts
x icon for redirection page in the pages section (back-office) x icon for redirection page in the pages section (back-office)
x installed on heroku with source x installed on heroku with source
x test and/or convert existing templates (the 2 of the themes section) x test and/or convert existing templates (the 2 of the themes section)

View File

@ -45,9 +45,6 @@ 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

@ -24,10 +24,10 @@ module Locomotive
end end
def public_page_url(page, options = {}) def public_page_url(page, options = {})
if content = options.delete(:content) if contents = options.delete(:contents)
File.join(current_site_public_url, page.fullpath.gsub('content_type_template', ''), content._slug) File.join(current_site_public_url, page.compiled_fullpath(contents))
else else
File.join(current_site_public_url, page.fullpath) File.join(current_site_public_url, page.pretty_fullpath)
end end
end end

View File

@ -10,4 +10,4 @@ module Formtastic
end end
end end
end end
end end

View File

@ -1,10 +1,10 @@
require 'haml/helpers/action_view_mods' require 'haml/helpers/action_view_mods'
# Only preserve whitespace in the tag's content: https://github.com/nex3/haml/pull/503
module ActionView module ActionView
module Helpers module Helpers #:nodoc:
module TagHelper module TagHelper
# Only preserve whitespace in the tag's content: https://github.com/nex3/haml/pull/503
def content_tag_with_haml_and_preserve(name, content_or_options_with_block = nil, *args, &block) def content_tag_with_haml_and_preserve(name, content_or_options_with_block = nil, *args, &block)
return content_tag_without_haml(name, content_or_options_with_block, *args, &block) unless is_haml? return content_tag_without_haml(name, content_or_options_with_block, *args, &block) unless is_haml?
@ -31,4 +31,4 @@ module ActionView
alias_method :content_tag_with_haml, :content_tag_with_haml_and_preserve alias_method :content_tag_with_haml, :content_tag_with_haml_and_preserve
end end
end end
end end

View File

@ -3,14 +3,18 @@ module Locomotive
module Drops module Drops
class Page < Base class Page < Base
delegate :seo_title, :meta_keywords, :meta_description, :to => '_source' delegate :title, :seo_title, :meta_keywords, :meta_description, :to => '_source'
def title def title
self._source.templatized? ? @context['entry']._label : self._source.title # TODO
# self._source.templatized? ? @context['entry']._label : self._source.title
self._source.title
end end
def slug def slug
self._source.templatized? ? self._source.content_type.slug.singularize : self._source.slug # TODO
# self._source.templatized? ? self._source.content_type.slug.singularize : self._source.slug
self._source.slug
end end
def parent def parent

View File

@ -0,0 +1,46 @@
module Locomotive
module Liquid
module Tags
class Find < ::Liquid::Tag
Syntax = /(#{::Liquid::Expression}+)?/
def initialize(tag_name, markup, tokens, context)
if markup =~ Syntax
@options = markup.scan(::Liquid::QuotedFragment)
syntax_error! if @options.size != 2
else
syntax_error!
end
super
end
def render(context)
Rails.logger.debug "\n\n =>>>>>>> #{@options.inspect} / #{context['params'].inspect}"
permalink = context[@options.last]
site = context.registers[:site]
type = site.content_types.where(:slug => @options.first).first
entry = type.klass_with_custom_fields(:entries).find_by_permalink(permalink)
entry_name = @options.first.singularize
context.scopes.last['content_entry'] = entry
context.scopes.last[entry_name] = entry
''
end
protected
def syntax_error!
raise ::Liquid::SyntaxError.new("Syntax Error in 'find' - Valid syntax: find <content_type>, <permalink>")
end
end
::Liquid::Template.register_tag('find', Find)
end
end
end

View File

@ -1,5 +1,5 @@
module Liquid module Locomotive
module Locomotive module Liquid
module Tags module Tags
class GoogleAnalytics < ::Liquid::Tag class GoogleAnalytics < ::Liquid::Tag

View File

@ -1,5 +1,5 @@
module Liquid module Locomotive
module Locomotive module Liquid
module Tags module Tags
class InlineEditor < ::Liquid::Tag class InlineEditor < ::Liquid::Tag

View File

@ -42,11 +42,14 @@ module Locomotive
output += @site.locales.collect do |locale| output += @site.locales.collect do |locale|
::Mongoid::Fields::I18n.with_locale(locale) do ::Mongoid::Fields::I18n.with_locale(locale) do
fullpath = @site.localized_page_fullpath(@page, locale) # fullpath = @site.localized_page_fullpath(@page, locale)
if @page.templatized? # if @page.templatized?
fullpath.gsub!('content_type_template', context['entry']._permalink) # fullpath.gsub!('content_type_template', context['entry']._permalink)
end # end
# TODO: TO BE TESTED
fullpath = @page.compiled_fullpath(@page.wildcards_hash)
%(<a href="/#{fullpath}" class="#{locale} #{'current' if locale == context['current_locale']}">#{link_label(locale)}</a>) %(<a href="/#{fullpath}" class="#{locale} #{'current' if locale == context['current_locale']}">#{link_label(locale)}</a>)
end end

View File

@ -34,7 +34,7 @@ module Locomotive
end end
else else
raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <page|site> <options>") raise ::Liquid::SyntaxError.new("Syntax Error in 'nav' - Valid syntax: nav <page|site>, <options>")
end end
super super
@ -134,7 +134,7 @@ module Locomotive
# Determines whether or not a page should be a part of the menu # Determines whether or not a page should be a part of the menu
def include_page?(page) def include_page?(page)
if !page.listed? || page.templatized? || !page.published? if !page.listed? || page.has_wildcards? || !page.published?
false false
elsif @options[:exclude] elsif @options[:exclude]
(page.fullpath =~ @options[:exclude]).nil? (page.fullpath =~ @options[:exclude]).nil?

View File

@ -59,3 +59,5 @@ module Mongoid#:nodoc:
end end
end end

View File

@ -63,10 +63,7 @@ module Locomotive
assigns.merge!(flash.to_hash.stringify_keys) # data from public submissions assigns.merge!(flash.to_hash.stringify_keys) # data from public submissions
if @page.templatized? # add instance from content type assigns['params'].merge!(@page.wildcards_hash.stringify_keys) if @page.has_wildcards?
assigns['entry'] = @page.content_entry
assigns[@page.target_entry_name] = @page.content_entry # just here to help to write readable liquid code
end
registers = { registers = {
:controller => self, :controller => self,

View File

@ -5,6 +5,17 @@ rescue LoadError
puts 'You must `gem install bundler` and `bundle install` to run rake tasks' puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
end end
# Locomotive::Page.each do |page|
# page.editable_elements.each_with_index do |el, index|
# next if el._type != 'Locomotive::EditableFile' || el.attributes['source'].is_a?(Hash)
#
# value = el.attributes['source']
#
# page.collection.update({ '_id' => page._id }, { '$set' => { "editable_elements.#{index}.source" => { 'en' => value } } })
# end
# end
# ================ GLOBAL VARIABLES ============== # ================ GLOBAL VARIABLES ==============
$database_name = 'locomotive_engine_dev' $database_name = 'locomotive_engine_dev'

View File

@ -1,2 +1,2 @@
Haml::Template.options[:format] = :html5 Haml::Template.options[:format] = :html5
Haml::Template.options[:ugly] = true # improve performance in dev Haml::Template.options[:ugly] = true # improve performance in dev

View File

@ -56,9 +56,6 @@ 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,

View File

@ -71,13 +71,14 @@ describe Locomotive::Liquid::Drops::Page do
render_template('{{ home.title }}').should == 'Home page' render_template('{{ home.title }}').should == 'Home page'
end end
it 'renders the content instance highlighted field instead for a templatized page' do # TODO
templatized = FactoryGirl.build(:page, :title => 'Lorem ipsum template', :templatized => true) # it 'renders the content instance highlighted field instead for a templatized page' do
# templatized = FactoryGirl.build(:page, :title => 'Lorem ipsum template', :templatized => true)
entry = Locomotive::Liquid::Drops::ContentEntry.new(mock('entry', :_label => 'Locomotive rocks !')) #
# entry = Locomotive::Liquid::Drops::ContentEntry.new(mock('entry', :_label => 'Locomotive rocks !'))
render_template('{{ page.title }}', 'page' => templatized, 'entry' => entry).should == 'Locomotive rocks !' #
end # render_template('{{ page.title }}', 'page' => templatized, 'entry' => entry).should == 'Locomotive rocks !'
# end
end end

View File

@ -0,0 +1,52 @@
require 'spec_helper'
describe Locomotive::Liquid::Tags::Find do
it 'is valid if the 2 arguments are present' do
lambda do
Locomotive::Liquid::Tags::Find.new('find', 'projects, params.permalink', [], {})
end.should_not raise_error
end
it 'is not valid if one of the 2 arguments is missing' do
lambda do
Locomotive::Liquid::Tags::Find.new('find', 'projects', [], {})
end.should raise_error
end
# it 'tests' do
# tag = Locomotive::Liquid::Tags::Find.new('find', 'projects, params.permalink', [], {})
# puts tag.inspect
# # attributes = scope.send(:decode, scope.instance_variable_get(:@attributes), ::Liquid::Context.new)
# # attributes['active'].should == true
# # attributes['price'].should == 42
# # attributes['title'].should == 'foo'
# # attributes['hidden'].should == false
# end
# it 'decodes more complex options' do
# scope = Locomotive::Liquid::Tags::WithScope.new('with_scope', 'price.gt:42.0 price.lt:50', ["{% endwith_scope %}"], {})
# attributes = scope.send(:decode, scope.instance_variable_get(:@attributes), ::Liquid::Context.new)
# attributes['price.gt'].should == 42.0
# attributes['price.lt'].should == 50
# end
#
# it 'decodes context variable' do
# scope = Locomotive::Liquid::Tags::WithScope.new('with_scope', 'category: params.type', ["{% endwith_scope %}"], {})
# attributes = scope.send(:decode, scope.instance_variable_get(:@attributes), ::Liquid::Context.new({ 'params' => { 'type' => 'posts' } }))
# attributes['category'].should == 'posts'
# end
#
# it 'allows order_by option' do
# scope = Locomotive::Liquid::Tags::WithScope.new('with_scope', 'order_by:\'name DESC\'', ["{% endwith_scope %}"], {})
# attributes = scope.send(:decode, scope.instance_variable_get(:@attributes), ::Liquid::Context.new)
# attributes['order_by'].should == 'name DESC'
# end
#
# it 'stores attributes in the context' do
# template = ::Liquid::Template.parse("{% with_scope active:true title:'foo' %}{{ with_scope.active }}-{{ with_scope.title }}{% endwith_scope %}")
# text = template.render
# text.should == "true-foo"
# end
end

View File

@ -15,7 +15,7 @@ describe Locomotive::Liquid::Tags::Nav do
Locomotive::Page.new(:title => 'Child #2.1', :fullpath => 'child_2/sub_child_1', :slug => 'sub_child_1', :published => true), Locomotive::Page.new(:title => 'Child #2.1', :fullpath => 'child_2/sub_child_1', :slug => 'sub_child_1', :published => true),
Locomotive::Page.new(:title => 'Child #2.2', :fullpath => 'child_2/sub_child_2', :slug => 'sub_child_2', :published => true), Locomotive::Page.new(:title => 'Child #2.2', :fullpath => 'child_2/sub_child_2', :slug => 'sub_child_2', :published => true),
Locomotive::Page.new(:title => 'Unpublished #2.2', :fullpath => 'child_2/sub_child_unpublishd_2', :slug => 'sub_child_unpublished_2', :published => false), Locomotive::Page.new(:title => 'Unpublished #2.2', :fullpath => 'child_2/sub_child_unpublishd_2', :slug => 'sub_child_unpublished_2', :published => false),
Locomotive::Page.new(:title => 'Templatized #2.3', :fullpath => 'child_2/sub_child_template_3', :slug => 'sub_child_template_3', :published => true, :templatized => true), Locomotive::Page.new(:title => 'Templatized #2.3', :fullpath => 'child_2/sub_child_template_3', :slug => 'sub_child_template_3', :published => true, :wildcard => true),
Locomotive::Page.new(:title => 'Unlisted #2.4', :fullpath => 'child_2/sub_child_unlisted_4', :slug => 'sub_child_unlisted_4', :published => true, :listed => false) Locomotive::Page.new(:title => 'Unlisted #2.4', :fullpath => 'child_2/sub_child_unlisted_4', :slug => 'sub_child_unlisted_4', :published => true, :listed => false)
] ]
@home.children.last.stubs(:children_with_minimal_attributes).returns(other_children) @home.children.last.stubs(:children_with_minimal_attributes).returns(other_children)
@ -55,7 +55,7 @@ describe Locomotive::Liquid::Tags::Nav do
output.should match /<\/a><\/li><\/ul><\/li><\/ul>/ output.should match /<\/a><\/li><\/ul><\/li><\/ul>/
end end
it 'does not render templatized pages' do it 'does not render pages with wildcards' do
output = render_nav('site', {}, 'depth: 2') output = render_nav('site', {}, 'depth: 2')
output.should_not match /sub-child-template-3/ output.should_not match /sub-child-template-3/

View File

@ -81,6 +81,10 @@ describe 'Locomotive rendering system' do
context 'when retrieving page' do context 'when retrieving page' do
before(:each) do
@page.fullpath = 'index' # we do not care if the fullpath is consistent or not.
end
it 'should retrieve the index page /' do it 'should retrieve the index page /' do
@controller.request.fullpath = '/' @controller.request.fullpath = '/'
@controller.current_site.pages.expects(:where).with(:depth => 0, :fullpath.in => %w{index}).returns([@page]) @controller.current_site.pages.expects(:where).with(:depth => 0, :fullpath.in => %w{index}).returns([@page])
@ -95,13 +99,13 @@ describe 'Locomotive rendering system' do
it 'should retrieve it based on the full path' do it 'should retrieve it based on the full path' do
@controller.request.fullpath = '/about_us/team.html' @controller.request.fullpath = '/about_us/team.html'
@controller.current_site.pages.expects(:where).with(:depth => 2, :fullpath.in => %w{about_us/team about_us/content_type_template content_type_template/team}).returns([@page]) @controller.current_site.pages.expects(:where).with(:depth => 2, :fullpath.in => %w{about_us/team about_us/* */team */*}).returns([@page])
@controller.send(:locomotive_page).should_not be_nil @controller.send(:locomotive_page).should_not be_nil
end end
it 'does not include the query string' do it 'does not include the query string' do
@controller.request.fullpath = '/about_us/team.html?some=params&we=use' @controller.request.fullpath = '/about_us/team.html?some=params&we=use'
@controller.current_site.pages.expects(:where).with(:depth => 2, :fullpath.in => %w{about_us/team about_us/content_type_template content_type_template/team}).returns([@page]) @controller.current_site.pages.expects(:where).with(:depth => 2, :fullpath.in => %w{about_us/team about_us/* */team */*}).returns([@page])
@controller.send(:locomotive_page).should_not be_nil @controller.send(:locomotive_page).should_not be_nil
end end
@ -118,7 +122,7 @@ describe 'Locomotive rendering system' do
@page.redirect = true @page.redirect = true
@page.redirect_url = 'http://www.example.com/' @page.redirect_url = 'http://www.example.com/'
@controller.request.fullpath = '/contact' @controller.request.fullpath = '/contact'
@controller.current_site.pages.expects(:where).with(:depth => 1, :fullpath.in => %w{contact content_type_template}).returns([@page]) @controller.current_site.pages.expects(:where).with(:depth => 1, :fullpath.in => %w{contact *}).returns([@page])
end end
it 'redirects to the redirect_url' do it 'redirects to the redirect_url' do
@ -128,37 +132,22 @@ describe 'Locomotive rendering system' do
end end
context 'templatized page' do context 'wildcards page' do
before(:each) do before(:each) do
@content_type = FactoryGirl.build(:content_type, :site => nil) @page.attributes = { 'wildcards' => %w(permalink), 'fullpath' => 'projects/*', 'wildcard' => true }
@content_entry = @content_type.entries.build(:_visible => true)
@page.templatized = true
@page.stubs(:fetch_target_entry).returns(@content_entry)
@page.stubs(:fullpath).returns('/projects/content_type_template')
@controller.request.fullpath = '/projects/edeneo.html' @controller.request.fullpath = '/projects/edeneo.html'
@controller.current_site.pages.expects(:where).with(:depth => 2, :fullpath.in => %w{projects/edeneo projects/content_type_template content_type_template/edeneo}).returns([@page]) @controller.current_site.pages.expects(:where).with(:depth => 2, :fullpath.in => %w{projects/edeneo projects/* */edeneo */*}).returns([@page])
end end
it 'sets the content_entry variable' do it 'finds the page' do
page = @controller.send(:locomotive_page) page = @controller.send(:locomotive_page)
page.should_not be_nil page.should_not be_nil
page.content_entry.should == @content_entry
end end
it 'returns the 404 page if the instance does not exist' do it 'assigns values to wildcards' do
@page.stubs(:fetch_target_entry).returns(nil) page = @controller.send(:locomotive_page)
(klass = Locomotive::Page).expects(:published).returns([true]) page.wildcards_hash.should == { 'permalink' => 'edeneo' }
@controller.current_site.pages.expects(:not_found).returns(klass)
@controller.send(:locomotive_page).should be_true
end
it 'returns the 404 page if the instance is not visible' do
@content_entry._visible = false
@page.stubs(:fetch_target_entry).returns(@content_entry)
(klass = Locomotive::Page).expects(:published).returns([true])
@controller.current_site.pages.expects(:not_found).returns(klass)
@controller.send(:locomotive_page).should be_true
end end
end end
@ -172,7 +161,7 @@ describe 'Locomotive rendering system' do
it 'should return the 404 page if the page has not been published yet' do it 'should return the 404 page if the page has not been published yet' do
@controller.request.fullpath = '/contact' @controller.request.fullpath = '/contact'
@controller.current_site.pages.expects(:where).with(:depth => 1, :fullpath.in => %w{contact content_type_template}).returns([@page]) @controller.current_site.pages.expects(:where).with(:depth => 1, :fullpath.in => %w{contact *}).returns([@page])
(klass = Locomotive::Page).expects(:published).returns([true]) (klass = Locomotive::Page).expects(:published).returns([true])
@controller.current_site.pages.expects(:not_found).returns(klass) @controller.current_site.pages.expects(:not_found).returns(klass)
@controller.send(:locomotive_page).should be_true @controller.send(:locomotive_page).should be_true
@ -181,7 +170,7 @@ describe 'Locomotive rendering system' do
it 'should not return the 404 page if the page has not been published yet and admin is logged in' do it 'should not return the 404 page if the page has not been published yet and admin is logged in' do
@controller.current_locomotive_account = true @controller.current_locomotive_account = true
@controller.request.fullpath = '/contact' @controller.request.fullpath = '/contact'
@controller.current_site.pages.expects(:where).with(:depth => 1, :fullpath.in => %w{contact content_type_template}).returns([@page]) @controller.current_site.pages.expects(:where).with(:depth => 1, :fullpath.in => %w{contact *}).returns([@page])
@controller.send(:locomotive_page).should == @page @controller.send(:locomotive_page).should == @page
end end

View File

@ -5,7 +5,7 @@ describe Locomotive::EditableShortText do
describe 'a simple case' do describe 'a simple case' do
before(:each) do before(:each) do
@site = FactoryGirl.create(:site) @site = FactoryGirl.create(:site, :locales => %w(en fr))
@home = @site.pages.root.first @home = @site.pages.root.first
@home.update_attributes :raw_template => "{% block body %}{% editable_short_text 'body' %}Lorem ipsum{% endeditable_short_text %}{% endblock %}" @home.update_attributes :raw_template => "{% block body %}{% editable_short_text 'body' %}Lorem ipsum{% endeditable_short_text %}{% endblock %}"

View File

@ -0,0 +1,179 @@
# coding: utf-8
require 'spec_helper'
describe Locomotive::Page do
before(:each) do
Locomotive::Site.any_instance.stubs(:create_default_pages!).returns(true)
Locomotive::Page.any_instance.stubs(:set_default_raw_template).returns(true)
end
describe 'wildcards' do
before(:each) do
@page = FactoryGirl.build(:page, :parent => FactoryGirl.build(:page), :title => 'Project template', :slug => 'permalink', :wildcard => true)
end
it 'has a fullpath with wildcards' do
@page.wildcard?.should be_true
end
it 'returns a nice output of the fullpath' do
@page.fullpath = 'archives/*/projects/*'
@page.wildcards = %w(month permalink)
@page.pretty_fullpath.should == 'archives/:month/projects/:permalink'
end
it 'compiles a fullpath with wildcards' do
@page.fullpath = 'archives/*/projects/*'
@page.wildcards = %w(month permalink)
@page.compiled_fullpath('month' => 'june', 'permalink' => 'hello-world').should == 'archives/june/projects/hello-world'
end
describe 'building the fullpath' do
it 'returns "index" for the root page' do
@page = FactoryGirl.build(:page)
@page.send(:build_fullpath)
@page.fullpath.should == 'index'
end
it 'returns "404" for the "page not found" page' do
@page = FactoryGirl.build(:page, :slug => '404')
@page.send(:build_fullpath)
@page.fullpath.should == '404'
end
it 'includes a single "*" if the page enables wildcards' do
@page.send(:build_fullpath)
@page.fullpath.should == '*'
end
it 'includes a single "*" if the page enables wildcards and if there are a lot of ancestors' do
@page.stubs(:parent).returns(FactoryGirl.build(:page, :fullpath => 'archives/projects'))
@page.send(:build_fullpath)
@page.fullpath.should == 'archives/projects/*'
end
it 'includes many "*" when there are ancestors enabling wildcards' do
@page.stubs(:parent).returns(FactoryGirl.build(:page, :fullpath => 'archives/*/projects'))
@page.send(:build_fullpath)
@page.fullpath.should == 'archives/*/projects/*'
end
end
describe 'adding wildcards' do
it 'fills the array of wildcards' do
@page.valid?
@page.wildcards.should == %w(permalink)
end
it 'fills the array of wildcards from ancestors' do
@page.parent.stubs(:has_wildcards?).returns(true)
@page.parent.wildcards = %w(category month)
@page.valid?
@page.wildcards.should == %w(category month permalink)
end
it 'contains a array of valid wildcards' do
@page.slug = 'another permalink'
@page.valid?
@page.wildcards.should == %w(another-permalink)
end
end
describe 'propagating changes' do
before(:each) do
@home_page = FactoryGirl.create(:page)
@archives_page = FactoryGirl.create(:page, :site => @home_page.site, :parent => @home_page, :slug => 'archives')
@month_page = FactoryGirl.create(:page, :site => @home_page.site, :parent => @archives_page, :slug => 'month')
@projects_page = FactoryGirl.create(:page, :site => @home_page.site, :parent => @month_page, :slug => 'projects')
@project_page = FactoryGirl.create(:page, :site => @home_page.site, :parent => @projects_page, :slug => 'project', :wildcard => true)
@posts_page = FactoryGirl.create(:page, :site => @home_page.site, :parent => @month_page, :slug => 'posts')
[@home_page, @archives_page, @month_page, @projects_page, @project_page, @posts_page].map(&:reload)
end
it 'keeps the wildcards as they were if we modify a slug of an ancestor' do
@archives_page.update_attributes :slug => 'my_archives'
@project_page.reload
@project_page.fullpath.should == 'my_archives/month/projects/*'
end
it 'modifies a wildcard' do
@month_page.update_attributes :wildcard => true
@month_page.reload
@month_page.update_attributes :slug => 'a_month'
@project_page.reload
@project_page.fullpath.should == 'archives/*/projects/*'
@project_page.wildcards.should == %w(a_month project)
end
it 'turns a page into a wildcards one' do
@month_page.update_attributes :wildcard => true
@project_page.reload
@project_page.fullpath.should == 'archives/*/projects/*'
@posts_page.reload
@posts_page.fullpath.should == 'archives/*/posts'
end
it 'turns off the wildcard property of page' do
@month_page.update_attributes :wildcard => true
@month_page.reload
@month_page.update_attributes :wildcard => false
@project_page.reload
@project_page.fullpath.should == 'archives/month/projects/*'
@project_page.wildcards.should == %w(project)
@posts_page.reload
@posts_page.fullpath.should == 'archives/month/posts'
@posts_page.wildcards.should == []
end
end
describe 'building the hash map asssociating a wildcard name with its value from a path' do
it 'returns an empty map for non wildcards fullpath' do
@page.fullpath = 'index'
@page.wildcards = nil
@page.match_wildcards('index').should be_empty
end
it 'underscores the wildcard name in the returned hash map' do
@page.fullpath = 'projects/*'
@page.wildcards = %w(my-permalink)
@page.match_wildcards('projects/hello-world').should == { 'my_permalink' => 'hello-world' }
end
it 'returns a map with one element if the fullpath contains a single wildcard' do
@page.fullpath = 'projects/*'
@page.wildcards = %w(permalink)
@page.match_wildcards('projects/hello-world').should == { 'permalink' => 'hello-world' }
end
it 'returns a map with as many elements as there are wildcards in the fullpath' do
@page.fullpath = 'archives/*/projects/*'
@page.wildcards = %w(month permalink)
@page.match_wildcards('archives/june/projects/hello-world').should == {
'month' => 'june',
'permalink' => 'hello-world'
}
end
it 'stores the map inside a virtual attribute' do
@page.fullpath = 'projects/*'
@page.wildcards = %w(permalink)
@page.match_wildcards('projects/hello-world')
@page.wildcards_hash.should == { 'permalink' => 'hello-world' }
end
end
end
end

View File

@ -208,123 +208,123 @@ describe Locomotive::Page do
context '#path combinations' do context '#path combinations' do
it 'generates them for a path depth equals to 1' do it 'generates them for a path depth equals to 1' do
Locomotive::Page.path_combinations('foo').should == ['foo', 'content_type_template'] Locomotive::Page.path_combinations('foo').should == ['foo', '*']
end end
it 'generates them for a path depth equals to 2' do it 'generates them for a path depth equals to 2' do
Locomotive::Page.path_combinations('foo/bar').should == ['foo/bar', 'foo/content_type_template', 'content_type_template/bar'] Locomotive::Page.path_combinations('foo/bar').should == ['foo/bar', 'foo/*', '*/bar', '*/*']
end end
it 'generates them for a path depth equals to 3' do it 'generates them for a path depth equals to 3' do
Locomotive::Page.path_combinations('foo/bar/baz').should == ['foo/bar/baz', 'foo/bar/content_type_template', 'foo/content_type_template/baz', 'content_type_template/bar/baz'] Locomotive::Page.path_combinations('foo/bar/baz').should == ['foo/bar/baz', 'foo/bar/*', 'foo/*/baz', 'foo/*/*', '*/bar/baz', '*/bar/*', '*/*/baz', '*/*/*']
end end
end end
end end
describe 'templatized extension' do # describe 'templatized extension' do
#
before(:each) do # before(:each) do
@page = FactoryGirl.build(:page, :parent => FactoryGirl.build(:page, :templatized => false), :templatized => true, :target_klass_name => 'Foo') # @page = FactoryGirl.build(:page, :parent => FactoryGirl.build(:page, :templatized => false), :templatized => true, :target_klass_name => 'Foo')
end # end
#
it 'is considered as a templatized page' do # it 'is considered as a templatized page' do
@page.templatized?.should be_true # @page.templatized?.should be_true
end # end
#
it 'fills in the slug field' do # it 'fills in the slug field' do
@page.valid? # @page.valid?
@page.slug.should == 'content_type_template' # @page.slug.should == 'content_type_template'
end # end
#
it 'returns the target klass' do # it 'returns the target klass' do
@page.target_klass.should == Foo # @page.target_klass.should == Foo
end # end
#
it 'has a name for the target entry' do # it 'has a name for the target entry' do
@page.target_entry_name.should == 'foo' # @page.target_entry_name.should == 'foo'
end # end
#
it 'uses the find_by_permalink method when fetching the entry' do # it 'uses the find_by_permalink method when fetching the entry' do
Foo.expects(:find_by_permalink) # Foo.expects(:find_by_permalink)
@page.fetch_target_entry('foo') # @page.fetch_target_entry('foo')
end # end
#
context '#descendants' do # context '#descendants' do
#
before(:each) do # before(:each) do
@home = FactoryGirl.create(:page) # @home = FactoryGirl.create(:page)
@page.attributes = { :parent_id => @home._id, :site => @home.site }; @page.save! # @page.attributes = { :parent_id => @home._id, :site => @home.site }; @page.save!
@sub_page = FactoryGirl.build(:page, :title => 'Subpage', :slug => 'foo', :parent => @page, :site => @home.site, :templatized => false) # @sub_page = FactoryGirl.build(:page, :title => 'Subpage', :slug => 'foo', :parent => @page, :site => @home.site, :templatized => false)
end # end
#
it 'inherits the templatized property from its parent' do # it 'inherits the templatized property from its parent' do
@sub_page.valid? # @sub_page.valid?
@sub_page.templatized?.should be_true # @sub_page.templatized?.should be_true
@sub_page.templatized_from_parent?.should be_true # @sub_page.templatized_from_parent?.should be_true
@sub_page.target_klass_name.should == 'Foo' # @sub_page.target_klass_name.should == 'Foo'
end # end
#
it 'gets templatized if its parent is' do # it 'gets templatized if its parent is' do
@page.attributes = { :templatized => false, :target_klass_name => nil }; @page.save! # @page.attributes = { :templatized => false, :target_klass_name => nil }; @page.save!
@sub_page.save.should be_true # @sub_page.save.should be_true
@sub_page.templatized?.should be_false # @sub_page.templatized?.should be_false
#
@page.attributes = { :templatized => true, :target_klass_name => 'Foo' }; @page.save! # @page.attributes = { :templatized => true, :target_klass_name => 'Foo' }; @page.save!
@sub_page.reload # @sub_page.reload
@sub_page.templatized?.should be_true # @sub_page.templatized?.should be_true
@sub_page.templatized_from_parent?.should be_true # @sub_page.templatized_from_parent?.should be_true
@sub_page.target_klass_name.should == 'Foo' # @sub_page.target_klass_name.should == 'Foo'
end # end
#
it 'is not templatized if its parent is no more a templatized page' do # it 'is not templatized if its parent is no more a templatized page' do
@sub_page.save.should be_true # @sub_page.save.should be_true
@page.templatized = false; @page.save! # @page.templatized = false; @page.save!
@sub_page.reload # @sub_page.reload
@sub_page.templatized.should be_false # @sub_page.templatized.should be_false
@sub_page.templatized_from_parent.should be_false # @sub_page.templatized_from_parent.should be_false
@sub_page.target_klass_name.should be_nil # @sub_page.target_klass_name.should be_nil
end # end
#
end # end
#
context 'using a content type' do # context 'using a content type' do
#
before(:each) do # before(:each) do
@site = FactoryGirl.build(:site) # @site = FactoryGirl.build(:site)
@content_type = FactoryGirl.build(:content_type, :slug => 'posts', :site => @site) # @content_type = FactoryGirl.build(:content_type, :slug => 'posts', :site => @site)
@page.site = @site # @page.site = @site
@page.target_klass_name = 'Locomotive::Entry42' # @page.target_klass_name = 'Locomotive::Entry42'
end # end
#
it 'has a name for the target entry' do # it 'has a name for the target entry' do
@site.stubs(:content_types).returns(mock(:find => @content_type)) # @site.stubs(:content_types).returns(mock(:find => @content_type))
@page.target_entry_name.should == 'post' # @page.target_entry_name.should == 'post'
end # end
#
context '#security' do # context '#security' do
#
before(:each) do # before(:each) do
Locomotive::ContentType.stubs(:find).returns(@content_type) # Locomotive::ContentType.stubs(:find).returns(@content_type)
end # end
#
it 'is valid if the content type belongs to the site' do # it 'is valid if the content type belongs to the site' do
@page.send(:ensure_target_klass_name_security) # @page.send(:ensure_target_klass_name_security)
@page.errors.should be_empty # @page.errors.should be_empty
end # end
#
it 'does not valid the page if the content type does not belong to the site' do # it 'does not valid the page if the content type does not belong to the site' do
@content_type.site = FactoryGirl.build(:site) # @content_type.site = FactoryGirl.build(:site)
@page.send(:ensure_target_klass_name_security) # @page.send(:ensure_target_klass_name_security)
@page.errors[:target_klass_name].should == ['presents a security problem'] # @page.errors[:target_klass_name].should == ['presents a security problem']
end # end
#
end # end
#
end # end
#
end # end
describe 'listed extension' do describe 'listed extension' do

View File

@ -111,7 +111,7 @@ describe Locomotive::Site do
@site = FactoryGirl.create(:site) @site = FactoryGirl.create(:site)
end end
it 'should create index and 404 pages' do it 'creates index and 404 pages' do
@site.pages.size.should == 2 @site.pages.size.should == 2
@site.pages.map(&:fullpath).sort.should == %w{404 index} @site.pages.map(&:fullpath).sort.should == %w{404 index}
end end

View File

@ -26,7 +26,7 @@ describe Locomotive::Snippet do
context 'with a normal top level snippet' do context 'with a normal top level snippet' do
before :each do before :each do
@page = FactoryGirl.create(:page, :site => @site, :slug => 'my_page_here', :raw_template => "{% include 'my_test_snippet' %}") @page = FactoryGirl.create(:page, :site => @site, :parent => @site.pages.root.first, :slug => 'my_page_here', :raw_template => "{% include 'my_test_snippet' %}")
end end
it 'updates templates with the new snippet template' do it 'updates templates with the new snippet template' do
@ -39,7 +39,7 @@ describe Locomotive::Snippet do
context 'for snippets inside of a block' do context 'for snippets inside of a block' do
before :each do before :each do
@page = FactoryGirl.create(:page, :site => @site, :slug => 'my_page_here', :raw_template => "{% block main %}{% include 'my_test_snippet' %}{% endblock %}") @page = FactoryGirl.create(:page, :site => @site, :parent => @site.pages.root.first, :slug => 'my_page_here', :raw_template => "{% block main %}{% include 'my_test_snippet' %}{% endblock %}")
end end
it 'updates templates with the new snippet template' do it 'updates templates with the new snippet template' do
@ -54,7 +54,7 @@ describe Locomotive::Snippet do
before :each do before :each do
Mongoid::Fields::I18n.with_locale(:fr) do Mongoid::Fields::I18n.with_locale(:fr) do
@snippet = FactoryGirl.create(:snippet, :site => @site, :slug => 'my_localized_test_snippet', :template => 'a testing template') @snippet = FactoryGirl.create(:snippet, :site => @site, :slug => 'my_localized_test_snippet', :template => 'a testing template')
@page = FactoryGirl.create(:page, :site => @site, :slug => 'my_localized_test_snippet', :raw_template => "{% block main %}{% include 'my_localized_test_snippet' %}{% endblock %}") @page = FactoryGirl.create(:page, :site => @site, :parent => @site.pages.root.first, :slug => 'my_localized_test_snippet', :raw_template => "{% block main %}{% include 'my_localized_test_snippet' %}{% endblock %}")
end end
end end