locomotive_cms 2.0.0.rc9 → 2.0.0.rc10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/Gemfile +6 -5
- data/app/assets/javascripts/locomotive/inline_editor.js.coffee +1 -1
- data/app/assets/javascripts/locomotive/models/custom_field_select_option.js.coffee +3 -0
- data/app/assets/javascripts/locomotive/models/site.js.coffee +2 -1
- data/app/assets/javascripts/locomotive/views/content_entries/_form_view.js.coffee +27 -0
- data/app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee +7 -1
- data/app/assets/javascripts/locomotive/views/content_types/_form_view.js.coffee +4 -1
- data/app/assets/javascripts/locomotive/views/content_types/select_options_view.js.coffee +5 -1
- data/app/assets/javascripts/locomotive/views/inline_editor/toolbar_view.js.coffee +0 -2
- data/app/assets/javascripts/locomotive/views/pages/edit_view.js.coffee +3 -0
- data/app/assets/javascripts/locomotive/views/shared/fields/select_view.js.coffee +83 -0
- data/app/assets/stylesheets/locomotive/backoffice/application.css.scss +13 -0
- data/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.scss +25 -95
- data/app/assets/stylesheets/locomotive/inline_editor/toolbar.css.scss +1 -1
- data/app/assets/stylesheets/locomotive/shared/_helpers.css.scss +98 -0
- data/app/assets/stylesheets/locomotive/shared/common.css.scss +1 -1
- data/app/controllers/locomotive/current_site_controller.rb +6 -0
- data/app/helpers/locomotive/base_helper.rb +14 -0
- data/app/models/locomotive/content_entry.rb +42 -3
- data/app/models/locomotive/content_type.rb +6 -1
- data/app/models/locomotive/editable_element.rb +9 -0
- data/app/models/locomotive/editable_short_text.rb +9 -0
- data/app/models/locomotive/extensions/page/tree.rb +3 -4
- data/app/presenters/locomotive/content_entry_presenter.rb +4 -2
- data/app/views/locomotive/content_entries/_form.html.haml +2 -1
- data/app/views/locomotive/custom_fields/_form.html.haml +4 -18
- data/app/views/locomotive/custom_fields/_select_templates.html.haml +16 -0
- data/app/views/locomotive/custom_fields/types/_has_many.html.haml +3 -3
- data/app/views/locomotive/custom_fields/types/_select.html.haml +30 -1
- data/app/views/locomotive/notifications/new_content_entry.html.haml +3 -1
- data/app/views/locomotive/public/pages/show_toolbar.html.haml +4 -1
- data/config/locales/admin_ui.de.yml +3 -4
- data/config/locales/admin_ui.en.yml +1 -1
- data/config/locales/admin_ui.fr.yml +4 -4
- data/config/locales/admin_ui.nb.yml +0 -1
- data/config/locales/admin_ui.ru.yml +0 -1
- data/lib/locomotive/custom_fields.rb +12 -0
- data/lib/locomotive/liquid/drops/content_entry.rb +2 -2
- data/lib/locomotive/liquid/drops/content_types.rb +2 -0
- data/lib/locomotive/liquid/tags/editable/short_text.rb +4 -0
- data/lib/locomotive/liquid/tags/inline_editor.rb +14 -12
- data/lib/locomotive/middlewares/inline_editor.rb +20 -15
- data/lib/locomotive/render.rb +94 -40
- data/lib/locomotive/version.rb +1 -1
- data/vendor/assets/javascripts/locomotive/form_submit_notification.js +1 -1
- data/vendor/assets/javascripts/locomotive/toggle.js +9 -8
- data/vendor/assets/stylesheets/locomotive/liquid_mode.css +3 -5
- data/vendor/assets/stylesheets/locomotive/toggle.css.scss +33 -10
- metadata +13 -12
- data/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.css +0 -0
@@ -70,3 +70,101 @@
|
|
70
70
|
color: #fff;
|
71
71
|
@include single-text-shadow(rgba(0, 0, 0, 0.6), 0px, -1px, 0px);
|
72
72
|
}
|
73
|
+
|
74
|
+
@mixin tiny-buttons-group {
|
75
|
+
a.add {
|
76
|
+
@include gray-button;
|
77
|
+
|
78
|
+
padding-left: 10px;
|
79
|
+
}
|
80
|
+
|
81
|
+
a.edit, a.remove, a.toggle, a.drag {
|
82
|
+
display: inline-block;
|
83
|
+
width: 16px;
|
84
|
+
height: 16px;
|
85
|
+
|
86
|
+
position: relative;
|
87
|
+
top: 4px;
|
88
|
+
|
89
|
+
outline: none;
|
90
|
+
text-indent: -9999px;
|
91
|
+
|
92
|
+
&.edit {
|
93
|
+
background: transparent image-url("locomotive/list/icons/pencil_off.png") repeat 0 0;
|
94
|
+
&:hover {
|
95
|
+
background-image: image-url("locomotive/list/icons/pencil.png");
|
96
|
+
}
|
97
|
+
}
|
98
|
+
|
99
|
+
&.remove {
|
100
|
+
background: transparent image-url("locomotive/list/icons/trash_off.png") repeat 0 0;
|
101
|
+
&:hover {
|
102
|
+
background-image: image-url("locomotive/list/icons/trash.png");
|
103
|
+
}
|
104
|
+
}
|
105
|
+
|
106
|
+
&.toggle {
|
107
|
+
background: transparent image-url("locomotive/list/icons/toggle_off.png") repeat 0 0;
|
108
|
+
&:hover {
|
109
|
+
background-image: image-url("locomotive/list/icons/toggle.png");
|
110
|
+
}
|
111
|
+
&.open {
|
112
|
+
@include rotate(180deg);
|
113
|
+
}
|
114
|
+
@include single-transition(transform, 0.5s);
|
115
|
+
}
|
116
|
+
|
117
|
+
&.drag {
|
118
|
+
cursor: move;
|
119
|
+
background: transparent image-url("locomotive/list/icons/move_off.png") repeat 0 0;
|
120
|
+
&:hover {
|
121
|
+
background-image: image-url("locomotive/list/icons/move.png");
|
122
|
+
}
|
123
|
+
}
|
124
|
+
}
|
125
|
+
}
|
126
|
+
|
127
|
+
@mixin select-options-edit-style {
|
128
|
+
.list {
|
129
|
+
line-height: auto;
|
130
|
+
|
131
|
+
ul, li, > span.actions {
|
132
|
+
float: left;
|
133
|
+
}
|
134
|
+
|
135
|
+
ul {
|
136
|
+
width: 558px;
|
137
|
+
min-height: 34px;
|
138
|
+
|
139
|
+
li.entry {
|
140
|
+
background: #c2e0f0;
|
141
|
+
@include border-radius(2px);
|
142
|
+
@include box-shadow(rgba(0, 0, 0, 0.2) 0px 1px 0px 0px);
|
143
|
+
|
144
|
+
padding: 0 8px 0 8px;
|
145
|
+
margin: 2px 10px 8px 0;
|
146
|
+
height: auto;
|
147
|
+
|
148
|
+
color: #29739b;
|
149
|
+
line-height: 24px;
|
150
|
+
|
151
|
+
span.name {
|
152
|
+
cursor: pointer;
|
153
|
+
@include text-shadow(#fff 0px 1px 1px);
|
154
|
+
}
|
155
|
+
|
156
|
+
span.actions {
|
157
|
+
position: static;
|
158
|
+
margin-left: 8px;
|
159
|
+
line-height: 24px;
|
160
|
+
}
|
161
|
+
}
|
162
|
+
} // .list ul
|
163
|
+
|
164
|
+
> span.actions {
|
165
|
+
position: static;
|
166
|
+
margin-left: 15px;
|
167
|
+
line-height: 24px;
|
168
|
+
} // .list .actions
|
169
|
+
}
|
170
|
+
}
|
@@ -11,6 +11,8 @@ module Locomotive
|
|
11
11
|
|
12
12
|
before_filter :filter_attributes
|
13
13
|
|
14
|
+
before_filter :ensure_domains_list, :only => :update
|
15
|
+
|
14
16
|
respond_to :json, :only => :update
|
15
17
|
|
16
18
|
def edit
|
@@ -40,5 +42,9 @@ module Locomotive
|
|
40
42
|
end
|
41
43
|
end
|
42
44
|
|
45
|
+
def ensure_domains_list
|
46
|
+
params[:site][:domains] = [] unless params[:site][:domains]
|
47
|
+
end
|
48
|
+
|
43
49
|
end
|
44
50
|
end
|
@@ -14,6 +14,20 @@ module Locomotive
|
|
14
14
|
resource.persisted? && resource.errors.empty?
|
15
15
|
end
|
16
16
|
|
17
|
+
# Execute the code only once during the request time. It avoids duplicated
|
18
|
+
# dom elements in the rendered rails page.
|
19
|
+
#
|
20
|
+
# @param [ String / Symbol ] label Unique identifier of the block
|
21
|
+
#
|
22
|
+
def required_once(label, &block)
|
23
|
+
symbol = :"@block_#{label.to_s.underscore}"
|
24
|
+
|
25
|
+
if instance_variable_get(symbol).blank?
|
26
|
+
yield
|
27
|
+
instance_variable_set(symbol, true)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
17
31
|
def submenu_entry(name, url, options = {}, &block)
|
18
32
|
default_options = { :i18n => true, :css => name.dasherize.downcase }
|
19
33
|
default_options.merge!(options)
|
@@ -38,6 +38,13 @@ module Locomotive
|
|
38
38
|
alias :_permalink :_slug
|
39
39
|
alias :_permalink= :_slug=
|
40
40
|
|
41
|
+
# Any content entry owns a label property which is used in the back-office UI
|
42
|
+
# to represent it. The field used as the label is defined within the parent content type.
|
43
|
+
#
|
44
|
+
# @param [ Object ] (optional) The content type to avoid to run another MongoDB and useless query.
|
45
|
+
#
|
46
|
+
# @return [ String ] The "label" of the content entry
|
47
|
+
#
|
41
48
|
def _label(type = nil)
|
42
49
|
value = if self._label_field_name
|
43
50
|
self.send(self._label_field_name.to_sym)
|
@@ -50,6 +57,11 @@ module Locomotive
|
|
50
57
|
|
51
58
|
alias :to_label :_label
|
52
59
|
|
60
|
+
# Tells if the content entry has been translated or not.
|
61
|
+
# It just checks if the field used for the label has been translated.
|
62
|
+
#
|
63
|
+
# @return [ Boolean ] True if translated, false otherwise
|
64
|
+
#
|
53
65
|
def translated?
|
54
66
|
if self.respond_to?(:"#{self._label_field_name}_translations")
|
55
67
|
self.send(:"#{self._label_field_name}_translations").key?(::Mongoid::Fields::I18n.locale.to_s) #rescue false
|
@@ -58,18 +70,37 @@ module Locomotive
|
|
58
70
|
end
|
59
71
|
end
|
60
72
|
|
73
|
+
# Return the next content entry based on the order defined in the parent content type.
|
74
|
+
#
|
75
|
+
# @param [ Object ] The next content entry or nil if not found
|
76
|
+
#
|
61
77
|
def next
|
62
78
|
next_or_previous :gt
|
63
79
|
end
|
64
80
|
|
81
|
+
# Return the previous content entry based on the order defined in the parent content type.
|
82
|
+
#
|
83
|
+
# @param [ Object ] The previous content entry or nil if not found
|
84
|
+
#
|
65
85
|
def previous
|
66
86
|
next_or_previous :lt
|
67
87
|
end
|
68
88
|
|
89
|
+
# Find a content entry by its permalink
|
90
|
+
#
|
91
|
+
# @param [ String ] The permalink
|
92
|
+
#
|
93
|
+
# @return [ Object ] The content entry matching the permalink or nil if not found
|
94
|
+
#
|
69
95
|
def self.find_by_permalink(permalink)
|
70
96
|
self.where(:_slug => permalink).first
|
71
97
|
end
|
72
98
|
|
99
|
+
# Sort the content entries from an ordered array of content entry ids.
|
100
|
+
# Their new positions are persisted.
|
101
|
+
#
|
102
|
+
# @param [ Array ] The ordered array of ids
|
103
|
+
#
|
73
104
|
def self.sort_entries!(ids)
|
74
105
|
list = self.any_in(:_id => ids.map { |id| BSON::ObjectId.from_string(id.to_s) }).to_a
|
75
106
|
ids.each_with_index do |id, position|
|
@@ -93,11 +124,19 @@ module Locomotive
|
|
93
124
|
|
94
125
|
protected
|
95
126
|
|
127
|
+
# Retrieve the next or the previous entry following the order
|
128
|
+
# defined in the parent content type.
|
129
|
+
#
|
130
|
+
# @param [ Symbol ] :gt for the next element, :lt for the previous element
|
131
|
+
#
|
132
|
+
# @return [ Object ] The next or previous content entry or nil if none
|
133
|
+
#
|
96
134
|
def next_or_previous(matcher = :gt)
|
97
|
-
order_by = self.content_type.order_by_definition
|
98
|
-
criterion =
|
135
|
+
order_by = self.content_type.order_by_definition(matcher == :lt)
|
136
|
+
criterion = self.content_type.order_by_attribute.to_sym.send(matcher)
|
137
|
+
value = self.send(self.content_type.order_by_attribute.to_sym)
|
99
138
|
|
100
|
-
self.class.where(criterion =>
|
139
|
+
self.class.where(criterion => value).order_by([order_by]).limit(1).first
|
101
140
|
end
|
102
141
|
|
103
142
|
# Sets the slug of the instance by using the value of the highlighted field
|
@@ -50,8 +50,13 @@ module Locomotive
|
|
50
50
|
self.order_by == '_position'
|
51
51
|
end
|
52
52
|
|
53
|
-
def order_by_definition
|
53
|
+
def order_by_definition(reverse_order = false)
|
54
54
|
direction = self.order_manually? ? 'asc' : self.order_direction || 'asc'
|
55
|
+
|
56
|
+
if reverse_order
|
57
|
+
direction = (direction == 'asc' ? 'desc' : 'asc')
|
58
|
+
end
|
59
|
+
|
55
60
|
[order_by_attribute, direction]
|
56
61
|
end
|
57
62
|
|
@@ -75,6 +75,15 @@ module Locomotive
|
|
75
75
|
self.locales << locale unless self.locales.include?(locale)
|
76
76
|
end
|
77
77
|
|
78
|
+
# Set the content of the editable element with a default value
|
79
|
+
# only if the content has not already been modified by the user.
|
80
|
+
#
|
81
|
+
# @param [ String ] content The default content.
|
82
|
+
#
|
83
|
+
def content_from_default=(content)
|
84
|
+
# needs to be overridden for each kind of elements
|
85
|
+
end
|
86
|
+
|
78
87
|
protected
|
79
88
|
|
80
89
|
def _selector
|
@@ -8,6 +8,7 @@ module Locomotive
|
|
8
8
|
## methods ##
|
9
9
|
|
10
10
|
def content=(value)
|
11
|
+
return if value == self.content
|
11
12
|
self.add_current_locale
|
12
13
|
self.default_content = false unless self.new_record?
|
13
14
|
super
|
@@ -17,6 +18,14 @@ module Locomotive
|
|
17
18
|
!!self.default_content
|
18
19
|
end
|
19
20
|
|
21
|
+
def content_from_default=(content)
|
22
|
+
if self.default_content?
|
23
|
+
self.content_will_change!
|
24
|
+
self.attributes['content'] ||= {}
|
25
|
+
self.attributes['content'][::Mongoid::Fields::I18n.locale.to_s] = content
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
20
29
|
def copy_attributes_from(el)
|
21
30
|
super(el)
|
22
31
|
|
@@ -81,8 +81,8 @@ module Locomotive
|
|
81
81
|
#
|
82
82
|
# @return [ Array ] The children pages ordered by their position
|
83
83
|
#
|
84
|
-
def children_with_minimal_attributes(
|
85
|
-
self.children.minimal_attributes(
|
84
|
+
def children_with_minimal_attributes(attrs = [])
|
85
|
+
self.children.minimal_attributes(attrs)
|
86
86
|
end
|
87
87
|
|
88
88
|
# Assigns the new position of each child of this node.
|
@@ -115,8 +115,7 @@ module Locomotive
|
|
115
115
|
end
|
116
116
|
|
117
117
|
def persist_depth
|
118
|
-
self.
|
119
|
-
self.depth_will_change!
|
118
|
+
self.depth = self.parent_ids.count
|
120
119
|
end
|
121
120
|
|
122
121
|
end
|
@@ -1,7 +1,9 @@
|
|
1
1
|
module Locomotive
|
2
2
|
class ContentEntryPresenter < BasePresenter
|
3
3
|
|
4
|
-
delegate :_label, :_slug, :_position, :seo_title,
|
4
|
+
delegate :_label, :_slug, :_position, :seo_title,
|
5
|
+
:meta_keywords, :meta_description, :select_custom_fields,
|
6
|
+
:file_custom_fields, :has_many_custom_fields, :many_to_many_custom_fields, :to => :source
|
5
7
|
|
6
8
|
# Lists of all the attributes editable thru the html form for instance
|
7
9
|
#
|
@@ -31,7 +33,7 @@ module Locomotive
|
|
31
33
|
end
|
32
34
|
|
33
35
|
def included_methods
|
34
|
-
default_list = %w(_label _slug _position content_type_slug file_custom_fields has_many_custom_fields many_to_many_custom_fields safe_attributes)
|
36
|
+
default_list = %w(_label _slug _position content_type_slug select_custom_fields file_custom_fields has_many_custom_fields many_to_many_custom_fields safe_attributes)
|
35
37
|
default_list << 'errors' if !!self.options[:include_errors]
|
36
38
|
super + self.filtered_custom_fields_methods + default_list
|
37
39
|
end
|
@@ -3,7 +3,8 @@
|
|
3
3
|
|
4
4
|
- content_for :backbone_view_data do
|
5
5
|
:plain
|
6
|
-
content_entry: #{j @content_entry.to_json.html_safe}
|
6
|
+
content_entry: #{j @content_entry.to_json.html_safe},
|
7
|
+
content_type: #{j @content_type.to_json.html_safe}
|
7
8
|
|
8
9
|
= f.inputs :name => :attributes do
|
9
10
|
- @content_type.ordered_entries_custom_fields.each_with_index do |field, index|
|
@@ -30,7 +30,8 @@
|
|
30
30
|
= g.select :type, options_for_custom_field_type, {}, { :class => 'type' }
|
31
31
|
|
32
32
|
.required-input.col
|
33
|
-
=
|
33
|
+
%span= t('.required')
|
34
|
+
= g.check_box :required, :class => 'required'
|
34
35
|
|
35
36
|
%ol.nested{ :style => 'display: none' }
|
36
37
|
|
@@ -56,20 +57,5 @@
|
|
56
57
|
|
57
58
|
.clear
|
58
59
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
%ul{ :'data-prompt' => t('.select_options.ask_name') }
|
63
|
-
|
64
|
-
%span.actions
|
65
|
-
= link_to t('locomotive.buttons.new_item'), '#', :class => 'add'
|
66
|
-
|
67
|
-
|
68
|
-
%script{ :type => 'text/html', :id => 'select_option_entry' }
|
69
|
-
|
70
|
-
%li.entry
|
71
|
-
%span.name {{name}}
|
72
|
-
|
73
|
-
%span.actions
|
74
|
-
= link_to 'drag', '#', :class => 'drag'
|
75
|
-
= link_to 'x', '#', :class => 'remove', :data => { :confirm => t('locomotive.messages.confirm') }
|
60
|
+
/ Handlebar templates for managing the select options
|
61
|
+
= render 'locomotive/custom_fields/select_templates'
|
@@ -0,0 +1,16 @@
|
|
1
|
+
%script{ :type => 'text/html', :id => 'select_options_list' }
|
2
|
+
|
3
|
+
%ul{ :'data-prompt' => t('locomotive.custom_fields.form.select_options.ask_name') }
|
4
|
+
|
5
|
+
%span.actions
|
6
|
+
= link_to t('locomotive.buttons.new_item'), '#', :class => 'add'
|
7
|
+
|
8
|
+
|
9
|
+
%script{ :type => 'text/html', :id => 'select_option_entry' }
|
10
|
+
|
11
|
+
%li.entry
|
12
|
+
%span.name {{name}}
|
13
|
+
|
14
|
+
%span.actions
|
15
|
+
= link_to 'drag', '#', :class => 'drag'
|
16
|
+
= link_to 'x', '#', :class => 'remove', :data => { :confirm => t('locomotive.messages.confirm') }
|
@@ -47,7 +47,7 @@
|
|
47
47
|
%span.new-section= t('locomotive.content_entries.new.title', :type => name.capitalize).html_safe
|
48
48
|
%span.edit-section= t('locomotive.content_entries.edit.title', :type => name.capitalize).html_safe
|
49
49
|
|
50
|
-
= semantic_form_for new_target_content_entry, :as => :content_entry, :url => content_entries_url(target_content_type.slug), :html => { :multipart => true } do |form|
|
50
|
+
= semantic_form_for new_target_content_entry, :as => :content_entry, :url => content_entries_url(target_content_type.slug), :html => { :multipart => true, :data => { :sending_form_message => t('locomotive.messages.sending_form') } } do |form|
|
51
51
|
|
52
52
|
= form.inputs :name => :attributes do
|
53
53
|
- target_content_type.ordered_entries_custom_fields.each_with_index do |_field, index|
|
@@ -65,6 +65,6 @@
|
|
65
65
|
= link_to t('locomotive.buttons.close'), '#', :id => 'close-link'
|
66
66
|
|
67
67
|
.button-wrapper
|
68
|
-
%span.new-section= submit_tag t('locomotive.shared.form_actions.create')
|
69
|
-
%span.edit-section= submit_tag t('locomotive.shared.form_actions.update')
|
68
|
+
%span.new-section= submit_tag t('locomotive.shared.form_actions.create'), :'data-disable-with' => t('locomotive.shared.form_actions.disable_with')
|
69
|
+
%span.edit-section= submit_tag t('locomotive.shared.form_actions.update'), :'data-disable-with' => t('locomotive.shared.form_actions.disable_with')
|
70
70
|
|
@@ -2,4 +2,33 @@
|
|
2
2
|
:label => field.label,
|
3
3
|
:hint => field.hint,
|
4
4
|
:as => :select,
|
5
|
-
:collection => field.ordered_select_options.map { |option| [option.name, option.id] }
|
5
|
+
:collection => field.ordered_select_options.map { |option| [option.name, option.id] }
|
6
|
+
|
7
|
+
- content_for :head do
|
8
|
+
|
9
|
+
- required_once :select_options do
|
10
|
+
|
11
|
+
/ Handlebar templates for managing the select options
|
12
|
+
= render 'locomotive/custom_fields/select_templates'
|
13
|
+
|
14
|
+
%script{ :type => 'text/html', :id => 'edit_select_options_button' }
|
15
|
+
= link_to t('locomotive.content_entries.form.edit_select_options'), '#', :class => 'edit-options-button'
|
16
|
+
|
17
|
+
|
18
|
+
- content_for :foot do
|
19
|
+
|
20
|
+
- required_once :select_options_popup do
|
21
|
+
|
22
|
+
/ Handlebar template for the popup
|
23
|
+
%div{ :id => 'edit-select-option-entries', :style => 'display: none' }
|
24
|
+
|
25
|
+
%h2
|
26
|
+
= t('.manage_select_options_entries')
|
27
|
+
|
28
|
+
.placeholder{ :data => { :sending_form_message => t('locomotive.messages.sending_form') } }
|
29
|
+
|
30
|
+
.dialog-actions
|
31
|
+
= link_to t('locomotive.buttons.close'), '#', :id => 'close-link'
|
32
|
+
|
33
|
+
.button-wrapper
|
34
|
+
= submit_tag t('locomotive.shared.form_actions.update'), { :data => { :disable_with => t('locomotive.shared.form_actions.disable_with') } }
|