diff --git a/Gemfile b/Gemfile index a002defd..63b0a639 100644 --- a/Gemfile +++ b/Gemfile @@ -52,6 +52,7 @@ group :test do gem 'rspec-rails', '2.3.1' gem 'factory_girl_rails' gem 'pickle' + gem 'xpath', :git => 'https://github.com/wunderbread/xpath.git' gem 'capybara' gem 'database_cleaner' diff --git a/Gemfile.lock b/Gemfile.lock index e646dbdc..305eebd4 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -4,6 +4,13 @@ GIT specs: mocha (0.9.12.20110213002255) +GIT + remote: https://github.com/wunderbread/xpath.git + revision: d04da707886287e7dfe82705fda5b3d4f65e94c3 + specs: + xpath (0.1.2) + nokogiri (~> 1.4) + GEM remote: http://rubygems.org/ specs: @@ -45,7 +52,7 @@ GEM bson (1.2.4) bson_ext (1.2.4) builder (2.1.2) - capybara (0.4.1.2) + capybara (0.4.0) celerity (>= 0.7.9) culerity (>= 0.2.4) mime-types (>= 1.16) @@ -53,7 +60,7 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) selenium-webdriver (>= 0.0.27) - xpath (~> 0.1.3) + xpath (~> 0.1.2) celerity (0.8.8) childprocess (0.1.7) ffi (~> 0.6.3) @@ -242,8 +249,6 @@ GEM warden (0.10.7) rack (>= 1.0.0) will_paginate (3.0.pre2) - xpath (0.1.3) - nokogiri (~> 1.3) yard (0.6.4) yui-compressor (0.9.4) @@ -291,3 +296,4 @@ DEPENDENCIES unicorn warden will_paginate + xpath! diff --git a/app/controllers/admin/imports_controller.rb b/app/controllers/admin/imports_controller.rb index c611ce23..ab717bf1 100644 --- a/app/controllers/admin/imports_controller.rb +++ b/app/controllers/admin/imports_controller.rb @@ -34,7 +34,7 @@ module Admin rescue Exception => e logger.error "[Locomotive import] #{e.message} / #{e.backtrace}" - @error = t('errors.messages.invalid_theme_file') + @error = e.message flash[:alert] = t('flash.admin.imports.create.alert') render 'new' diff --git a/app/models/editable_element.rb b/app/models/editable_element.rb index 109bc121..60c563c8 100644 --- a/app/models/editable_element.rb +++ b/app/models/editable_element.rb @@ -6,8 +6,10 @@ class EditableElement field :slug field :block field :default_content + field :default_attribute field :hint field :disabled, :type => Boolean, :default => false + field :assignable, :type => Boolean, :default => true field :from_parent, :type => Boolean, :default => false ## associations ## diff --git a/app/models/extensions/page/editable_elements.rb b/app/models/extensions/page/editable_elements.rb index 8398f4cc..e0a6d746 100644 --- a/app/models/extensions/page/editable_elements.rb +++ b/app/models/extensions/page/editable_elements.rb @@ -62,17 +62,23 @@ module Models def merge_editable_elements_from_page(source) source.editable_elements.each do |el| - next if el.disabled? + next if el.disabled? or !el.assignable? existing_el = self.find_editable_element(el.block, el.slug) if existing_el.nil? # new one from parents new_attributes = el.attributes.merge(:from_parent => true) - new_attributes[:default_content] = el.content - + if new_attributes['default_attribute'].present? + new_attributes['default_content'] = self.send(new_attributes['default_attribute']) || el.content + else + new_attributes['default_content'] = el.content + end + self.editable_elements.build(new_attributes, el.class) - else + elsif existing_el.default_attribute.nil? existing_el.attributes = { :disabled => false, :default_content => el.content } + else + existing_el.attributes = { :disabled => false } end end end diff --git a/app/models/extensions/page/parse.rb b/app/models/extensions/page/parse.rb index 7395b241..ec304ee5 100644 --- a/app/models/extensions/page/parse.rb +++ b/app/models/extensions/page/parse.rb @@ -37,9 +37,9 @@ module Models begin self._parse_and_serialize_template rescue ::Liquid::SyntaxError => error - @parsing_errors << :liquid_syntax + @parsing_errors << I18n.t(:liquid_syntax, :fullpath => self.fullpath, :error => error.to_s,:scope => [:errors, :messages]) rescue ::Locomotive::Liquid::PageNotFound => error - @parsing_errors << :liquid_extend + @parsing_errors << I18n.t(:liquid_extend, :fullpath => self.fullpath,:scope => [:errors, :messages]) end end end diff --git a/config/locales/default.de.yml b/config/locales/default.de.yml index 1b82a2bf..99615424 100644 --- a/config/locales/default.de.yml +++ b/config/locales/default.de.yml @@ -32,8 +32,8 @@ de: protected_page: "Du kannst keine Index oder 404 Seiten löschen" extname_changed: "Die neue Datei hat nicht die originale Dateiendung" array_too_short: "ist zu kurz (minimale Element-Zahl ist %{count})" - liquid_syntax: "Liquid Syntax-Fehler, bitte überprüfe die Syntax" - liquid_extend: "Die Seite verwendet eine Vorlage, die gar nicht existiert" + liquid_syntax: "Liquid Syntax-Fehler, bitte überprüfe die Syntax ('%{error}'/'%{fullpath}')" + liquid_extend: "Die Seite '%{fullpath}' verwendet eine Vorlage, die gar nicht existiert" invalid_theme_file: "darf nicht leer sein oder ist keine zip-Datei" attributes: diff --git a/config/locales/default.en.yml b/config/locales/default.en.yml index 9b892cbb..9793b90e 100644 --- a/config/locales/default.en.yml +++ b/config/locales/default.en.yml @@ -11,8 +11,8 @@ en: protected_page: "You can not remove index or 404 pages" extname_changed: "New file does not have the original extension" array_too_short: "is too small (minimum element number is %{count})" - liquid_syntax: "Liquid Syntax error, please check the syntax" - liquid_extend: "The page extends a template which does not exist" + liquid_syntax: "Liquid Syntax error ('%{error}' on '%{fullpath}')" + liquid_extend: "The page '%{fullpath}' extends a template which does not exist" invalid_theme_file: "can't be blank or isn't a zip file" attributes: diff --git a/config/locales/default.fr.yml b/config/locales/default.fr.yml index 20bb5c5b..9ec2aef7 100644 --- a/config/locales/default.fr.yml +++ b/config/locales/default.fr.yml @@ -32,8 +32,8 @@ fr: protected_page: "Vous ne pouvez pas supprimer les pages index et 404" extname_changed: "Nouveau fichier n'a pas l'extension original" array_too_short: "est trop petit (le nombre minimum d'éléments est %{count})" - liquid_syntax: "Erreur de syntaxe dans les sections de page, veuillez vérifier la syntaxe" - liquid_extend: "La page étend le contenu d'une page qui n'existe pas" + liquid_syntax: "Erreur de syntaxe dans les sections de page, veuillez vérifier la syntaxe ('%{error}'/'%{fullpath}')" + liquid_extend: "La page '%{fullpath}' étend le contenu d'une page qui n'existe pas" invalid_theme_file: "doit être rempli ou n'est pas un fichier zip" attributes: diff --git a/lib/locomotive.rb b/lib/locomotive.rb index d0a782a7..045a7679 100644 --- a/lib/locomotive.rb +++ b/lib/locomotive.rb @@ -17,7 +17,7 @@ require 'locomotive/routing' require 'locomotive/regexps' require 'locomotive/render' require 'locomotive/import' -require 'locomotive/delayed_job' +#require 'locomotive/delayed_job' require 'locomotive/middlewares' require 'locomotive/session_store' diff --git a/lib/locomotive/engine.rb b/lib/locomotive/engine.rb index f3254a3f..8f50f106 100644 --- a/lib/locomotive/engine.rb +++ b/lib/locomotive/engine.rb @@ -16,7 +16,7 @@ require 'actionmailer_with_request' require 'heroku' require 'httparty' require 'redcloth' -require 'delayed_job_mongoid' +#require 'delayed_job_mongoid' require 'zip/zipfilesystem' require 'jammit-s3' diff --git a/lib/locomotive/import/pages.rb b/lib/locomotive/import/pages.rb index 610ae68f..a10f1db6 100644 --- a/lib/locomotive/import/pages.rb +++ b/lib/locomotive/import/pages.rb @@ -91,13 +91,10 @@ module Locomotive def build_parent_template(template) # just check if the template contains the extends keyword - fullpath = template.scan(/\{% extends (\w+) %\}/).flatten.first + fullpath = template.scan(/\{% extends \'?([\w|\/]+)\'? %\}/).flatten.first if fullpath # inheritance detected - fullpath.gsub!("'", '') - return if fullpath == 'parent' - self.add_page(fullpath) end end diff --git a/lib/locomotive/liquid/drops/contents.rb b/lib/locomotive/liquid/drops/contents.rb index cfa58cf6..65a2e1c2 100644 --- a/lib/locomotive/liquid/drops/contents.rb +++ b/lib/locomotive/liquid/drops/contents.rb @@ -28,6 +28,18 @@ module Locomotive def each(&block) self.collection.each(&block) end + + def size + self.collection.size + end + + def empty? + self.collection.empty? + end + + def any? + self.collection.any? + end def api { 'create' => @context.registers[:controller].send('admin_api_contents_url', @content_type.slug) } diff --git a/lib/locomotive/liquid/filters/html.rb b/lib/locomotive/liquid/filters/html.rb index acfca793..963cfd30 100644 --- a/lib/locomotive/liquid/filters/html.rb +++ b/lib/locomotive/liquid/filters/html.rb @@ -43,10 +43,8 @@ module Locomotive # input: name of file including folder # example: 'about/myphoto.jpg' | theme_image # def theme_image_tag(input, *args) - return '' if input.nil? - input = "images/#{input}" unless input.starts_with?('/') image_options = inline_options(args_to_options(args)) - "" + "" end # Write an image tag diff --git a/lib/locomotive/liquid/tags/editable.rb b/lib/locomotive/liquid/tags/editable.rb index 82609f12..ed857c94 100644 --- a/lib/locomotive/liquid/tags/editable.rb +++ b/lib/locomotive/liquid/tags/editable.rb @@ -1,4 +1,5 @@ require 'locomotive/liquid/tags/editable/base' require 'locomotive/liquid/tags/editable/short_text' require 'locomotive/liquid/tags/editable/long_text' -require 'locomotive/liquid/tags/editable/file' \ No newline at end of file +require 'locomotive/liquid/tags/editable/file' +require 'locomotive/liquid/tags/editable/content' \ No newline at end of file diff --git a/lib/locomotive/liquid/tags/editable/base.rb b/lib/locomotive/liquid/tags/editable/base.rb index b2621185..aaa04c50 100644 --- a/lib/locomotive/liquid/tags/editable/base.rb +++ b/lib/locomotive/liquid/tags/editable/base.rb @@ -9,7 +9,7 @@ module Locomotive def initialize(tag_name, markup, tokens, context) if markup =~ Syntax @slug = $1.gsub('\'', '') - @options = {} + @options = { :assignable => true } markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/^'/, '').gsub(/'$/, '') } else raise ::Liquid::SyntaxError.new("Syntax Error in 'editable_xxx' - Valid syntax: editable_xxx (, )") @@ -26,7 +26,9 @@ module Locomotive :block => @context[:current_block].try(:name), :slug => @slug, :hint => @options[:hint], - :default_content => @nodelist.first.to_s, + :default_attribute => @options[:default], + :default_content => default_content, + :assignable => @options[:assignable], :disabled => false, :from_parent => false }, document_type) @@ -37,11 +39,11 @@ module Locomotive current_page = context.registers[:page] element = current_page.find_editable_element(context['block'].try(:name), @slug) - - if element + + if element.present? render_element(context, element) else - Locomotive.logger "[editable element] missing element #{context[:block].name} / #{@slug}" + Locomotive.logger "[editable element] missing element `#{context['block'].try(:name)}` / #{@slug} on #{current_page.fullpath}" '' end end @@ -55,6 +57,15 @@ module Locomotive def document_type raise 'FIXME: has to be overidden' end + + + def default_content + if @options[:default].present? + @context[:page].send(@options[:default]) + else + @nodelist.first.to_s + end + end end diff --git a/lib/locomotive/liquid/tags/editable/content.rb b/lib/locomotive/liquid/tags/editable/content.rb new file mode 100644 index 00000000..1964f92a --- /dev/null +++ b/lib/locomotive/liquid/tags/editable/content.rb @@ -0,0 +1,49 @@ +module Locomotive + module Liquid + module Tags + module Editable + class Content < ::Liquid::Tag + + Syntax = /(#{::Liquid::Expression}+)?/ + + def initialize(tag_name, markup, tokens, context) + if markup =~ Syntax + @slug = $1 + @options = { :inherit => false } + markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') } + else + raise ::Liquid::SyntaxError.new("Syntax Error in 'content' - Valid syntax: slug") + end + + super + end + + def render(context) + page = context.registers[:page] + element = find_element(page) + + if element.nil? && @options[:inherit] != false + while page.parent.present? && element.nil? + page = page.parent + element = find_element(page) + end + end + + if element.present? + return element.content + else + raise ::Liquid::SyntaxError.new("Error in 'content' - Can't find editable element called `#{@slug}`") + end + end + + def find_element(page) + page.editable_elements.where(:slug => @slug).first + end + + end + + ::Liquid::Template.register_tag('content', Content) + end + end + end +end \ No newline at end of file diff --git a/public/stylesheets/admin/menu.css b/public/stylesheets/admin/menu.css index 2a3c63a1..fba1dfe3 100644 --- a/public/stylesheets/admin/menu.css +++ b/public/stylesheets/admin/menu.css @@ -8,7 +8,8 @@ position: relative; top: -1px; z-index: 998; - height: 60px; + min-height: 60px; + width: 950px; margin: 0px; padding: 0 8px; background: transparent url(/images/admin/menu/shadow.png) repeat-y 0 0; @@ -24,12 +25,24 @@ border-top-right-radius: 3px ; -moz-border-radius-top-right: 3px ; -webkit-border-top-right-radius: 3px ; - height: 60px; } + width: 826px; + padding-right: 124px; + padding-top: 8px; + padding-bottom: 8px; + min-height: 44px; } + #submenu > ul:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; } #submenu > ul a { text-decoration: none; } #submenu > ul > li { - margin: 15px 7px 0 8px; - float: left; } + margin: 7px 7px 7px 8px; + height: 30px; + float: left; + position: relative; } #submenu > ul > li.hoverable > a span em { display: inline-block; background: transparent url(/images/admin/menu/icons.png) no-repeat 0 -16px; @@ -101,10 +114,9 @@ position: absolute; top: 0px; right: 22px; - height: 60px; + height: 100%; padding-left: 20px; - z-index: 1; - background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; } + z-index: 1; } #submenu > .action a { margin-top: 18px; display: inline-block; @@ -136,7 +148,7 @@ border-color: black; } #submenu .popup { position: absolute; - top: 42px; + top: 26px; min-width: 250px; background: #fff; box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.5); @@ -311,7 +323,7 @@ body.contents #submenu > ul { background: -moz-linear-gradient(0% 100% 90deg, #212229, #1e1e24); background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#1e1e24), to(#212229)); } body.contents #submenu > .action { - background-image: url(/images/admin/menu/submenu/black-action-border.png) !important; } + background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; } #menu li.assets a em { position: relative; diff --git a/public/stylesheets/sass/admin/_helpers.scss b/public/stylesheets/sass/admin/_helpers.scss index c24aeada..278c7fc2 100644 --- a/public/stylesheets/sass/admin/_helpers.scss +++ b/public/stylesheets/sass/admin/_helpers.scss @@ -1,5 +1,15 @@ /* ___ rounded ___ */ +@mixin clearfix { + &:after { + content: "."; + display: block; + height: 0; + clear: both; + visibility: hidden; + }; +} + @mixin rounded($side, $radius: 10px, $important: false) { @if $important == true { $important: !important; } diff --git a/public/stylesheets/sass/admin/menu.scss b/public/stylesheets/sass/admin/menu.scss index 67443840..230bec14 100644 --- a/public/stylesheets/sass/admin/menu.scss +++ b/public/stylesheets/sass/admin/menu.scss @@ -9,7 +9,8 @@ position: relative; top: -1px; z-index: 998; - height: 60px; + min-height: 60px; + width: 950px; margin: 0px; padding: 0 8px; background: transparent url(/images/admin/menu/shadow.png) repeat-y 0 0; @@ -17,16 +18,22 @@ /* ___ submenu items ___ */ & > ul { + @include clearfix; @include reset; border-top: 1px solid rgba(255, 255, 255, 0.4); background: transparent url(/images/admin/menu/submenu/shadow.png) repeat-x 0 0; @include rounded(top-right, 3px); - - height: 60px; + width: 826px; + padding-right: 124px; + padding-top: 8px; + padding-bottom: 8px; + min-height: 44px; & > li { - margin: 15px 7px 0 8px; + margin: 7px 7px 7px 8px; + height: 30px; float: left; + position: relative; &.hoverable > a span { em { @@ -105,10 +112,9 @@ & > .action { @include absolute-position(top, 0px, right, 22px); - height: 60px; + height: 100%; padding-left: 20px; z-index: 1; - background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; a { margin-top: 18px; @@ -150,7 +156,7 @@ .popup { position: absolute; - top: 42px; + top: 26px; min-width: 250px; background: #fff; @include box-shadow(0px, 0px, 10px, rgba(0, 0, 0, 0.5)); @@ -253,7 +259,7 @@ } & > .action { - background-image: url(/images/admin/menu/submenu/black-action-border.png) !important; + background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; } } diff --git a/spec/factories.rb b/spec/factories.rb index 9f9f253a..23a61783 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -40,7 +40,7 @@ Factory.define "frenchy user", :parent => :account do |a| end Factory.define "brazillian user", :parent => :account do |a| - a.name "José Carlos" + a.name "Jose Carlos" a.email "jose@carlos.com.br" a.locale 'pt-BR' end diff --git a/spec/fixtures/themes/default.zip b/spec/fixtures/themes/default.zip index 916d4cac..9bf39d8f 100644 Binary files a/spec/fixtures/themes/default.zip and b/spec/fixtures/themes/default.zip differ diff --git a/spec/lib/locomotive/import_spec.rb b/spec/lib/locomotive/import_spec.rb index 425ee5d9..485fc379 100644 --- a/spec/lib/locomotive/import_spec.rb +++ b/spec/lib/locomotive/import_spec.rb @@ -53,7 +53,7 @@ describe Locomotive::Import::Job do end it 'inserts all the pages' do - @site.pages.count.should == 9 + @site.pages.count.should == 11 end it 'inserts the index and 404 pages' do diff --git a/spec/lib/locomotive/liquid/tags/editable/content_spec.rb b/spec/lib/locomotive/liquid/tags/editable/content_spec.rb new file mode 100644 index 00000000..d5405545 --- /dev/null +++ b/spec/lib/locomotive/liquid/tags/editable/content_spec.rb @@ -0,0 +1,97 @@ +require 'spec_helper' + +describe Locomotive::Liquid::Tags::Editable::Content do + + before :each do + Locomotive::Liquid::Tags::Editable::Content.any_instance.stubs(:end_tag).returns(true) + end + + context 'syntax' do + + it 'should have a valid syntax' do + ["slug", "slug, inherit: true"].each do |markup| + lambda do + Locomotive::Liquid::Tags::Editable::Content.new('content', markup, ["{% content %}"], {}) + end.should_not raise_error + end + end + + end + + context 'output' do + + before :each do + EditableElement.any_instance.stubs(:content).returns("test string") + end + + context 'inheriting from a parent' do + + before :each do + @parent = Factory.build(:page) + @child = Factory.build(:page) + + @child.stubs(:parent).returns(@parent) + end + + it 'should return the parents field if inherit is set' do + @element = @parent.editable_elements.create(:slug => 'test') + @child.stubs(:raw_template).returns("{% content test, inherit: true %}") + template = Liquid::Template.parse(@child.raw_template) + text = template.render!(liquid_context(:page => @child)) + text.should match /test string/ + end + + it 'should raise an exception if it cant find the field' do + @child.stubs(:raw_template).returns("{% content test, inherit: true %}") + template = Liquid::Template.parse(@child.raw_template) + lambda do + template.render!(liquid_context(:page => @child)) + end.should raise_error + end + + after :each do + @parent.editable_elements.destroy_all + end + + end + + context 'reading from the same page' do + + before :each do + @page = Factory.build(:page) + end + + it 'should return the previously defined field' do + @element = @page.editable_elements.create(:slug => 'test') + @page.stubs(:raw_template).returns("{% content test %}") + template = Liquid::Template.parse(@page.raw_template) + text = template.render!(liquid_context(:page => @page)) + text.should match /test string/ + end + + it 'should raise an exception if it wasnt defined' do + @page.stubs(:raw_template).returns("{% content test %}") + template = Liquid::Template.parse(@page.raw_template) + lambda do + template.render!(liquid_context(:page => @page)) + end.should raise_error + end + + after :each do + @page.editable_elements.destroy_all + end + + end + + end + + # ___ helpers methods ___ # + + def liquid_context(options = {}) + ::Liquid::Context.new({}, {}, + { + :page => options[:page] + }, true) + end + +end