diff --git a/app/models/content_instance.rb b/app/models/content_instance.rb
index 5f6a1a86..a3f5c41f 100644
--- a/app/models/content_instance.rb
+++ b/app/models/content_instance.rb
@@ -7,6 +7,7 @@ class ContentInstance
include CustomFields::ProxyClassEnabler
## fields (dynamic fields) ##
+ field :_slug
field :_position_in_list, :type => Integer, :default => 0
## validations ##
@@ -16,6 +17,7 @@ class ContentInstance
embedded_in :content_type, :inverse_of => :contents
## callbacks ##
+ before_save :set_slug
before_create :add_to_list_bottom
## named scopes ##
@@ -29,16 +31,29 @@ class ContentInstance
protected
+ def set_slug
+ _alias = self.highlighted_field_alias
+ self._slug = self.send(_alias).parameterize('_')
+ end
+
def add_to_list_bottom
Rails.logger.debug "add_to_list_bottom"
self._position_in_list = self.content_type.contents.size
end
def require_highlighted_field
- _alias = self.content_type.highlighted_field._alias.to_sym
+ _alias = self.highlighted_field_alias
if self.send(_alias).blank?
self.errors.add(_alias, :blank)
end
end
+ def highlighted_field_value
+ self.send(self.content_type.highlighted_field._name)
+ end
+
+ def highlighted_field_alias
+ self.content_type.highlighted_field._alias.to_sym
+ end
+
end
\ No newline at end of file
diff --git a/app/models/extensions/page/templatized.rb b/app/models/extensions/page/templatized.rb
new file mode 100644
index 00000000..b5a181cf
--- /dev/null
+++ b/app/models/extensions/page/templatized.rb
@@ -0,0 +1,32 @@
+module Models
+ module Extensions
+ module Page
+ module Templatized
+
+ extend ActiveSupport::Concern
+
+ included do
+
+ belongs_to_related :content_type
+
+ field :templatized, :type => Boolean, :default => false
+
+ field :content_type_visible_column
+
+ before_validate :set_slug_if_templatized
+ end
+
+ module InstanceMethods
+
+ def set_slug_if_templatized
+ self.slug = 'content_type_template' if self.templatized?
+ end
+
+ end
+
+ end
+ end
+ end
+end
+
+
\ No newline at end of file
diff --git a/app/models/page.rb b/app/models/page.rb
index 53874105..8dced9d8 100644
--- a/app/models/page.rb
+++ b/app/models/page.rb
@@ -6,6 +6,7 @@ class Page
include Models::Extensions::Page::Tree
include Models::Extensions::Page::Parts
include Models::Extensions::Page::Render
+ include Models::Extensions::Page::Templatized
## fields ##
field :title
@@ -31,8 +32,8 @@ class Page
## named scopes ##
named_scope :latest_updated, :order_by => [[:updated_at, :desc]], :limit => Locomotive.config.lastest_items_nb
- named_scope :index, :where => { :slug => 'index', :depth => 0, :published => true }
- named_scope :not_found, :where => { :slug => '404', :depth => 0, :published => true }
+ named_scope :index, :where => { :slug => 'index', :depth => 0 }
+ named_scope :not_found, :where => { :slug => '404', :depth => 0 }
named_scope :published, :where => { :published => true }
## behaviours ##
diff --git a/app/views/admin/pages/_form.html.haml b/app/views/admin/pages/_form.html.haml
index b1026410..61d364c2 100644
--- a/app/views/admin/pages/_form.html.haml
+++ b/app/views/admin/pages/_form.html.haml
@@ -11,8 +11,13 @@
- if not @page.index? and not @page.not_found?
= f.input :parent_id, :as => :select, :collection => parent_pages_options, :include_blank => false
- = f.input :slug, :required => false, :hint => @page.slug.blank? ? ' ' : @page.url, :input_html => { :data_url => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }
-
+ = f.input :slug, :required => false, :hint => @page.slug.blank? ? ' ' : @page.url, :input_html => { :data_url => get_path_admin_pages_url, :disabled => @page.index? || @page.not_found? }, :wrapper_html => { :style => "#{'display: none' if @page.templatized?}" }
+
+ = f.custom_input :templatized, :css => 'toggle' do
+ = f.check_box :templatized
+
+ = f.input :content_type_id, :as => :select, :collection => current_site.content_types.all.to_a, :include_blank => false, :wrapper_html => { :style => "#{'display: none' unless @page.templatized?}" }
+
= f.custom_input :published, :css => 'toggle' do
= f.check_box :published
diff --git a/app/views/admin/pages/_page.html.haml b/app/views/admin/pages/_page.html.haml
index 0cfbcbe6..542a3140 100644
--- a/app/views/admin/pages/_page.html.haml
+++ b/app/views/admin/pages/_page.html.haml
@@ -1,4 +1,4 @@
-%li{ :id => "item-#{page.id}", :class => "#{'not-found' if page.not_found? }"}
+%li{ :id => "item-#{page.id}", :class => "#{'not-found' if page.not_found? } #{'templatized' if page.templatized?}"}
- if not page.index? and not page.children.empty?
= image_tag 'admin/list/icons/node_closed.png', :class => 'toggler'
%em
diff --git a/config/locales/admin_ui_en.yml b/config/locales/admin_ui_en.yml
index 9ce24b00..db6ab686 100644
--- a/config/locales/admin_ui_en.yml
+++ b/config/locales/admin_ui_en.yml
@@ -268,6 +268,7 @@ en:
page:
published: "Only authenticated accounts can view unpublished pages."
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."
snippet:
slug: "You need to know it in order to insert the snippet inside a page or a layout"
site:
diff --git a/config/locales/admin_ui_fr.yml b/config/locales/admin_ui_fr.yml
index a3cf735a..99378305 100644
--- a/config/locales/admin_ui_fr.yml
+++ b/config/locales/admin_ui_fr.yml
@@ -290,6 +290,7 @@ fr:
page:
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."
+ templatized: "Utilise la page comme un template pour un modèle défini."
snippet:
slug: "Utilisé pour insérer le snippet dans une page ou un gabarit."
site:
diff --git a/doc/TODO b/doc/TODO
index 487ebfd3..61432b7c 100644
--- a/doc/TODO
+++ b/doc/TODO
@@ -2,7 +2,7 @@ BOARD:
- refactor slugify method (use parameterize + create a module)
- send email when new content added thru api
-- page templatized (bound to a model)
+- shortcut for jquery include tag
BACKLOG:
@@ -61,4 +61,5 @@ x refactoring admin crud (pages + layouts + snippets)
x flash messages in French
x save layout / snippet / page / stylesheet / javascript with CMD + S (ajax)
x change action icons according to the right action [Sacha]
-x publish event when saving form in ajax (for instance, in order to update account name or site name)
\ No newline at end of file
+x publish event when saving form in ajax (for instance, in order to update account name or site name)
+x page templatized (bound to a model)
\ No newline at end of file
diff --git a/lib/locomotive/liquid/drops/content.rb b/lib/locomotive/liquid/drops/content.rb
index 687ead20..7c958fde 100644
--- a/lib/locomotive/liquid/drops/content.rb
+++ b/lib/locomotive/liquid/drops/content.rb
@@ -11,7 +11,9 @@ module Locomotive
end
end
-
+ def highlighted_field_value
+ @source.highlighted_field_value
+ end
end
end
diff --git a/lib/locomotive/liquid/drops/page.rb b/lib/locomotive/liquid/drops/page.rb
index 32c07462..db81dc4a 100644
--- a/lib/locomotive/liquid/drops/page.rb
+++ b/lib/locomotive/liquid/drops/page.rb
@@ -3,7 +3,15 @@ module Locomotive
module Drops
class Page < Base
- liquid_attributes << :title << :slug
+ # liquid_attributes << :title << :slug
+
+ def title
+ @source.templatized? ? @context['content_instance'].highlighted_field_value : @source.title
+ end
+
+ def slug
+ @source.templatized? ? @source.content_type.slug.singularize : @source.slug
+ end
def children
@children ||= liquify(*@source.children)
diff --git a/lib/locomotive/liquid/tags/nav.rb b/lib/locomotive/liquid/tags/nav.rb
index e2fae74f..def3254d 100644
--- a/lib/locomotive/liquid/tags/nav.rb
+++ b/lib/locomotive/liquid/tags/nav.rb
@@ -29,10 +29,12 @@ module Locomotive
source = context.registers[@site_or_page.to_sym]
+ puts "#{@site_or_page.to_sym} / source = #{source.inspect}"
+
if source.respond_to?(:name) # site ?
- source = source.pages.first # start from home page
+ source = source.pages.index.first # start from home page
else
- source = source.parent
+ source = source.parent || source
end
output = %{
}
diff --git a/lib/locomotive/render.rb b/lib/locomotive/render.rb
index d82bbe0f..36ceffb8 100644
--- a/lib/locomotive/render.rb
+++ b/lib/locomotive/render.rb
@@ -23,9 +23,24 @@ module Locomotive
path.gsub!(/^\//, '')
path = 'index' if path.blank?
- if page = current_site.pages.where(:fullpath => path).first
+ if path != 'index'
+ dirname = File.dirname(path).gsub(/^\.$/, '') # also look for templatized page path
+ path = [path, File.join(dirname, 'content_type_template').gsub(/^\//, '')]
+ end
+
+ # TODO: path is not correctly built + find content instance in order to render a 404 page if not found
+
+ if page = current_site.pages.any_in(:fullpath => [*path]).first
if not page.published? and current_admin.nil?
page = nil
+ else
+ if page.templatized?
+ @content_instance = page.content_type.contents.where(:_slug => File.basename(path.first)).first
+
+ if @content_instance.nil? # content instance not found
+ page = nil
+ end
+ end
end
end
@@ -43,6 +58,11 @@ module Locomotive
'current_page' => self.params[:page]
}
+ if @page.templatized? # add instance from content type
+ assigns['content_instance'] = @content_instance
+ assigns[@page.content_type.slug.singularize] = @content_instance # just here to help to write readable liquid code
+ end
+
registers = { :controller => self, :site => current_site, :page => @page }
::Liquid::Context.new(assigns, registers)
diff --git a/public/images/admin/list/item-left.png b/public/images/admin/list/item-left.png
index 666c09ff..4ef92cfb 100644
Binary files a/public/images/admin/list/item-left.png and b/public/images/admin/list/item-left.png differ
diff --git a/public/javascripts/admin/pages.js b/public/javascripts/admin/pages.js
index 6daf2a46..eee3ef9a 100644
--- a/public/javascripts/admin/pages.js
+++ b/public/javascripts/admin/pages.js
@@ -31,6 +31,19 @@ $(document).ready(function() {
}
});
+ // templatized feature
+
+ $.subscribe('toggle.page_templatized.checked', function(event, data) {
+ $('#page_slug_input').hide();
+ $('#page_content_type_id_input').show();
+ }, []);
+
+ $.subscribe('toggle.page_templatized.unchecked', function(event, data) {
+ $('#page_slug_input').show();
+ $('#page_slug').val(makeSlug($('#page_title').val())).addClass('touched');
+ $('#page_content_type_id_input').hide();
+ }, []);
+
// automatic slug from page title
$('#page_title').keypress(function() {
var input = $(this);
diff --git a/public/javascripts/admin/plugins/toggle.js b/public/javascripts/admin/plugins/toggle.js
index bf560f16..f200d4cb 100644
--- a/public/javascripts/admin/plugins/toggle.js
+++ b/public/javascripts/admin/plugins/toggle.js
@@ -64,6 +64,8 @@
$(element).parent().css("background-color", settings.off_bg_color);
$(element).parent().parent().prev().removeAttr("checked");
$(element).removeClass("left").addClass("right");
+
+ $.publish('toggle.' + $(element).parent().parent().prev().attr('id') + '.unchecked', []);
});
}else{
@@ -78,6 +80,7 @@
$(element).parent().parent().prev().attr("checked","checked");
$(element).removeClass("right").addClass("left");
+ $.publish('toggle.' + $(element).parent().parent().prev().attr('id') + '.checked', []);
});
}
diff --git a/public/stylesheets/admin/application.css b/public/stylesheets/admin/application.css
index 65312fe9..333256e2 100644
--- a/public/stylesheets/admin/application.css
+++ b/public/stylesheets/admin/application.css
@@ -117,7 +117,7 @@ ul.assets li.asset.last {
margin-right: 0px;
}
-ul.assets li.asset h4 { margin: 0px; height: 30px; border-bottom: 1px solid #C2C4D2; }
+ul.assets li.asset h4 { margin: 0px; height: 30px; border-bottom: 1px solid #ccced7; }
ul.assets li.asset h4 a {
position: relative;
@@ -205,6 +205,11 @@ div#uploadAssetsInputQueue { display: none; }
cursor: move;
}
+#pages-list ul.folder li.templatized em {
+ background-position: left -62px;
+ cursor: pointer;
+}
+
#pages-list li .toggler {
position: absolute;
top: 9px;
diff --git a/public/stylesheets/admin/formtastic_changes.css b/public/stylesheets/admin/formtastic_changes.css
index 9ca40a69..a9a9071e 100644
--- a/public/stylesheets/admin/formtastic_changes.css
+++ b/public/stylesheets/admin/formtastic_changes.css
@@ -80,7 +80,7 @@ form.formtastic fieldset.inputs ol {
padding-top: 15px;
padding-bottom: 5px;
background: #ebedf4 url(/images/admin/form/footer.png) no-repeat 0 bottom;
- border-top: 1px solid #C2C4D2;
+ border-top: 1px solid #ccced7;
}
@media screen and (-webkit-min-device-pixel-ratio:0) {
diff --git a/public/stylesheets/admin/page_parts.css b/public/stylesheets/admin/page_parts.css
index 89b9fe43..60af0f9a 100644
--- a/public/stylesheets/admin/page_parts.css
+++ b/public/stylesheets/admin/page_parts.css
@@ -2,7 +2,7 @@
background: #ebedf4 url(/images/admin/form/footer.png) no-repeat 0 bottom;
width: 880px;
padding: 20px 20px;
- border-top: 1px solid #C2C4D2;
+ border-top: 1px solid #ccced7;
}
#page-parts {
diff --git a/spec/lib/locomotive/render_spec.rb b/spec/lib/locomotive/render_spec.rb
index e620d65b..3d43fe29 100644
--- a/spec/lib/locomotive/render_spec.rb
+++ b/spec/lib/locomotive/render_spec.rb
@@ -52,19 +52,19 @@ describe 'Locomotive rendering system' do
it 'should retrieve the index page /' do
@controller.request.fullpath = '/'
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'index' }).returns([@page])
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{index} }).returns([@page])
@controller.send(:locomotive_page).should_not be_nil
end
it 'should also retrieve the index page (index.html)' do
@controller.request.fullpath = '/index.html'
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'index' }).returns([@page])
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{index} }).returns([@page])
@controller.send(:locomotive_page).should_not be_nil
end
it 'should retrieve it based on the full path' do
@controller.request.fullpath = '/about_us/team.html'
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'about_us/team' }).returns([@page])
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{about_us/team about_us/content_type_template} }).returns([@page])
@controller.send(:locomotive_page).should_not be_nil
end
@@ -74,6 +74,31 @@ describe 'Locomotive rendering system' do
@controller.send(:locomotive_page).should be_true
end
+ context 'templatized page' do
+
+ before(:each) do
+ @content_type = Factory.build(:content_type, :site => nil)
+ @page.templatized = true
+ @page.content_type = @content_type
+ @controller.request.fullpath = '/projects/edeneo.html'
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{projects/edeneo projects/content_type_template} }).returns([@page])
+ end
+
+ it 'sets the content_instance variable' do
+ @content_type.contents.stubs(:where).returns([42])
+ @controller.send(:locomotive_page).should_not be_nil
+ @controller.instance_variable_get(:@content_instance).should == 42
+ end
+
+ it 'returns the 404 page if the instance does not exist' do
+ @content_type.contents.stubs(:where).returns([])
+ @controller.current_site.pages.expects(:not_found).returns([true])
+ @controller.send(:locomotive_page).should be_true
+ @controller.instance_variable_get(:@content_instance).should be_nil
+ end
+
+ end
+
context 'non published page' do
before(:each) do
@@ -83,7 +108,7 @@ describe 'Locomotive rendering system' do
it 'should return the 404 page if the page has not been published yet' do
@controller.request.fullpath = '/contact'
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'contact' }).returns([@page])
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{contact content_type_template} }).returns([@page])
@controller.current_site.pages.expects(:not_found).returns([true])
@controller.send(:locomotive_page).should be_true
end
@@ -91,7 +116,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
@controller.current_admin = true
@controller.request.fullpath = '/contact'
- @controller.current_site.pages.expects(:where).with({ :fullpath => 'contact' }).returns([@page])
+ @controller.current_site.pages.expects(:any_in).with({ :fullpath => %w{contact content_type_template} }).returns([@page])
@controller.send(:locomotive_page).should == @page
end
diff --git a/spec/models/page_spec.rb b/spec/models/page_spec.rb
index afb31290..eb566b9e 100644
--- a/spec/models/page_spec.rb
+++ b/spec/models/page_spec.rb
@@ -333,4 +333,26 @@ describe Page do
end
end
+
+ describe 'templatized extension' do
+
+ before(:each) do
+ @page = Factory.build(:page, :site => nil, :templatized => true, :content_type_id => 42)
+ ContentType.stubs(:find).returns(Factory.build(:content_type, :site => nil))
+ end
+
+ it 'is considered as a templatized page' do
+ @page.templatized?.should be_true
+ end
+
+ it 'fills in the slug field' do
+ @page.valid?
+ @page.slug.should == 'content_type_template'
+ end
+
+ it 'does forget to set the content type id' do
+ @page.content_type_id.should == '42'
+ end
+
+ end
end
\ No newline at end of file