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.
Files changed (50) hide show
  1. data/Gemfile +6 -5
  2. data/app/assets/javascripts/locomotive/inline_editor.js.coffee +1 -1
  3. data/app/assets/javascripts/locomotive/models/custom_field_select_option.js.coffee +3 -0
  4. data/app/assets/javascripts/locomotive/models/site.js.coffee +2 -1
  5. data/app/assets/javascripts/locomotive/views/content_entries/_form_view.js.coffee +27 -0
  6. data/app/assets/javascripts/locomotive/views/content_entries/_popup_form_view.js.coffee +7 -1
  7. data/app/assets/javascripts/locomotive/views/content_types/_form_view.js.coffee +4 -1
  8. data/app/assets/javascripts/locomotive/views/content_types/select_options_view.js.coffee +5 -1
  9. data/app/assets/javascripts/locomotive/views/inline_editor/toolbar_view.js.coffee +0 -2
  10. data/app/assets/javascripts/locomotive/views/pages/edit_view.js.coffee +3 -0
  11. data/app/assets/javascripts/locomotive/views/shared/fields/select_view.js.coffee +83 -0
  12. data/app/assets/stylesheets/locomotive/backoffice/application.css.scss +13 -0
  13. data/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.scss +25 -95
  14. data/app/assets/stylesheets/locomotive/inline_editor/toolbar.css.scss +1 -1
  15. data/app/assets/stylesheets/locomotive/shared/_helpers.css.scss +98 -0
  16. data/app/assets/stylesheets/locomotive/shared/common.css.scss +1 -1
  17. data/app/controllers/locomotive/current_site_controller.rb +6 -0
  18. data/app/helpers/locomotive/base_helper.rb +14 -0
  19. data/app/models/locomotive/content_entry.rb +42 -3
  20. data/app/models/locomotive/content_type.rb +6 -1
  21. data/app/models/locomotive/editable_element.rb +9 -0
  22. data/app/models/locomotive/editable_short_text.rb +9 -0
  23. data/app/models/locomotive/extensions/page/tree.rb +3 -4
  24. data/app/presenters/locomotive/content_entry_presenter.rb +4 -2
  25. data/app/views/locomotive/content_entries/_form.html.haml +2 -1
  26. data/app/views/locomotive/custom_fields/_form.html.haml +4 -18
  27. data/app/views/locomotive/custom_fields/_select_templates.html.haml +16 -0
  28. data/app/views/locomotive/custom_fields/types/_has_many.html.haml +3 -3
  29. data/app/views/locomotive/custom_fields/types/_select.html.haml +30 -1
  30. data/app/views/locomotive/notifications/new_content_entry.html.haml +3 -1
  31. data/app/views/locomotive/public/pages/show_toolbar.html.haml +4 -1
  32. data/config/locales/admin_ui.de.yml +3 -4
  33. data/config/locales/admin_ui.en.yml +1 -1
  34. data/config/locales/admin_ui.fr.yml +4 -4
  35. data/config/locales/admin_ui.nb.yml +0 -1
  36. data/config/locales/admin_ui.ru.yml +0 -1
  37. data/lib/locomotive/custom_fields.rb +12 -0
  38. data/lib/locomotive/liquid/drops/content_entry.rb +2 -2
  39. data/lib/locomotive/liquid/drops/content_types.rb +2 -0
  40. data/lib/locomotive/liquid/tags/editable/short_text.rb +4 -0
  41. data/lib/locomotive/liquid/tags/inline_editor.rb +14 -12
  42. data/lib/locomotive/middlewares/inline_editor.rb +20 -15
  43. data/lib/locomotive/render.rb +94 -40
  44. data/lib/locomotive/version.rb +1 -1
  45. data/vendor/assets/javascripts/locomotive/form_submit_notification.js +1 -1
  46. data/vendor/assets/javascripts/locomotive/toggle.js +9 -8
  47. data/vendor/assets/stylesheets/locomotive/liquid_mode.css +3 -5
  48. data/vendor/assets/stylesheets/locomotive/toggle.css.scss +33 -10
  49. metadata +13 -12
  50. data/app/assets/stylesheets/locomotive/backoffice/formtastic_changes.css.css +0 -0
@@ -96,7 +96,7 @@
96
96
  .editing-mode {
97
97
  .toggleSwitch {
98
98
  position: relative;
99
- top: 0px;
99
+ top: 5px;
100
100
  margin-left: 3px;
101
101
  display: inline-block;
102
102
 
@@ -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
+ }
@@ -31,4 +31,4 @@ p.no-items {
31
31
  @include hover-link;
32
32
  color: #ff2900;
33
33
  }
34
- }
34
+ }
@@ -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 = :_position.send(matcher)
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 => self._position).order_by([order_by]).limit(1).first
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( attrs = [] )
85
- self.children.minimal_attributes( attrs )
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.attributes['depth'] = self.depth
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, :meta_keywords, :meta_description, :file_custom_fields, :has_many_custom_fields, :many_to_many_custom_fields, :to => :source
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
- = g.check_box :required, :class => 'required', :'data-on-label' => t('.required'), :'data-off-label' => t('.optional')
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
- %script{ :type => 'text/html', :id => 'select_options_list' }
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') } }