locomotive_cms 2.0.0.rc9 → 2.0.0.rc10
Sign up to get free protection for your applications and to get access to all the features.
- 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') } }
|