new editable element for a page: the control
This commit is contained in:
parent
ab5a4755b8
commit
b3b0a5ac16
@ -32,6 +32,9 @@ class Locomotive.Views.EditableElements.EditAllView extends Backbone.View
|
||||
view.model = @collection.get(view.model.get('id'))
|
||||
view.refresh()
|
||||
|
||||
unbind_model: ->
|
||||
_.each @_editable_elements_views, (view) => Backbone.ModelBinding.unbind(view)
|
||||
|
||||
render_elements: ->
|
||||
index = 0
|
||||
|
||||
@ -45,6 +48,7 @@ class Locomotive.Views.EditableElements.EditAllView extends Backbone.View
|
||||
when 'EditableShortText' then Locomotive.Views.EditableElements.ShortTextView
|
||||
when 'EditableLongText' then Locomotive.Views.EditableElements.LongTextView
|
||||
when 'EditableFile' then Locomotive.Views.EditableElements.FileView
|
||||
when 'EditableControl' then Locomotive.Views.EditableElements.ControlView
|
||||
|
||||
view = new view_class(model: element)
|
||||
@$("#block-#{block.index} > fieldset > ol").append(view.render().el)
|
||||
|
@ -90,6 +90,7 @@ class Locomotive.Views.Pages.FormView extends Locomotive.Views.Shared.FormView
|
||||
|
||||
# Just re-connect the model and the views (+ redraw the file fields)
|
||||
refresh_editable_elements: ->
|
||||
@editable_elements_view.unbind_model()
|
||||
@editable_elements_view.collection = @model.get('editable_elements')
|
||||
@editable_elements_view.refresh()
|
||||
|
||||
|
@ -1,97 +1,101 @@
|
||||
module Locomotive
|
||||
class EditableElement
|
||||
|
||||
include ::Mongoid::Document
|
||||
include ::Mongoid::Document
|
||||
|
||||
## fields ##
|
||||
field :slug
|
||||
field :block
|
||||
field :hint
|
||||
field :priority, :type => Integer, :default => 0
|
||||
field :fixed, :type => Boolean, :default => false
|
||||
field :disabled, :type => Boolean, :default => false, :localize => true
|
||||
field :from_parent, :type => Boolean, :default => false
|
||||
field :locales, :type => Array, :default => []
|
||||
## fields ##
|
||||
field :slug
|
||||
field :block
|
||||
field :hint
|
||||
field :priority, :type => Integer, :default => 0
|
||||
field :fixed, :type => Boolean, :default => false
|
||||
field :disabled, :type => Boolean, :default => false, :localize => true
|
||||
field :from_parent, :type => Boolean, :default => false
|
||||
field :locales, :type => Array, :default => []
|
||||
|
||||
## associations ##
|
||||
embedded_in :page, :class_name => 'Locomotive::Page', :inverse_of => :editable_elements
|
||||
## associations ##
|
||||
embedded_in :page, :class_name => 'Locomotive::Page', :inverse_of => :editable_elements
|
||||
|
||||
## validations ##
|
||||
validates_presence_of :slug
|
||||
## validations ##
|
||||
validates_presence_of :slug
|
||||
|
||||
## callbacks ##
|
||||
after_save :propagate_content, :if => :fixed?
|
||||
## callbacks ##
|
||||
after_save :propagate_content, :if => :fixed?
|
||||
|
||||
## scopes ##
|
||||
scope :by_priority, :order_by => [[:priority, :desc]]
|
||||
## scopes ##
|
||||
scope :by_priority, :order_by => [[:priority, :desc]]
|
||||
|
||||
## methods ##
|
||||
## methods ##
|
||||
|
||||
def disabled?
|
||||
!!self.disabled # the original method does not work quite well with the localization
|
||||
end
|
||||
def disabled?
|
||||
!!self.disabled # the original method does not work quite well with the localization
|
||||
end
|
||||
|
||||
# Determines if the current element can be edited in the back-office
|
||||
#
|
||||
def editable?
|
||||
!self.disabled? && self.locales.include?(::Mongoid::Fields::I18n.locale.to_s) && (!self.fixed? || !self.from_parent?)
|
||||
end
|
||||
# Determines if the current element can be edited in the back-office
|
||||
#
|
||||
def editable?
|
||||
!self.disabled? && self.locales.include?(::Mongoid::Fields::I18n.locale.to_s) && (!self.fixed? || !self.from_parent?)
|
||||
end
|
||||
|
||||
def _run_rearrange_callbacks
|
||||
# callback from page/tree. not needed in the editable elements
|
||||
end
|
||||
def _run_rearrange_callbacks
|
||||
# callback from page/tree. not needed in the editable elements
|
||||
end
|
||||
|
||||
def default_content?
|
||||
# needs to be overridden for each kind of elements
|
||||
true
|
||||
end
|
||||
def default_content?
|
||||
# needs to be overridden for each kind of elements
|
||||
true
|
||||
end
|
||||
|
||||
# Copy attributes extracted from the corresponding Liquid tag
|
||||
# Each editable element overrides this method.
|
||||
#
|
||||
# @param [ Hash ] attributes The up-to-date attributes
|
||||
#
|
||||
def copy_attributes(attributes)
|
||||
self.attributes = attributes
|
||||
end
|
||||
# Copy attributes extracted from the corresponding Liquid tag
|
||||
# Each editable element overrides this method.
|
||||
#
|
||||
# @param [ Hash ] attributes The up-to-date attributes
|
||||
#
|
||||
def copy_attributes(attributes)
|
||||
self.attributes = attributes
|
||||
end
|
||||
|
||||
# Copy attributes from an existing editable element coming
|
||||
# from the parent page. Each editable element may or not
|
||||
# override this method. The source element is a new record.
|
||||
#
|
||||
# @param [ EditableElement] el The source element
|
||||
#
|
||||
def copy_attributes_from(el)
|
||||
self.attributes = el.attributes.reject { |attr| !%w(slug block hint priority fixed disabled locales from_parent).include?(attr) }
|
||||
self.from_parent = true
|
||||
end
|
||||
# Copy attributes from an existing editable element coming
|
||||
# from the parent page. Each editable element may or not
|
||||
# override this method. The source element is a new record.
|
||||
#
|
||||
# @param [ EditableElement] el The source element
|
||||
#
|
||||
def copy_attributes_from(el)
|
||||
self.attributes = el.attributes.reject { |attr| !%w(slug block hint priority fixed disabled locales from_parent).include?(attr) }
|
||||
self.from_parent = true
|
||||
end
|
||||
|
||||
# Make sure the current locale is added to the list
|
||||
# of locales for the current element so that we know
|
||||
# in which languages the element was translated.
|
||||
#
|
||||
def add_current_locale
|
||||
locale = ::Mongoid::Fields::I18n.locale.to_s
|
||||
self.locales << locale unless self.locales.include?(locale)
|
||||
end
|
||||
# Make sure the current locale is added to the list
|
||||
# of locales for the current element so that we know
|
||||
# in which languages the element was translated.
|
||||
#
|
||||
def add_current_locale
|
||||
locale = ::Mongoid::Fields::I18n.locale.to_s
|
||||
self.locales << locale unless self.locales.include?(locale)
|
||||
end
|
||||
|
||||
protected
|
||||
protected
|
||||
|
||||
def _selector
|
||||
locale = ::Mongoid::Fields::I18n.locale
|
||||
{
|
||||
'site_id' => self.page.site_id,
|
||||
"template_dependencies.#{locale}" => { '$in' => [self.page._id] },
|
||||
'editable_elements.fixed' => true,
|
||||
'editable_elements.block' => self.block,
|
||||
'editable_elements.slug' => self.slug
|
||||
}
|
||||
end
|
||||
def _selector
|
||||
locale = ::Mongoid::Fields::I18n.locale
|
||||
{
|
||||
'site_id' => self.page.site_id,
|
||||
"template_dependencies.#{locale}" => { '$in' => [self.page._id] },
|
||||
'editable_elements.fixed' => true,
|
||||
'editable_elements.block' => self.block,
|
||||
'editable_elements.slug' => self.slug
|
||||
}
|
||||
end
|
||||
|
||||
def propagate_content
|
||||
# needs to be overridden for each kind of elements (file, short text, ...etc)
|
||||
true
|
||||
end
|
||||
# Update the value (or content) of the elements matching the same block/slug
|
||||
# as the current element in all the pages inheriting from the current page.
|
||||
# This method is called only if the element has the "fixed" property set to true.
|
||||
# It also needs to be overridden for each kind of elements (file, short text, ...etc)
|
||||
#
|
||||
def propagate_content
|
||||
true
|
||||
end
|
||||
|
||||
end
|
||||
end
|
@ -1,12 +1,15 @@
|
||||
module Locomotive
|
||||
class EditableFile < EditableElement
|
||||
|
||||
## behaviours ##
|
||||
mount_uploader 'source', EditableFileUploader
|
||||
|
||||
replace_field 'source', ::String, true
|
||||
|
||||
## fields ##
|
||||
field :default_source_url, :localize => true
|
||||
|
||||
## callbacks ##
|
||||
after_save :propagate_content
|
||||
|
||||
## methods ##
|
||||
|
@ -1,6 +1,8 @@
|
||||
module Locomotive
|
||||
class EditableLongText < EditableShortText
|
||||
|
||||
## methods ##
|
||||
|
||||
def as_json(options = {})
|
||||
Locomotive::EditableLongTextPresenter.new(self).as_json
|
||||
end
|
||||
|
@ -26,7 +26,7 @@ module Locomotive
|
||||
end
|
||||
|
||||
def enabled_editable_elements
|
||||
self.editable_elements.by_priority.find_all(&:editable?) # { |el| !el.editable? }
|
||||
self.editable_elements.by_priority.find_all(&:editable?)
|
||||
end
|
||||
|
||||
def editable_elements_grouped_by_blocks
|
||||
@ -70,7 +70,7 @@ module Locomotive
|
||||
new_el = self.editable_elements.build({}, el.class)
|
||||
new_el.copy_attributes_from(el)
|
||||
else
|
||||
existing_el.disabled = false # = { :disabled => false }
|
||||
existing_el.disabled = false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -49,4 +49,16 @@
|
||||
%p.inline-hints {{hint}}
|
||||
{{/if}}
|
||||
|
||||
= hidden_field_tag 'page[editable_elements_attributes][{{index}}][id]', '{{id}}', :id => 'page_editable_elements_attributes_{{index}}_id'
|
||||
|
||||
%script{ :type => 'text/html', :id => 'editable_control_input' }
|
||||
|
||||
%label{ :for => 'page_editable_elements_attributes_{{index}}_content' } {{label}}
|
||||
|
||||
= select_tag 'page[editable_elements_attributes][{{index}}][content]', raw('{{#each options}}<option value="{{this.value}}" {{#if this.selected}}selected="selected"{{/if}}>{{this.text}}</option>{{/each}}'), :id => 'page_editable_elements_attributes_{{index}}_content', :class => 'content'
|
||||
|
||||
{{#if hint}}
|
||||
%p.inline-hints {{hint}}
|
||||
{{/if}}
|
||||
|
||||
= hidden_field_tag 'page[editable_elements_attributes][{{index}}][id]', '{{id}}', :id => 'page_editable_elements_attributes_{{index}}_id'
|
13
doc/TODO
13
doc/TODO
@ -20,7 +20,7 @@ x edit my site
|
||||
x domains
|
||||
x roles
|
||||
x save
|
||||
- fix other sections
|
||||
x fix other sections
|
||||
x edit my account
|
||||
x create a new site
|
||||
x create a new accounts
|
||||
@ -32,7 +32,7 @@ x edit my site
|
||||
x import/export
|
||||
x export
|
||||
x site picker
|
||||
- content types
|
||||
x content types
|
||||
x move content instances into their own collection
|
||||
x manage custom_fields
|
||||
x automatic name
|
||||
@ -65,7 +65,7 @@ x edit my site
|
||||
x bug text formatting
|
||||
x custom_fields: use the appropriate icon to drag select options
|
||||
x bug ui with contents popup
|
||||
- use list_or_group_entries instead of ordered_entries
|
||||
x use list_or_group_entries instead of ordered_entries
|
||||
x i18n
|
||||
x add locales a site responds to
|
||||
x locale switcher
|
||||
@ -90,7 +90,7 @@ x i18n
|
||||
x deployment
|
||||
x fix integration problems
|
||||
x pre-compile assets
|
||||
- API
|
||||
x API
|
||||
x authentication from a token + controller to deliver a token
|
||||
x api routes
|
||||
x change api location
|
||||
@ -115,6 +115,8 @@ x heroku module for locomotive
|
||||
x remove the import / export scripts
|
||||
x remove the cross domain authentication (use auth_token instead)
|
||||
- where to put Locomotive::InlineEditorMiddleware ?
|
||||
x global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating)
|
||||
x override sort for contents
|
||||
|
||||
- bugs / ui tweaks
|
||||
x unable to toggle the "required" check_boxes for content types
|
||||
@ -139,7 +141,6 @@ BACKLOG:
|
||||
- html view in the aloha popup
|
||||
- editable elements should wrap a tag: div, h1, ...etc (default span)
|
||||
- edit images (upload new ones, ...etc) => wait for aloha or send them an email ?
|
||||
- global regions: keyword in editable element (http://www.mongodb.org/display/DOCS/Updating)
|
||||
- cucumber features for admin pages (in progress)
|
||||
(- duostack/doutcloud version)
|
||||
- write my first tutorial about locomotive
|
||||
@ -151,7 +152,7 @@ BACKLOG:
|
||||
- sync data
|
||||
- import only theme assets
|
||||
- endless pagination
|
||||
- override sort for contents
|
||||
|
||||
- tooltip to explain the difference between 1.) Admin 2.) Author 3.) Designer?
|
||||
- [bushido] guiders / welcome page / devise cas authentication (SSO)
|
||||
|
||||
|
@ -125,7 +125,7 @@ Scenario: Insert editable files
|
||||
"""
|
||||
My application file is /default.pdf
|
||||
"""
|
||||
|
||||
|
||||
Scenario: Render liquid variable used as default editable file name
|
||||
Given a page named "hello-world" with the template:
|
||||
"""
|
||||
@ -135,7 +135,17 @@ Scenario: Render liquid variable used as default editable file name
|
||||
When I view the rendered page at "/hello-world"
|
||||
Then the rendered output should look like:
|
||||
"""
|
||||
|
||||
|
||||
My application file is /different-default.pdf
|
||||
"""
|
||||
|
||||
Scenario: Simple select element
|
||||
Given a page named "hello-world" with the template:
|
||||
"""
|
||||
{% block menuecontent %}{% editable_control 'menueposition', options: 'top=Top of the Page,bottom=Bottom of the Page' %}bottom{% endeditable_control %}{% endblock %}
|
||||
"""
|
||||
When I view the rendered page at "/hello-world"
|
||||
Then the rendered output should look like:
|
||||
"""
|
||||
bottom
|
||||
"""
|
@ -1,5 +1,5 @@
|
||||
When /^I follow image link "([^"]*)"$/ do |img_alt|
|
||||
find(:xpath, "//img[@alt = '#{img_alt}']/parent::a").click()
|
||||
find(:xpath, "//img[@alt = '#{img_alt}']/parent::a").click()
|
||||
end
|
||||
|
||||
Then /^I should get a download with the filename "([^\"]*)"$/ do |filename|
|
||||
@ -31,3 +31,11 @@ Then /^"([^"]*)" should( not)? be an option for "([^"]*)"(?: within "([^\"]*)")?
|
||||
field_labeled(field).first(:xpath, ".//option[text() = '#{value}']").send(expectation, be_present)
|
||||
end
|
||||
end
|
||||
|
||||
Then /^"([^"]*)" should be selected for "([^"]*)"$/ do |value, field|
|
||||
assert page.has_xpath?("//select[@name='#{field}' and option[@selected and contains(text(), '#{value}')]]")
|
||||
end
|
||||
|
||||
When /^I reload the page$/ do
|
||||
visit current_path
|
||||
end
|
@ -2,4 +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'
|
||||
require 'locomotive/liquid/tags/editable/control'
|
@ -1,49 +0,0 @@
|
||||
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
|
@ -6,8 +6,6 @@ module Locomotive
|
||||
|
||||
protected
|
||||
|
||||
protected
|
||||
|
||||
def default_element_attributes
|
||||
if @nodelist.first.is_a?(String)
|
||||
super.merge(:default_source_url => @nodelist.first.try(:to_s))
|
||||
|
@ -1,97 +0,0 @@
|
||||
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
|
||||
Locomotive::EditableElement.any_instance.stubs(:content).returns("test string")
|
||||
end
|
||||
|
||||
context 'inheriting from a parent' do
|
||||
|
||||
before :each do
|
||||
@parent = FactoryGirl.build(:page)
|
||||
@child = FactoryGirl.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 = FactoryGirl.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