Merge branch 'multi-level-sub_menu' of https://github.com/dirkkelly/locomotive into dirkkelly-multi-level-sub_menu
This commit is contained in:
commit
c3cc358934
1
Gemfile
1
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'
|
||||
|
14
Gemfile.lock
14
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!
|
||||
|
@ -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'
|
||||
|
@ -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 ##
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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:
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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'
|
||||
|
||||
|
@ -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
|
||||
|
@ -29,6 +29,18 @@ module Locomotive
|
||||
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) }
|
||||
end
|
||||
|
@ -43,10 +43,8 @@ module Locomotive
|
||||
# input: name of file including folder
|
||||
# example: 'about/myphoto.jpg' | theme_image # <img src="images/about/myphoto.jpg" />
|
||||
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))
|
||||
"<img src=\"#{asset_url(input)}\" #{image_options}/>"
|
||||
"<img src=\"#{theme_image_url(input)}\" #{image_options}/>"
|
||||
end
|
||||
|
||||
# Write an image tag
|
||||
|
@ -2,3 +2,4 @@ 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'
|
||||
require 'locomotive/liquid/tags/editable/content'
|
@ -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 <slug>(, <options>)")
|
||||
@ -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)
|
||||
@ -38,10 +40,10 @@ module Locomotive
|
||||
|
||||
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
|
||||
@ -56,6 +58,15 @@ module Locomotive
|
||||
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
|
||||
|
||||
end
|
||||
|
49
lib/locomotive/liquid/tags/editable/content.rb
Normal file
49
lib/locomotive/liquid/tags/editable/content.rb
Normal file
@ -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
|
@ -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;
|
||||
|
@ -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; }
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
BIN
spec/fixtures/themes/default.zip
vendored
BIN
spec/fixtures/themes/default.zip
vendored
Binary file not shown.
@ -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
|
||||
|
97
spec/lib/locomotive/liquid/tags/editable/content_spec.rb
Normal file
97
spec/lib/locomotive/liquid/tags/editable/content_spec.rb
Normal file
@ -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
|
Loading…
Reference in New Issue
Block a user