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.
- checksums.yaml +4 -4
- data/.github/ISSUE_TEMPLATE/Bug_report.md +0 -5
- data/.rubocop.yml +4 -1
- data/.travis.yml +3 -3
- data/CHANGELOG.md +29 -10
- data/Gemfile +5 -7
- data/README.md +114 -62
- data/Rakefile +2 -2
- data/alchemy_cms.gemspec +5 -5
- data/app/assets/images/alchemy/icon.svg +1 -1
- data/app/assets/javascripts/alchemy/admin.js +2 -0
- data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +0 -24
- data/app/assets/javascripts/alchemy/alchemy.datepicker.js.coffee +11 -9
- data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +7 -24
- data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +9 -8
- data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +38 -0
- data/app/assets/javascripts/alchemy/alchemy.sitemap.js.coffee +2 -4
- data/app/assets/javascripts/alchemy/alchemy.translations.js.coffee +1 -1
- data/app/assets/stylesheets/alchemy/_mixins.scss +2 -1
- data/app/assets/stylesheets/alchemy/_variables.scss +7 -2
- data/app/assets/stylesheets/alchemy/admin.scss +2 -1
- data/app/assets/stylesheets/alchemy/archive.scss +18 -4
- data/app/assets/stylesheets/alchemy/buttons.scss +1 -1
- data/app/assets/stylesheets/alchemy/dialogs.scss +1 -1
- data/app/assets/stylesheets/alchemy/elements.scss +154 -90
- data/app/assets/stylesheets/alchemy/filter_field.scss +30 -0
- data/app/assets/stylesheets/alchemy/flatpickr.scss +839 -0
- data/app/assets/stylesheets/alchemy/forms.scss +5 -1
- data/app/assets/stylesheets/alchemy/frame.scss +6 -14
- data/app/assets/stylesheets/alchemy/navigation.scss +109 -98
- data/app/assets/stylesheets/alchemy/search.scss +11 -29
- data/app/assets/stylesheets/alchemy/tables.scss +0 -23
- data/app/assets/stylesheets/alchemy/tags.scss +4 -27
- data/app/assets/stylesheets/alchemy/toolbar.scss +12 -2
- data/app/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +4 -33
- data/app/controllers/alchemy/admin/attachments_controller.rb +1 -12
- data/app/controllers/alchemy/admin/contents_controller.rb +2 -24
- data/app/controllers/alchemy/admin/elements_controller.rb +11 -49
- data/app/controllers/alchemy/admin/pages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pictures_controller.rb +1 -14
- data/app/controllers/alchemy/api/contents_controller.rb +1 -1
- data/app/controllers/alchemy/api/elements_controller.rb +1 -1
- data/app/controllers/alchemy/api/pages_controller.rb +1 -1
- data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +20 -0
- data/app/helpers/alchemy/admin/base_helper.rb +8 -18
- data/app/helpers/alchemy/admin/elements_helper.rb +55 -85
- data/app/helpers/alchemy/admin/pictures_helper.rb +0 -23
- data/app/helpers/alchemy/elements_helper.rb +80 -120
- data/app/helpers/alchemy/pages_helper.rb +5 -24
- data/app/models/alchemy/content.rb +0 -1
- data/app/models/alchemy/content/factory.rb +33 -57
- data/app/models/alchemy/element.rb +46 -66
- data/app/models/alchemy/element/element_contents.rb +2 -2
- data/app/models/alchemy/page.rb +34 -4
- data/app/models/alchemy/page/page_elements.rb +30 -122
- data/app/serializers/alchemy/attachment_serializer.rb +0 -2
- data/app/serializers/alchemy/content_serializer.rb +0 -2
- data/app/serializers/alchemy/element_serializer.rb +0 -3
- data/app/serializers/alchemy/essence_boolean_serializer.rb +0 -2
- data/app/serializers/alchemy/essence_date_serializer.rb +0 -2
- data/app/serializers/alchemy/essence_file_serializer.rb +0 -2
- data/app/serializers/alchemy/essence_html_serializer.rb +0 -2
- data/app/serializers/alchemy/essence_link_serializer.rb +0 -2
- data/app/serializers/alchemy/essence_picture_serializer.rb +0 -2
- data/app/serializers/alchemy/essence_richtext_serializer.rb +0 -2
- data/app/serializers/alchemy/essence_select_serializer.rb +0 -2
- data/app/serializers/alchemy/essence_text_serializer.rb +0 -2
- data/app/serializers/alchemy/legacy_element_serializer.rb +0 -3
- data/app/serializers/alchemy/page_serializer.rb +2 -8
- data/app/serializers/alchemy/page_tree_serializer.rb +1 -1
- data/app/serializers/alchemy/picture_serializer.rb +0 -2
- data/app/views/alchemy/admin/clipboard/index.html.erb +2 -2
- data/app/views/alchemy/admin/clipboard/insert.js.erb +9 -12
- data/app/views/alchemy/admin/contents/create.js.erb +4 -30
- data/app/views/alchemy/admin/elements/_element.html.erb +27 -12
- data/app/views/alchemy/admin/elements/_element_toolbar.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_new_element_form.html.erb +0 -10
- data/app/views/alchemy/admin/elements/create.js.erb +12 -12
- data/app/views/alchemy/admin/elements/fold.js.erb +1 -1
- data/app/views/alchemy/admin/elements/index.html.erb +20 -19
- data/app/views/alchemy/admin/elements/publish.js.erb +5 -0
- data/app/views/alchemy/admin/elements/trash.js.erb +4 -1
- data/app/views/alchemy/admin/essence_pictures/assign.js.erb +0 -7
- data/app/views/alchemy/admin/essence_pictures/destroy.js.erb +0 -22
- data/app/views/alchemy/admin/pages/_publication_fields.html.erb +2 -4
- data/app/views/alchemy/admin/pages/edit.html.erb +1 -1
- data/app/views/alchemy/admin/pages/index.html.erb +14 -10
- data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +1 -1
- data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +1 -1
- data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +7 -5
- data/app/views/alchemy/admin/partials/_routes.html.erb +0 -1
- data/app/views/alchemy/admin/partials/_search_form.html.erb +6 -4
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +6 -3
- data/app/views/alchemy/admin/pictures/index.html.erb +1 -1
- data/app/views/alchemy/admin/trash/index.html.erb +8 -7
- data/app/views/alchemy/elements/_editor_not_found.html.erb +1 -1
- data/app/views/alchemy/essences/_essence_picture_editor.html.erb +4 -19
- data/app/views/layouts/alchemy/admin.html.erb +1 -0
- data/bin/rspec +0 -5
- data/config/alchemy/config.yml +3 -0
- data/config/brakeman.ignore +1 -1
- data/config/locales/alchemy.en.yml +6 -12
- data/config/routes.rb +1 -5
- data/db/migrate/20180226123013_alchemy_four_point_zero.rb +0 -29
- data/db/migrate/20180519204655_add_fixed_to_alchemy_elements.rb +6 -0
- data/lib/alchemy/admin/locale.rb +1 -1
- data/lib/alchemy/cache_digests/template_tracker.rb +4 -27
- data/lib/alchemy/elements_finder.rb +111 -0
- data/lib/alchemy/errors.rb +0 -4
- data/lib/alchemy/modules.rb +49 -18
- data/lib/alchemy/tasks/tidy.rb +3 -40
- data/lib/alchemy/test_support/controller_requests.rb +1 -1
- data/lib/alchemy/test_support/essence_shared_examples.rb +1 -1
- data/lib/alchemy/test_support/factories/attachment_factory.rb +5 -3
- data/lib/alchemy/test_support/factories/content_factory.rb +4 -4
- data/lib/alchemy/test_support/factories/dummy_user_factory.rb +5 -5
- data/lib/alchemy/test_support/factories/element_factory.rb +12 -7
- data/lib/alchemy/test_support/factories/essence_text_factory.rb +1 -1
- data/lib/alchemy/test_support/factories/language_factory.rb +13 -13
- data/lib/alchemy/test_support/factories/page_factory.rb +18 -17
- data/lib/alchemy/test_support/factories/picture_factory.rb +6 -4
- data/lib/alchemy/test_support/factories/site_factory.rb +6 -6
- data/lib/alchemy/tinymce.rb +1 -1
- data/lib/alchemy/upgrader/four_point_two.rb +68 -0
- data/lib/alchemy/upgrader/tasks/cells_migration.rb +41 -0
- data/lib/alchemy/upgrader/tasks/cells_upgrader.rb +146 -0
- data/lib/alchemy/upgrader/tasks/picture_gallery_migration.rb +65 -0
- data/lib/alchemy/upgrader/tasks/picture_gallery_upgrader.rb +195 -0
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +1 -0
- data/lib/rails/generators/alchemy/elements/elements_generator.rb +1 -0
- data/lib/rails/generators/alchemy/elements/templates/editor.html.erb +0 -3
- data/lib/rails/generators/alchemy/elements/templates/editor.html.haml +0 -3
- data/lib/rails/generators/alchemy/elements/templates/editor.html.slim +0 -3
- data/lib/rails/generators/alchemy/elements/templates/view.html.erb +3 -14
- data/lib/rails/generators/alchemy/elements/templates/view.html.haml +3 -10
- data/lib/rails/generators/alchemy/elements/templates/view.html.slim +3 -10
- data/lib/tasks/alchemy/tidy.rake +1 -23
- data/lib/tasks/alchemy/upgrade.rake +44 -1
- data/vendor/assets/javascripts/flatpickr/flatpickr.min.js +2 -0
- data/vendor/assets/javascripts/tinymce/license.txt +0 -0
- data/vendor/assets/javascripts/tinymce/tinymce.min.js +2 -2
- metadata +25 -31
- data/app/assets/stylesheets/alchemy/jquery.datetimepicker.scss +0 -478
- data/app/models/alchemy/cell.rb +0 -95
- data/app/models/alchemy/page/page_cells.rb +0 -69
- data/app/serializers/alchemy/cell_serializer.rb +0 -19
- data/app/views/alchemy/admin/contents/new.html.erb +0 -11
- data/app/views/alchemy/admin/contents/order.js.erb +0 -3
- data/app/views/alchemy/admin/elements/_add_picture.html.erb +0 -14
- data/app/views/alchemy/admin/elements/_picture_gallery_editor.html.erb +0 -24
- data/bin/spring +0 -16
- data/lib/alchemy/test_support/factories/cell_factory.rb +0 -9
- data/vendor/assets/javascripts/date-formatter.js +0 -161
- 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
|
-
"
|
35
|
-
"
|
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
|
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
|
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
|
56
|
+
# WHERE page_id = 1 and fixed = FALSE AND parent_element_id = NULL
|
56
57
|
#
|
57
|
-
acts_as_list scope: [:page_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 :
|
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(
|
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 :
|
101
|
-
scope :
|
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
|
125
|
-
return
|
126
|
-
|
127
|
-
|
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
|
-
|
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
|
-
|
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
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
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
|
-
|
323
|
-
|
324
|
-
|
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(
|
143
|
-
Content.
|
142
|
+
definition.fetch('contents', []).each do |attributes|
|
143
|
+
Content.create(attributes.merge(element: self))
|
144
144
|
end
|
145
145
|
end
|
146
146
|
end
|
data/app/models/alchemy/page.rb
CHANGED
@@ -43,7 +43,7 @@ module Alchemy
|
|
43
43
|
include Alchemy::Taggable
|
44
44
|
|
45
45
|
DEFAULT_ATTRIBUTES_FOR_COPY = {
|
46
|
-
|
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
|
-
|
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 :
|
8
|
+
attr_accessor :autogenerate_elements
|
9
9
|
|
10
|
-
has_many :elements,
|
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 :
|
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 :
|
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.
|
45
|
-
|
46
|
-
|
47
|
-
|
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 =
|
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
|
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
|
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
|
-
|
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
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
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
|
-
|
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
|