alchemy_cms 4.1.2 → 4.2.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (155) hide show
  1. checksums.yaml +4 -4
  2. data/.github/ISSUE_TEMPLATE/Bug_report.md +0 -5
  3. data/.rubocop.yml +4 -1
  4. data/.travis.yml +3 -3
  5. data/CHANGELOG.md +29 -10
  6. data/Gemfile +5 -7
  7. data/README.md +114 -62
  8. data/Rakefile +2 -2
  9. data/alchemy_cms.gemspec +5 -5
  10. data/app/assets/images/alchemy/icon.svg +1 -1
  11. data/app/assets/javascripts/alchemy/admin.js +2 -0
  12. data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +0 -24
  13. data/app/assets/javascripts/alchemy/alchemy.datepicker.js.coffee +11 -9
  14. data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +7 -24
  15. data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +9 -8
  16. data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +38 -0
  17. data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +2 -4
  18. data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +1 -1
  19. data/app/assets/stylesheets/alchemy/_mixins.scss +2 -1
  20. data/app/assets/stylesheets/alchemy/_variables.scss +7 -2
  21. data/app/assets/stylesheets/alchemy/admin.scss +2 -1
  22. data/app/assets/stylesheets/alchemy/archive.scss +18 -4
  23. data/app/assets/stylesheets/alchemy/buttons.scss +1 -1
  24. data/app/assets/stylesheets/alchemy/dialogs.scss +1 -1
  25. data/app/assets/stylesheets/alchemy/elements.scss +154 -90
  26. data/app/assets/stylesheets/alchemy/filter_field.scss +30 -0
  27. data/app/assets/stylesheets/alchemy/flatpickr.scss +839 -0
  28. data/app/assets/stylesheets/alchemy/forms.scss +5 -1
  29. data/app/assets/stylesheets/alchemy/frame.scss +6 -14
  30. data/app/assets/stylesheets/alchemy/navigation.scss +109 -98
  31. data/app/assets/stylesheets/alchemy/search.scss +11 -29
  32. data/app/assets/stylesheets/alchemy/tables.scss +0 -23
  33. data/app/assets/stylesheets/alchemy/tags.scss +4 -27
  34. data/app/assets/stylesheets/alchemy/toolbar.scss +12 -2
  35. data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +4 -33
  36. data/app/controllers/alchemy/admin/attachments_controller.rb +1 -12
  37. data/app/controllers/alchemy/admin/contents_controller.rb +2 -24
  38. data/app/controllers/alchemy/admin/elements_controller.rb +11 -49
  39. data/app/controllers/alchemy/admin/pages_controller.rb +1 -1
  40. data/app/controllers/alchemy/admin/pictures_controller.rb +1 -14
  41. data/app/controllers/alchemy/api/contents_controller.rb +1 -1
  42. data/app/controllers/alchemy/api/elements_controller.rb +1 -1
  43. data/app/controllers/alchemy/api/pages_controller.rb +1 -1
  44. data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +20 -0
  45. data/app/helpers/alchemy/admin/base_helper.rb +8 -18
  46. data/app/helpers/alchemy/admin/elements_helper.rb +55 -85
  47. data/app/helpers/alchemy/admin/pictures_helper.rb +0 -23
  48. data/app/helpers/alchemy/elements_helper.rb +80 -120
  49. data/app/helpers/alchemy/pages_helper.rb +5 -24
  50. data/app/models/alchemy/content.rb +0 -1
  51. data/app/models/alchemy/content/factory.rb +33 -57
  52. data/app/models/alchemy/element.rb +46 -66
  53. data/app/models/alchemy/element/element_contents.rb +2 -2
  54. data/app/models/alchemy/page.rb +34 -4
  55. data/app/models/alchemy/page/page_elements.rb +30 -122
  56. data/app/serializers/alchemy/attachment_serializer.rb +0 -2
  57. data/app/serializers/alchemy/content_serializer.rb +0 -2
  58. data/app/serializers/alchemy/element_serializer.rb +0 -3
  59. data/app/serializers/alchemy/essence_boolean_serializer.rb +0 -2
  60. data/app/serializers/alchemy/essence_date_serializer.rb +0 -2
  61. data/app/serializers/alchemy/essence_file_serializer.rb +0 -2
  62. data/app/serializers/alchemy/essence_html_serializer.rb +0 -2
  63. data/app/serializers/alchemy/essence_link_serializer.rb +0 -2
  64. data/app/serializers/alchemy/essence_picture_serializer.rb +0 -2
  65. data/app/serializers/alchemy/essence_richtext_serializer.rb +0 -2
  66. data/app/serializers/alchemy/essence_select_serializer.rb +0 -2
  67. data/app/serializers/alchemy/essence_text_serializer.rb +0 -2
  68. data/app/serializers/alchemy/legacy_element_serializer.rb +0 -3
  69. data/app/serializers/alchemy/page_serializer.rb +2 -8
  70. data/app/serializers/alchemy/page_tree_serializer.rb +1 -1
  71. data/app/serializers/alchemy/picture_serializer.rb +0 -2
  72. data/app/views/alchemy/admin/clipboard/index.html.erb +2 -2
  73. data/app/views/alchemy/admin/clipboard/insert.js.erb +9 -12
  74. data/app/views/alchemy/admin/contents/create.js.erb +4 -30
  75. data/app/views/alchemy/admin/elements/_element.html.erb +27 -12
  76. data/app/views/alchemy/admin/elements/_element_toolbar.html.erb +1 -1
  77. data/app/views/alchemy/admin/elements/_new_element_form.html.erb +0 -10
  78. data/app/views/alchemy/admin/elements/create.js.erb +12 -12
  79. data/app/views/alchemy/admin/elements/fold.js.erb +1 -1
  80. data/app/views/alchemy/admin/elements/index.html.erb +20 -19
  81. data/app/views/alchemy/admin/elements/publish.js.erb +5 -0
  82. data/app/views/alchemy/admin/elements/trash.js.erb +4 -1
  83. data/app/views/alchemy/admin/essence_pictures/assign.js.erb +0 -7
  84. data/app/views/alchemy/admin/essence_pictures/destroy.js.erb +0 -22
  85. data/app/views/alchemy/admin/pages/_publication_fields.html.erb +2 -4
  86. data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
  87. data/app/views/alchemy/admin/pages/index.html.erb +14 -10
  88. data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +1 -1
  89. data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +1 -1
  90. data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +7 -5
  91. data/app/views/alchemy/admin/partials/_routes.html.erb +0 -1
  92. data/app/views/alchemy/admin/partials/_search_form.html.erb +6 -4
  93. data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +6 -3
  94. data/app/views/alchemy/admin/pictures/index.html.erb +1 -1
  95. data/app/views/alchemy/admin/trash/index.html.erb +8 -7
  96. data/app/views/alchemy/elements/_editor_not_found.html.erb +1 -1
  97. data/app/views/alchemy/essences/_essence_picture_editor.html.erb +4 -19
  98. data/app/views/layouts/alchemy/admin.html.erb +1 -0
  99. data/bin/rspec +0 -5
  100. data/config/alchemy/config.yml +3 -0
  101. data/config/brakeman.ignore +1 -1
  102. data/config/locales/alchemy.en.yml +6 -12
  103. data/config/routes.rb +1 -5
  104. data/db/migrate/20180226123013_alchemy_four_point_zero.rb +0 -29
  105. data/db/migrate/20180519204655_add_fixed_to_alchemy_elements.rb +6 -0
  106. data/lib/alchemy/admin/locale.rb +1 -1
  107. data/lib/alchemy/cache_digests/template_tracker.rb +4 -27
  108. data/lib/alchemy/elements_finder.rb +111 -0
  109. data/lib/alchemy/errors.rb +0 -4
  110. data/lib/alchemy/modules.rb +49 -18
  111. data/lib/alchemy/tasks/tidy.rb +3 -40
  112. data/lib/alchemy/test_support/controller_requests.rb +1 -1
  113. data/lib/alchemy/test_support/essence_shared_examples.rb +1 -1
  114. data/lib/alchemy/test_support/factories/attachment_factory.rb +5 -3
  115. data/lib/alchemy/test_support/factories/content_factory.rb +4 -4
  116. data/lib/alchemy/test_support/factories/dummy_user_factory.rb +5 -5
  117. data/lib/alchemy/test_support/factories/element_factory.rb +12 -7
  118. data/lib/alchemy/test_support/factories/essence_text_factory.rb +1 -1
  119. data/lib/alchemy/test_support/factories/language_factory.rb +13 -13
  120. data/lib/alchemy/test_support/factories/page_factory.rb +18 -17
  121. data/lib/alchemy/test_support/factories/picture_factory.rb +6 -4
  122. data/lib/alchemy/test_support/factories/site_factory.rb +6 -6
  123. data/lib/alchemy/tinymce.rb +1 -1
  124. data/lib/alchemy/upgrader/four_point_two.rb +68 -0
  125. data/lib/alchemy/upgrader/tasks/cells_migration.rb +41 -0
  126. data/lib/alchemy/upgrader/tasks/cells_upgrader.rb +146 -0
  127. data/lib/alchemy/upgrader/tasks/picture_gallery_migration.rb +65 -0
  128. data/lib/alchemy/upgrader/tasks/picture_gallery_upgrader.rb +195 -0
  129. data/lib/alchemy/version.rb +1 -1
  130. data/lib/alchemy_cms.rb +1 -0
  131. data/lib/rails/generators/alchemy/elements/elements_generator.rb +1 -0
  132. data/lib/rails/generators/alchemy/elements/templates/editor.html.erb +0 -3
  133. data/lib/rails/generators/alchemy/elements/templates/editor.html.haml +0 -3
  134. data/lib/rails/generators/alchemy/elements/templates/editor.html.slim +0 -3
  135. data/lib/rails/generators/alchemy/elements/templates/view.html.erb +3 -14
  136. data/lib/rails/generators/alchemy/elements/templates/view.html.haml +3 -10
  137. data/lib/rails/generators/alchemy/elements/templates/view.html.slim +3 -10
  138. data/lib/tasks/alchemy/tidy.rake +1 -23
  139. data/lib/tasks/alchemy/upgrade.rake +44 -1
  140. data/vendor/assets/javascripts/flatpickr/flatpickr.min.js +2 -0
  141. data/vendor/assets/javascripts/tinymce/license.txt +0 -0
  142. data/vendor/assets/javascripts/tinymce/tinymce.min.js +2 -2
  143. metadata +25 -31
  144. data/app/assets/stylesheets/alchemy/jquery.datetimepicker.scss +0 -478
  145. data/app/models/alchemy/cell.rb +0 -95
  146. data/app/models/alchemy/page/page_cells.rb +0 -69
  147. data/app/serializers/alchemy/cell_serializer.rb +0 -19
  148. data/app/views/alchemy/admin/contents/new.html.erb +0 -11
  149. data/app/views/alchemy/admin/contents/order.js.erb +0 -3
  150. data/app/views/alchemy/admin/elements/_add_picture.html.erb +0 -14
  151. data/app/views/alchemy/admin/elements/_picture_gallery_editor.html.erb +0 -24
  152. data/bin/spring +0 -16
  153. data/lib/alchemy/test_support/factories/cell_factory.rb +0 -9
  154. data/vendor/assets/javascripts/date-formatter.js +0 -161
  155. data/vendor/assets/javascripts/jquery_plugins/jquery.datetimepicker.full.min.js +0 -2
@@ -9,13 +9,13 @@
9
9
  # position :integer
10
10
  # page_id :integer not null
11
11
  # public :boolean default(TRUE)
12
+ # fixed :boolean default(FALSE)
12
13
  # folded :boolean default(FALSE)
13
14
  # unique :boolean default(FALSE)
14
15
  # created_at :datetime not null
15
16
  # updated_at :datetime not null
16
17
  # creator_id :integer
17
18
  # updater_id :integer
18
- # cell_id :integer
19
19
  # cached_tag_list :text
20
20
  # parent_element_id :integer
21
21
  #
@@ -28,11 +28,12 @@ module Alchemy
28
28
 
29
29
  FORBIDDEN_DEFINITION_ATTRIBUTES = [
30
30
  "amount",
31
+ "autogenerate",
31
32
  "nestable_elements",
32
33
  "contents",
33
34
  "hint",
34
- "picture_gallery",
35
- "taggable"
35
+ "taggable",
36
+ "compact"
36
37
  ].freeze
37
38
 
38
39
  SKIPPED_ATTRIBUTES_ON_COPY = [
@@ -46,15 +47,15 @@ module Alchemy
46
47
  "updater_id"
47
48
  ].freeze
48
49
 
49
- # All Elements that share the same page id, cell id and parent element id are considered a list.
50
+ # All Elements that share the same page id and parent element id and are fixed or not are considered a list.
50
51
  #
51
- # If cell id and parent element id are nil (typical case for a simple page),
52
+ # If parent element id is nil (typical case for a simple page),
52
53
  # then all elements on that page are still in one list,
53
54
  # because acts_as_list correctly creates this statement:
54
55
  #
55
- # WHERE page_id = 1 and cell_id = NULL AND parent_element_id = NULL
56
+ # WHERE page_id = 1 and fixed = FALSE AND parent_element_id = NULL
56
57
  #
57
- acts_as_list scope: [:page_id, :cell_id, :parent_element_id]
58
+ acts_as_list scope: [:page_id, :fixed, :parent_element_id]
58
59
 
59
60
  stampable stamper_class_name: Alchemy.user_class_name
60
61
 
@@ -69,7 +70,6 @@ module Alchemy
69
70
  foreign_key: :parent_element_id,
70
71
  dependent: :destroy
71
72
 
72
- belongs_to :cell, optional: true, touch: true
73
73
  belongs_to :page, touch: true, inverse_of: :descendent_elements
74
74
 
75
75
  # A nested element belongs to a parent element.
@@ -85,20 +85,22 @@ module Alchemy
85
85
  validates_presence_of :name, on: :create
86
86
  validates_format_of :name, on: :create, with: /\A[a-z0-9_-]+\z/
87
87
 
88
- attr_accessor :create_contents_after_create
88
+ attr_accessor :autogenerate_contents
89
+ attr_accessor :autogenerate_nested_elements
90
+ after_create :create_contents, unless: -> { autogenerate_contents == false }
91
+ after_create :generate_nested_elements, unless: -> { autogenerate_nested_elements == false }
89
92
 
90
- after_create :create_contents, unless: proc { |e| e.create_contents_after_create == false }
91
93
  after_update :touch_touchable_pages
92
94
 
93
95
  scope :trashed, -> { where(position: nil).order('updated_at DESC') }
94
- scope :not_trashed, -> { where(Element.arel_table[:position].not_eq(nil)) }
96
+ scope :not_trashed, -> { where.not(position: nil) }
95
97
  scope :published, -> { where(public: true) }
96
98
  scope :not_restricted, -> { joins(:page).merge(Page.not_restricted) }
97
99
  scope :available, -> { published.not_trashed }
98
100
  scope :named, ->(names) { where(name: names) }
99
101
  scope :excluded, ->(names) { where(arel_table[:name].not_in(names)) }
100
- scope :not_in_cell, -> { where(cell_id: nil) }
101
- scope :in_cell, -> { where("#{table_name}.cell_id IS NOT NULL") }
102
+ scope :fixed, -> { where(fixed: true) }
103
+ scope :unfixed, -> { where(fixed: false) }
102
104
  scope :from_current_site, -> { where(Language.table_name => {site_id: Site.current || Site.default}).joins(page: 'language') }
103
105
  scope :folded, -> { where(folded: true) }
104
106
  scope :expanded, -> { where(folded: false) }
@@ -121,23 +123,21 @@ module Alchemy
121
123
  # - Raises Alchemy::ElementDefinitionError if no definition for given attributes[:name]
122
124
  # could be found
123
125
  #
124
- def new_from_scratch(attributes = {})
125
- return new if attributes[:name].blank?
126
- new_element_from_definition_by(attributes) || raise(ElementDefinitionError, attributes)
127
- end
126
+ def new(attributes = {})
127
+ return super if attributes[:name].blank?
128
+ element_attributes = attributes.to_h.merge(name: attributes[:name].split('#').first)
129
+ element_definition = Element.definition_by_name(element_attributes[:name])
130
+ if element_definition.nil?
131
+ raise(ElementDefinitionError, attributes)
132
+ end
128
133
 
129
- # Creates a new element as described in +/config/alchemy/elements.yml+
130
- #
131
- # - Returns a new Alchemy::Element object if no name is given in attributes,
132
- # because the definition can not be found w/o name
133
- # - Raises Alchemy::ElementDefinitionError if no definition for given attributes[:name]
134
- # could be found
135
- #
136
- def create_from_scratch(attributes)
137
- element = new_from_scratch(attributes)
138
- element.save if element
139
- element
134
+ super(element_definition.merge(element_attributes).except(*FORBIDDEN_DEFINITION_ATTRIBUTES))
140
135
  end
136
+ alias_method :new_from_scratch, :new
137
+ deprecate new_from_scratch: :new, deprecator: Alchemy::Deprecation
138
+
139
+ alias_method :create_from_scratch, :create
140
+ deprecate create_from_scratch: :create, deprecator: Alchemy::Deprecation
141
141
 
142
142
  # This methods does a copy of source and all depending contents and all of their depending essences.
143
143
  #
@@ -155,7 +155,8 @@ module Alchemy
155
155
  .except(*SKIPPED_ATTRIBUTES_ON_COPY)
156
156
  .merge(differences)
157
157
  .merge({
158
- create_contents_after_create: false,
158
+ autogenerate_contents: false,
159
+ autogenerate_nested_elements: false,
159
160
  tag_list: source_element.tag_list
160
161
  })
161
162
 
@@ -185,16 +186,6 @@ module Alchemy
185
186
  page.available_element_names.include?(ce.name)
186
187
  }
187
188
  end
188
-
189
- private
190
-
191
- def new_element_from_definition_by(attributes)
192
- element_attributes = attributes.to_h.merge(name: attributes[:name].split('#').first)
193
- element_definition = Element.definition_by_name(element_attributes[:name])
194
- return if element_definition.nil?
195
-
196
- new(element_definition.merge(element_attributes).except(*FORBIDDEN_DEFINITION_ATTRIBUTES))
197
- end
198
189
  end
199
190
 
200
191
  # Returns next public element from same page.
@@ -235,17 +226,6 @@ module Alchemy
235
226
  position.nil?
236
227
  end
237
228
 
238
- # The names of all cells from given page this element could be placed in.
239
- #
240
- def available_page_cell_names(page)
241
- cellnames = unique_available_page_cell_names(page)
242
- if cellnames.blank? || !page.has_cells?
243
- ['for_other_elements']
244
- else
245
- cellnames
246
- end
247
- end
248
-
249
229
  # Returns true if the definition of this element has a taggable true value.
250
230
  def taggable?
251
231
  definition['taggable'] == true
@@ -256,6 +236,11 @@ module Alchemy
256
236
  !folded?
257
237
  end
258
238
 
239
+ # Defined as compact element?
240
+ def compact?
241
+ definition['compact'] == true
242
+ end
243
+
259
244
  # The element's view partial is dependent from its name
260
245
  #
261
246
  # == Define elements
@@ -298,31 +283,26 @@ module Alchemy
298
283
  nested_elements.map do |nested_element|
299
284
  Element.copy(nested_element, {
300
285
  parent_element_id: target_element.id,
301
- page_id: target_element.page_id,
302
- cell_id: target_element.cell_id
286
+ page_id: target_element.page_id
303
287
  })
304
288
  end
305
289
  end
306
290
 
307
291
  private
308
292
 
309
- def select_element(elements, name, order)
310
- elements = elements.named(name) if name.present?
311
- elements.reorder(position: order).limit(1).first
312
- end
313
-
314
- # Returns all cells from given page this element could be placed in.
315
- #
316
- def available_page_cells(page)
317
- page.cells.select do |cell|
318
- cell.available_elements.include?(name)
293
+ def generate_nested_elements
294
+ definition.fetch('autogenerate', []).each do |nestable_element|
295
+ if nestable_elements.include?(nestable_element)
296
+ Element.create(page: page, parent_element_id: id, name: nestable_element)
297
+ else
298
+ log_warning("Element '#{nestable_element}' not a nestable element for '#{name}'. Skipping!")
299
+ end
319
300
  end
320
301
  end
321
302
 
322
- # Returns all uniq cell names from given page this element could be placed in.
323
- #
324
- def unique_available_page_cell_names(page)
325
- available_page_cells(page).collect(&:name).uniq
303
+ def select_element(elements, name, order)
304
+ elements = elements.named(name) if name.present?
305
+ elements.reorder(position: order).limit(1).first
326
306
  end
327
307
 
328
308
  # Updates all +touchable_pages+
@@ -139,8 +139,8 @@ module Alchemy
139
139
 
140
140
  # creates the contents for this element as described in the elements.yml
141
141
  def create_contents
142
- definition.fetch("contents", []).each do |content_hash|
143
- Content.create_from_scratch(self, content_hash)
142
+ definition.fetch('contents', []).each do |attributes|
143
+ Content.create(attributes.merge(element: self))
144
144
  end
145
145
  end
146
146
  end
@@ -43,7 +43,7 @@ module Alchemy
43
43
  include Alchemy::Taggable
44
44
 
45
45
  DEFAULT_ATTRIBUTES_FOR_COPY = {
46
- do_not_autogenerate: true,
46
+ autogenerate_elements: false,
47
47
  visible: false,
48
48
  public_on: nil,
49
49
  public_until: nil,
@@ -130,7 +130,6 @@ module Alchemy
130
130
  include Alchemy::Page::PageNatures
131
131
  include Alchemy::Page::PageNaming
132
132
  include Alchemy::Page::PageUsers
133
- include Alchemy::Page::PageCells
134
133
  include Alchemy::Page::PageElements
135
134
 
136
135
  # site_name accessor
@@ -188,7 +187,6 @@ module Alchemy
188
187
  page = Alchemy::Page.new(attributes_from_source_for_copy(source, differences))
189
188
  page.tag_list = source.tag_list
190
189
  if page.save!
191
- copy_cells(source, page)
192
190
  copy_elements(source, page)
193
191
  page
194
192
  end
@@ -206,7 +204,7 @@ module Alchemy
206
204
  name: "Layoutroot for #{language.name}",
207
205
  layoutpage: true,
208
206
  language: language,
209
- do_not_autogenerate: true,
207
+ autogenerate_elements: false,
210
208
  parent_id: Page.root.id
211
209
  )
212
210
  end
@@ -291,6 +289,38 @@ module Alchemy
291
289
  # Instance methods
292
290
  #
293
291
 
292
+ # Returns elements from page.
293
+ #
294
+ # @option options [Array<String>|String] :only
295
+ # Returns only elements with given names
296
+ # @option options [Array<String>|String] :except
297
+ # Returns all elements except the ones with given names
298
+ # @option options [Integer] :count
299
+ # Limit the count of returned elements
300
+ # @option options [Integer] :offset
301
+ # Starts with an offset while returning elements
302
+ # @option options [Boolean] :include_hidden (false)
303
+ # Return hidden elements as well
304
+ # @option options [Boolean] :random (false)
305
+ # Return elements randomly shuffled
306
+ # @option options [Boolean] :reverse (false)
307
+ # Reverse the load order
308
+ # @option options [Class] :finder (Alchemy::ElementsFinder)
309
+ # A class that will return elements from page.
310
+ # Use this for your custom element loading logic.
311
+ #
312
+ # @return [ActiveRecord::Relation]
313
+ def find_elements(options = {}, show_non_public = false)
314
+ if show_non_public
315
+ Alchemy::Deprecation.warn "Passing true as second argument to page#find_elements to include" \
316
+ " invisible elements has been removed. Please implement your own ElementsFinder" \
317
+ " and pass it with options[:finder]."
318
+ end
319
+
320
+ finder = options[:finder] || Alchemy::ElementsFinder.new(options)
321
+ finder.elements(page: self)
322
+ end
323
+
294
324
  # The page's view partial is dependent from its page layout
295
325
  #
296
326
  # == Define page layouts
@@ -5,14 +5,22 @@ module Alchemy
5
5
  extend ActiveSupport::Concern
6
6
 
7
7
  included do
8
- attr_accessor :do_not_autogenerate
8
+ attr_accessor :autogenerate_elements
9
9
 
10
- has_many :elements, -> { where(parent_element_id: nil).not_trashed.order(:position) }
10
+ has_many :elements,
11
+ -> { order(:position).not_nested.unfixed.not_trashed },
12
+ class_name: 'Alchemy::Element'
13
+ has_many :elements_including_fixed,
14
+ -> { order(:position).not_nested.not_trashed },
15
+ class_name: 'Alchemy::Element'
11
16
  has_many :trashed_elements,
12
17
  -> { Element.trashed.order(:position) },
13
18
  class_name: 'Alchemy::Element'
19
+ has_many :fixed_elements,
20
+ -> { order(:position).fixed.not_trashed },
21
+ class_name: 'Alchemy::Element'
14
22
  has_many :descendent_elements,
15
- -> { order(:position).not_trashed },
23
+ -> { order(:position).unfixed.not_trashed },
16
24
  class_name: 'Alchemy::Element'
17
25
  has_many :contents, through: :elements
18
26
  has_many :descendent_contents,
@@ -23,12 +31,13 @@ module Alchemy
23
31
  class_name: 'Alchemy::Element',
24
32
  join_table: ElementToPage.table_name
25
33
 
26
- after_create :autogenerate_elements, unless: -> { systempage? || do_not_autogenerate }
34
+ after_create :generate_elements,
35
+ unless: -> { systempage? || autogenerate_elements == false }
27
36
 
28
37
  after_update :trash_not_allowed_elements!,
29
38
  if: :has_page_layout_changed?
30
39
 
31
- after_update :autogenerate_elements,
40
+ after_update :generate_elements,
32
41
  if: :has_page_layout_changed?
33
42
  end
34
43
 
@@ -41,15 +50,10 @@ module Alchemy
41
50
  #
42
51
  def copy_elements(source, target)
43
52
  new_elements = []
44
- source.elements.not_trashed.each do |source_element|
45
- cell = nil
46
- if source_element.cell
47
- cell = target.cells.find_by(name: source_element.cell.name)
48
- end
49
- new_element = Element.copy source_element, {
50
- page_id: target.id,
51
- cell_id: cell.try(:id)
52
- }
53
+ source.elements_including_fixed.each do |source_element|
54
+ new_element = Element.copy(source_element, {
55
+ page_id: target.id
56
+ })
53
57
  new_element.move_to_bottom
54
58
  new_elements << new_element
55
59
  end
@@ -57,46 +61,6 @@ module Alchemy
57
61
  end
58
62
  end
59
63
 
60
- # Finds elements of page.
61
- #
62
- # @param [Hash]
63
- # options hash
64
- # @param [Boolean] (false)
65
- # Pass true, if you want to also have not published elements.
66
- #
67
- # @option options [Array] only
68
- # Returns only elements with given names
69
- # @option options [Array] except
70
- # Returns all elements except the ones with given names
71
- # @option options [Fixnum] count
72
- # Limit the count of returned elements
73
- # @option options [Fixnum] offset
74
- # Starts with an offset while returning elements
75
- # @option options [Boolean] random (false)
76
- # Return elements randomly shuffled
77
- # @option options [Alchemy::Cell || String] from_cell
78
- # Return elements from given cell
79
- #
80
- # @return [ActiveRecord::Relation]
81
- #
82
- def find_elements(options = {}, show_non_public = false)
83
- elements = elements_from_cell_or_self(options[:from_cell])
84
- if options[:only].present?
85
- elements = elements.named(options[:only])
86
- elsif options[:except].present?
87
- elements = elements.excluded(options[:except])
88
- end
89
- if options[:reverse_sort] || options[:reverse]
90
- elements = elements.reverse_order
91
- end
92
- elements = elements.offset(options[:offset]).limit(options[:count])
93
- if options[:random]
94
- elements = elements.order("RAND()")
95
- end
96
- show_non_public ? elements : elements.published
97
- end
98
- alias_method :find_selected_elements, :find_elements
99
-
100
64
  # All available element definitions that can actually be placed on current page.
101
65
  #
102
66
  # It extracts all definitions that are unique or limited and already on page.
@@ -127,7 +91,7 @@ module Alchemy
127
91
 
128
92
  return [] if @_element_definitions.blank?
129
93
 
130
- @_existing_element_names = elements.not_trashed.pluck(:name)
94
+ @_existing_element_names = elements_including_fixed.pluck(:name)
131
95
  delete_unique_element_definitions!
132
96
  delete_outnumbered_element_definitions!
133
97
 
@@ -172,26 +136,20 @@ module Alchemy
172
136
  definitions.uniq { |d| d['name'] }
173
137
  end
174
138
 
175
- # All names of elements that are defined in the corresponding
176
- # page and cell definition.
139
+ # All names of elements that are defined in the page definition.
177
140
  #
178
- # Assign elements to a page in +config/alchemy/page_layouts.yml+ and/or
179
- # +config/alchemy/cells.yml+ file.
141
+ # Assign elements to a page in +config/alchemy/page_layouts.yml+.
180
142
  #
181
143
  # == Example of page_layouts.yml:
182
144
  #
183
145
  # - name: contact
184
- # cells: [right_column]
185
146
  # elements: [headline, contactform]
186
147
  #
187
- # == Example of cells.yml:
188
- #
189
- # - name: right_column
190
- # elements: [teaser]
191
- #
192
148
  def element_definition_names
193
- element_names_from_definition | element_names_from_cell_definitions
149
+ definition['elements'] || []
194
150
  end
151
+ alias_method :element_names_from_definition, :element_definition_names
152
+ deprecate element_names_from_definition: :element_definition_names, deprecator: Alchemy::Deprecation
195
153
 
196
154
  # Element definitions with given name(s)
197
155
  #
@@ -231,43 +189,17 @@ module Alchemy
231
189
  .collect(&:id)
232
190
  end
233
191
 
234
- def element_names_from_definition
235
- definition['elements'] || []
236
- end
237
-
238
192
  private
239
193
 
240
- def element_names_from_cell_definitions
241
- @_element_names_from_cell_definitions ||= cell_definitions.map do |d|
242
- d['elements']
243
- end.flatten
244
- end
245
-
246
194
  # Looks in the page_layout descripion, if there are elements to autogenerate.
247
195
  #
248
196
  # And if so, it generates them.
249
197
  #
250
- # If the page has cells, it looks if there are elements to generate.
251
- #
252
- def autogenerate_elements
253
- elements_already_on_page = elements.available.pluck(:name)
254
- elements = definition["autogenerate"]
255
- if elements.present?
256
- elements.each do |element|
257
- next if elements_already_on_page.include?(element)
258
- Element.create_from_scratch(attributes_for_element_name(element))
259
- end
260
- end
261
- end
262
-
263
- # Returns a hash of attributes for given element name
264
- def attributes_for_element_name(element)
265
- element_cell_definition = cell_definitions.detect { |c| c['elements'].include?(element) }
266
- if has_cells? && element_cell_definition
267
- cell = cells.find_by!(name: element_cell_definition['name'])
268
- {page_id: id, cell_id: cell.id, name: element}
269
- else
270
- {page_id: id, name: element}
198
+ def generate_elements
199
+ elements_already_on_page = elements_including_fixed.pluck(:name)
200
+ definition.fetch('autogenerate', []).each do |element_name|
201
+ next if elements_already_on_page.include?(element_name)
202
+ Element.create(page: self, name: element_name)
271
203
  end
272
204
  end
273
205
 
@@ -275,7 +207,7 @@ module Alchemy
275
207
  def trash_not_allowed_elements!
276
208
  not_allowed_elements = elements.where([
277
209
  "#{Element.table_name}.name NOT IN (?)",
278
- element_names_from_definition
210
+ element_definition_names
279
211
  ])
280
212
  not_allowed_elements.to_a.map(&:trash!)
281
213
  end
@@ -304,29 +236,5 @@ module Alchemy
304
236
  element['amount'] && outnumbered.count >= element['amount'].to_i
305
237
  end
306
238
  end
307
-
308
- # Returns elements either from given cell or self
309
- #
310
- def elements_from_cell_or_self(cell)
311
- case cell.class.name
312
- when 'Alchemy::Cell'
313
- cell.elements
314
- when 'String'
315
- cell_elements_by_name(cell)
316
- else
317
- elements.not_in_cell
318
- end
319
- end
320
-
321
- # Returns all elements from given cell name
322
- #
323
- def cell_elements_by_name(name)
324
- if cell = cells.find_by_name(name)
325
- cell.elements
326
- else
327
- Alchemy::Logger.warn("Cell with name `#{name}` could not be found!", caller(0..0))
328
- Element.none
329
- end
330
- end
331
239
  end
332
240
  end