Merge branch 'multi-level-sub_menu' of https://github.com/dirkkelly/locomotive into dirkkelly-multi-level-sub_menu

This commit is contained in:
did 2011-03-07 21:49:01 +01:00
commit c3cc358934
24 changed files with 259 additions and 51 deletions

View File

@ -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'

View File

@ -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!

View File

@ -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'

View File

@ -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 ##

View File

@ -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

View File

@ -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

View File

@ -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:

View File

@ -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:

View File

@ -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:

View File

@ -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'

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View 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

View File

@ -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;

View File

@ -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; }

View File

@ -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;
}
}

View File

@ -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

Binary file not shown.

View File

@ -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

View 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