alchemy_cms 4.1.2 → 4.2.0.rc1

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 (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