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.
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') } }