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