alchemy_cms 5.2.0 → 6.0.0.b3
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/workflows/ci.yml +6 -14
- data/.gitignore +0 -1
- data/.hound.yml +1 -1
- data/.rubocop.yml +46 -4
- data/CHANGELOG.md +114 -5
- data/Gemfile +8 -1
- data/README.md +5 -2
- data/alchemy_cms.gemspec +78 -65
- data/app/assets/javascripts/alchemy/admin.js +0 -2
- data/app/assets/javascripts/alchemy/alchemy.base.js.coffee +0 -27
- data/app/assets/javascripts/alchemy/alchemy.confirm_dialog.js.coffee +2 -1
- data/app/assets/javascripts/alchemy/alchemy.dialog.js.coffee +1 -1
- data/app/assets/javascripts/alchemy/alchemy.dragndrop.js.coffee +0 -25
- data/app/assets/javascripts/alchemy/alchemy.element_editors.js.coffee +1 -1
- data/app/assets/javascripts/alchemy/alchemy.elements_window.js.coffee +2 -0
- data/app/assets/javascripts/alchemy/alchemy.fixed_elements.js +1 -1
- data/app/assets/javascripts/alchemy/alchemy.gui.js.coffee +3 -1
- data/app/assets/javascripts/alchemy/alchemy.image_overlay.coffee +1 -1
- data/app/assets/javascripts/alchemy/alchemy.link_dialog.js.coffee +40 -27
- data/app/assets/javascripts/alchemy/templates/node_folder.hbs +1 -1
- data/app/assets/stylesheets/alchemy/_extends.scss +15 -2
- data/app/assets/stylesheets/alchemy/admin.scss +1 -1
- data/app/assets/stylesheets/alchemy/archive.scss +20 -5
- data/app/assets/stylesheets/alchemy/buttons.scss +0 -4
- data/app/assets/stylesheets/alchemy/elements.scss +73 -61
- data/app/assets/stylesheets/alchemy/images.scss +8 -0
- data/app/assets/stylesheets/alchemy/node-select.scss +4 -3
- data/app/assets/stylesheets/alchemy/page-select.scss +1 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +8 -4
- data/app/controllers/alchemy/admin/base_controller.rb +5 -7
- data/app/controllers/alchemy/admin/elements_controller.rb +59 -34
- data/app/controllers/alchemy/admin/essence_audios_controller.rb +30 -0
- data/app/controllers/alchemy/admin/essence_files_controller.rb +0 -14
- data/app/controllers/alchemy/admin/essence_pictures_controller.rb +8 -79
- data/app/controllers/alchemy/admin/essence_videos_controller.rb +33 -0
- data/app/controllers/alchemy/admin/ingredients_controller.rb +30 -0
- data/app/controllers/alchemy/admin/layoutpages_controller.rb +0 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +7 -22
- data/app/controllers/alchemy/admin/pictures_controller.rb +56 -17
- data/app/controllers/alchemy/admin/resources_controller.rb +84 -10
- data/app/controllers/alchemy/api/elements_controller.rb +13 -4
- data/app/controllers/alchemy/api/pages_controller.rb +4 -3
- data/app/controllers/concerns/alchemy/admin/archive_overlay.rb +13 -3
- data/app/controllers/concerns/alchemy/admin/crop_action.rb +26 -0
- data/app/decorators/alchemy/element_editor.rb +26 -1
- data/app/decorators/alchemy/ingredient_editor.rb +158 -0
- data/app/helpers/alchemy/admin/elements_helper.rb +1 -0
- data/app/helpers/alchemy/admin/essences_helper.rb +1 -1
- data/app/helpers/alchemy/admin/ingredients_helper.rb +42 -0
- data/app/helpers/alchemy/elements_block_helper.rb +23 -6
- data/app/helpers/alchemy/elements_helper.rb +12 -5
- data/app/helpers/alchemy/pages_helper.rb +3 -11
- data/app/jobs/alchemy/base_job.rb +11 -0
- data/app/jobs/alchemy/publish_page_job.rb +11 -0
- data/app/models/alchemy/attachment.rb +24 -7
- data/app/models/alchemy/content.rb +1 -6
- data/app/models/alchemy/content/factory.rb +23 -27
- data/app/models/alchemy/element.rb +39 -72
- data/app/models/alchemy/element/definitions.rb +29 -27
- data/app/models/alchemy/element/element_contents.rb +131 -122
- data/app/models/alchemy/element/element_essences.rb +111 -98
- data/app/models/alchemy/element/element_ingredients.rb +184 -0
- data/app/models/alchemy/element/presenters.rb +104 -85
- data/app/models/alchemy/elements_repository.rb +126 -0
- data/app/models/alchemy/essence_audio.rb +12 -0
- data/app/models/alchemy/essence_headline.rb +40 -0
- data/app/models/alchemy/essence_picture.rb +4 -116
- data/app/models/alchemy/essence_richtext.rb +12 -0
- data/app/models/alchemy/essence_video.rb +12 -0
- data/app/models/alchemy/image_cropper_settings.rb +87 -0
- data/app/models/alchemy/ingredient.rb +183 -0
- data/app/models/alchemy/ingredient_validator.rb +97 -0
- data/app/models/alchemy/ingredients/audio.rb +29 -0
- data/app/models/alchemy/ingredients/boolean.rb +21 -0
- data/app/models/alchemy/ingredients/datetime.rb +20 -0
- data/app/models/alchemy/ingredients/file.rb +30 -0
- data/app/models/alchemy/ingredients/headline.rb +42 -0
- data/app/models/alchemy/ingredients/html.rb +19 -0
- data/app/models/alchemy/ingredients/link.rb +16 -0
- data/app/models/alchemy/ingredients/node.rb +23 -0
- data/app/models/alchemy/ingredients/page.rb +23 -0
- data/app/models/alchemy/ingredients/picture.rb +41 -0
- data/app/models/alchemy/ingredients/richtext.rb +57 -0
- data/app/models/alchemy/ingredients/select.rb +10 -0
- data/app/models/alchemy/ingredients/text.rb +17 -0
- data/app/models/alchemy/ingredients/video.rb +33 -0
- data/app/models/alchemy/language.rb +0 -11
- data/app/models/alchemy/page.rb +76 -33
- data/app/models/alchemy/page/fixed_attributes.rb +53 -51
- data/app/models/alchemy/page/page_elements.rb +186 -205
- data/app/models/alchemy/page/page_naming.rb +66 -64
- data/app/models/alchemy/page/page_natures.rb +139 -142
- data/app/models/alchemy/page/page_scopes.rb +117 -102
- data/app/models/alchemy/page/publisher.rb +50 -0
- data/app/models/alchemy/page/url_path.rb +1 -1
- data/app/models/alchemy/page_version.rb +58 -0
- data/app/models/alchemy/picture.rb +18 -40
- data/app/models/alchemy/picture/calculations.rb +2 -8
- data/app/models/alchemy/picture/preprocessor.rb +2 -0
- data/app/models/alchemy/picture/transformations.rb +24 -96
- data/app/models/concerns/alchemy/picture_thumbnails.rb +181 -0
- data/app/models/concerns/alchemy/touch_elements.rb +2 -2
- data/app/presenters/alchemy/picture_view.rb +88 -0
- data/app/serializers/alchemy/element_serializer.rb +5 -0
- data/app/serializers/alchemy/page_tree_serializer.rb +3 -2
- data/app/services/alchemy/delete_elements.rb +44 -0
- data/app/services/alchemy/duplicate_element.rb +56 -0
- data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +2 -3
- data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +3 -3
- data/app/views/alchemy/admin/attachments/assign.js.erb +11 -0
- data/app/views/alchemy/admin/attachments/index.html.erb +2 -3
- data/app/views/alchemy/admin/crop.html.erb +36 -0
- data/app/views/alchemy/admin/elements/_element.html.erb +14 -10
- data/app/views/alchemy/admin/elements/{_element_footer.html.erb → _footer.html.erb} +0 -0
- data/app/views/alchemy/admin/elements/{_new_element_form.html.erb → _form.html.erb} +1 -1
- data/app/views/alchemy/admin/elements/{_element_header.html.erb → _header.html.erb} +1 -1
- data/app/views/alchemy/admin/elements/{_element_toolbar.html.erb → _toolbar.html.erb} +5 -6
- data/app/views/alchemy/admin/elements/create.js.erb +1 -1
- data/app/views/alchemy/admin/elements/{trash.js.erb → destroy.js.erb} +2 -6
- data/app/views/alchemy/admin/elements/fold.js.erb +2 -2
- data/app/views/alchemy/admin/elements/new.html.erb +3 -3
- data/app/views/alchemy/admin/elements/order.js.erb +0 -17
- data/app/views/alchemy/admin/elements/update.js.erb +3 -2
- data/app/views/alchemy/admin/essence_audios/edit.html.erb +7 -0
- data/app/views/alchemy/admin/essence_pictures/update.js.erb +0 -1
- data/app/views/alchemy/admin/essence_videos/edit.html.erb +11 -0
- data/app/views/alchemy/admin/ingredients/_audio_fields.html.erb +4 -0
- data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +18 -0
- data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +25 -0
- data/app/views/alchemy/admin/ingredients/_video_fields.html.erb +8 -0
- data/app/views/alchemy/admin/ingredients/edit.html.erb +4 -0
- data/app/views/alchemy/admin/layoutpages/edit.html.erb +0 -5
- data/app/views/alchemy/admin/nodes/_node.html.erb +2 -2
- data/app/views/alchemy/admin/pages/_anchor_link.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_external_link.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_file_link.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_form.html.erb +0 -6
- data/app/views/alchemy/admin/pages/_internal_link.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_tinymce_custom_config.html.erb +5 -2
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +1 -1
- data/app/views/alchemy/admin/pages/edit.html.erb +36 -24
- data/app/views/alchemy/admin/pages/index.html.erb +2 -9
- data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +2 -4
- data/app/views/alchemy/admin/partials/_routes.html.erb +7 -11
- data/app/views/alchemy/admin/partials/_search_form.html.erb +9 -0
- data/app/views/alchemy/admin/pictures/_archive.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +5 -7
- data/app/views/alchemy/admin/pictures/_infos.html.erb +0 -1
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +4 -4
- data/app/views/alchemy/admin/pictures/assign.js.erb +10 -0
- data/app/views/alchemy/admin/pictures/index.html.erb +8 -3
- data/app/views/alchemy/admin/resources/_filter.html.erb +12 -0
- data/app/views/alchemy/admin/resources/_filter_bar.html.erb +14 -17
- data/app/views/alchemy/admin/resources/_form.html.erb +3 -0
- data/app/views/alchemy/admin/resources/_table_header.html.erb +15 -0
- data/app/views/alchemy/admin/resources/index.html.erb +3 -11
- data/app/views/alchemy/essences/_essence_audio_editor.html.erb +4 -0
- data/app/views/alchemy/essences/_essence_audio_view.html.erb +15 -0
- data/app/views/alchemy/essences/_essence_file_editor.html.erb +15 -6
- data/app/views/alchemy/essences/_essence_headline_editor.html.erb +36 -0
- data/app/views/alchemy/essences/_essence_headline_view.html.erb +10 -0
- data/app/views/alchemy/essences/_essence_link_editor.html.erb +8 -4
- data/app/views/alchemy/essences/_essence_picture_editor.html.erb +27 -12
- data/app/views/alchemy/essences/_essence_picture_view.html.erb +3 -3
- data/app/views/alchemy/essences/_essence_text_editor.html.erb +12 -4
- data/app/views/alchemy/essences/_essence_video_editor.html.erb +4 -0
- data/app/views/alchemy/essences/_essence_video_view.html.erb +18 -0
- data/app/views/alchemy/essences/shared/_essence_picture_tools.html.erb +21 -16
- data/app/views/alchemy/essences/shared/_linkable_essence_tools.html.erb +2 -2
- data/app/views/alchemy/ingredients/_audio_editor.html.erb +5 -0
- data/app/views/alchemy/ingredients/_audio_view.html.erb +14 -0
- data/app/views/alchemy/ingredients/_boolean_editor.html.erb +11 -0
- data/app/views/alchemy/ingredients/_boolean_view.html.erb +1 -0
- data/app/views/alchemy/ingredients/_datetime_editor.html.erb +17 -0
- data/app/views/alchemy/ingredients/_datetime_view.html.erb +9 -0
- data/app/views/alchemy/ingredients/_file_editor.html.erb +52 -0
- data/app/views/alchemy/ingredients/_file_view.html.erb +17 -0
- data/app/views/alchemy/ingredients/_headline_editor.html.erb +30 -0
- data/app/views/alchemy/ingredients/_headline_view.html.erb +9 -0
- data/app/views/alchemy/ingredients/_html_editor.html.erb +8 -0
- data/app/views/alchemy/ingredients/_html_view.html.erb +1 -0
- data/app/views/alchemy/ingredients/_link_editor.html.erb +24 -0
- data/app/views/alchemy/ingredients/_link_view.html.erb +9 -0
- data/app/views/alchemy/ingredients/_node_editor.html.erb +26 -0
- data/app/views/alchemy/ingredients/_node_view.html.erb +1 -0
- data/app/views/alchemy/ingredients/_page_editor.html.erb +25 -0
- data/app/views/alchemy/ingredients/_page_view.html.erb +4 -0
- data/app/views/alchemy/ingredients/_picture_editor.html.erb +60 -0
- data/app/views/alchemy/ingredients/_picture_view.html.erb +5 -0
- data/app/views/alchemy/ingredients/_richtext_editor.html.erb +12 -0
- data/app/views/alchemy/ingredients/_richtext_view.html.erb +3 -0
- data/app/views/alchemy/ingredients/_select_editor.html.erb +30 -0
- data/app/views/alchemy/ingredients/_select_view.html.erb +1 -0
- data/app/views/alchemy/ingredients/_text_editor.html.erb +20 -0
- data/app/views/alchemy/ingredients/_text_view.html.erb +16 -0
- data/app/views/alchemy/ingredients/_video_editor.html.erb +5 -0
- data/app/views/alchemy/ingredients/_video_view.html.erb +17 -0
- data/app/views/alchemy/ingredients/shared/_link_tools.html.erb +20 -0
- data/app/views/alchemy/ingredients/shared/_picture_tools.html.erb +57 -0
- data/config/brakeman.ignore +66 -159
- data/config/initializers/dragonfly.rb +10 -0
- data/config/locales/alchemy.en.yml +108 -64
- data/config/routes.rb +17 -22
- data/db/migrate/20201207131309_create_page_versions.rb +19 -0
- data/db/migrate/20201207135820_add_page_version_id_to_alchemy_elements.rb +76 -0
- data/db/migrate/20210205143548_rename_public_on_and_public_until_on_alchemy_pages.rb +10 -0
- data/db/migrate/20210326105046_add_sanitized_body_to_alchemy_essence_richtexts.rb +7 -0
- data/db/migrate/20210406093436_add_alchemy_essence_headlines.rb +12 -0
- data/db/migrate/20210506135919_create_essence_audios.rb +19 -0
- data/db/migrate/20210506140258_create_essence_videos.rb +23 -0
- data/db/migrate/20210508091432_create_alchemy_ingredients.rb +22 -0
- data/lib/alchemy/admin/preview_url.rb +2 -0
- data/lib/alchemy/deprecation.rb +1 -1
- data/lib/alchemy/dragonfly/processors/auto_orient.rb +18 -0
- data/lib/alchemy/dragonfly/processors/crop_resize.rb +35 -0
- data/lib/alchemy/elements_finder.rb +14 -60
- data/lib/alchemy/essence.rb +1 -2
- data/lib/alchemy/forms/builder.rb +21 -1
- data/lib/alchemy/hints.rb +8 -4
- data/lib/alchemy/page_layout.rb +0 -13
- data/lib/alchemy/permissions.rb +30 -29
- data/lib/alchemy/resource.rb +13 -3
- data/lib/alchemy/resource_filter.rb +40 -0
- data/lib/alchemy/resources_helper.rb +1 -16
- data/lib/alchemy/tasks/tidy.rb +29 -0
- data/lib/alchemy/test_support.rb +2 -11
- data/lib/alchemy/test_support/essence_shared_examples.rb +0 -1
- data/lib/alchemy/test_support/factories/element_factory.rb +8 -8
- data/lib/alchemy/test_support/factories/essence_audio_factory.rb +7 -0
- data/lib/alchemy/test_support/factories/essence_video_factory.rb +7 -0
- data/lib/alchemy/test_support/factories/ingredient_factory.rb +25 -0
- data/lib/alchemy/test_support/factories/page_factory.rb +20 -1
- data/lib/alchemy/test_support/factories/page_version_factory.rb +23 -0
- data/lib/alchemy/test_support/having_crop_action_examples.rb +170 -0
- data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +646 -0
- data/lib/alchemy/test_support/shared_ingredient_editor_examples.rb +21 -0
- data/lib/alchemy/test_support/shared_ingredient_examples.rb +75 -0
- data/lib/alchemy/tinymce.rb +17 -0
- data/lib/alchemy/upgrader/six_point_zero.rb +21 -0
- data/lib/alchemy/upgrader/tasks/add_page_versions.rb +33 -0
- data/lib/alchemy/upgrader/tasks/ingredients_migrator.rb +62 -0
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy_cms.rb +1 -0
- data/lib/generators/alchemy/elements/elements_generator.rb +1 -0
- data/lib/generators/alchemy/elements/templates/view.html.erb +9 -0
- data/lib/generators/alchemy/elements/templates/view.html.haml +9 -0
- data/lib/generators/alchemy/elements/templates/view.html.slim +9 -0
- data/lib/generators/alchemy/ingredient/ingredient_generator.rb +38 -0
- data/lib/generators/alchemy/ingredient/templates/editor.html.erb +14 -0
- data/lib/generators/alchemy/ingredient/templates/model.rb.tt +13 -0
- data/lib/generators/alchemy/ingredient/templates/view.html.erb +1 -0
- data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +1 -1
- data/lib/generators/alchemy/menus/templates/node.html.erb +1 -1
- data/lib/generators/alchemy/menus/templates/node.html.haml +1 -1
- data/lib/generators/alchemy/menus/templates/node.html.slim +1 -1
- data/lib/generators/alchemy/menus/templates/wrapper.html.erb +1 -1
- data/lib/generators/alchemy/menus/templates/wrapper.html.haml +1 -1
- data/lib/generators/alchemy/menus/templates/wrapper.html.slim +1 -1
- data/lib/tasks/alchemy/thumbnails.rake +4 -2
- data/lib/tasks/alchemy/tidy.rake +12 -0
- data/lib/tasks/alchemy/upgrade.rake +26 -0
- data/package.json +3 -2
- data/package/admin.js +11 -1
- data/package/src/__tests__/i18n.spec.js +23 -0
- data/package/src/file_editors.js +28 -0
- data/package/src/i18n.js +1 -3
- data/package/src/image_cropper.js +103 -0
- data/package/src/image_loader.js +58 -0
- data/package/src/node_tree.js +5 -5
- data/package/src/picture_editors.js +169 -0
- data/package/src/utils/__tests__/ajax.spec.js +20 -12
- data/package/src/utils/ajax.js +8 -3
- data/vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js +3 -18
- data/vendor/assets/stylesheets/jquery.Jcrop.min.scss +2 -28
- metadata +292 -55
- data/app/assets/javascripts/alchemy/alchemy.image_cropper.js.coffee +0 -44
- data/app/assets/javascripts/alchemy/alchemy.trash_window.js.coffee +0 -30
- data/app/assets/stylesheets/alchemy/trash.scss +0 -8
- data/app/controllers/alchemy/admin/trash_controller.rb +0 -44
- data/app/views/alchemy/admin/attachments/_filter_bar.html.erb +0 -29
- data/app/views/alchemy/admin/essence_files/assign.js.erb +0 -3
- data/app/views/alchemy/admin/essence_pictures/assign.js.erb +0 -4
- data/app/views/alchemy/admin/essence_pictures/crop.html.erb +0 -48
- data/app/views/alchemy/admin/pictures/_filter_bar.html.erb +0 -30
- data/app/views/alchemy/admin/trash/clear.js.erb +0 -4
- data/app/views/alchemy/admin/trash/index.html.erb +0 -31
- data/lib/alchemy/test_support/factories.rb +0 -16
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
module Ingredients
|
|
5
|
+
# A simple line of text
|
|
6
|
+
#
|
|
7
|
+
# Optionally it can have a link
|
|
8
|
+
#
|
|
9
|
+
class Text < Alchemy::Ingredient
|
|
10
|
+
store_accessor :data,
|
|
11
|
+
:link,
|
|
12
|
+
:link_target,
|
|
13
|
+
:link_title,
|
|
14
|
+
:link_class_name
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
module Ingredients
|
|
5
|
+
# A video attachment
|
|
6
|
+
#
|
|
7
|
+
class Video < Alchemy::Ingredient
|
|
8
|
+
store_accessor :data,
|
|
9
|
+
:allow_fullscreen,
|
|
10
|
+
:autoplay,
|
|
11
|
+
:controls,
|
|
12
|
+
:height,
|
|
13
|
+
:loop,
|
|
14
|
+
:muted,
|
|
15
|
+
:preload,
|
|
16
|
+
:width
|
|
17
|
+
|
|
18
|
+
related_object_alias :attachment, class_name: "Alchemy::Attachment"
|
|
19
|
+
|
|
20
|
+
delegate :name, to: :attachment, allow_nil: true
|
|
21
|
+
|
|
22
|
+
# The first 30 characters of the attachments name
|
|
23
|
+
#
|
|
24
|
+
# Used by the Element#preview_text method.
|
|
25
|
+
#
|
|
26
|
+
# @param [Integer] max_length (30)
|
|
27
|
+
#
|
|
28
|
+
def preview_text(max_length = 30)
|
|
29
|
+
name.to_s[0..max_length - 1]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -54,9 +54,6 @@ module Alchemy
|
|
|
54
54
|
after_update :set_pages_language,
|
|
55
55
|
if: :should_set_pages_language?
|
|
56
56
|
|
|
57
|
-
after_update :unpublish_pages,
|
|
58
|
-
if: :should_unpublish_pages?
|
|
59
|
-
|
|
60
57
|
before_destroy if: -> { pages.any? } do
|
|
61
58
|
errors.add(:pages, :still_present)
|
|
62
59
|
throw(:abort)
|
|
@@ -170,13 +167,5 @@ module Alchemy
|
|
|
170
167
|
def set_pages_language
|
|
171
168
|
pages.update_all language_code: code
|
|
172
169
|
end
|
|
173
|
-
|
|
174
|
-
def should_unpublish_pages?
|
|
175
|
-
saved_changes[:public] == [true, false]
|
|
176
|
-
end
|
|
177
|
-
|
|
178
|
-
def unpublish_pages
|
|
179
|
-
pages.update_all(public_on: nil, public_until: nil)
|
|
180
|
-
end
|
|
181
170
|
end
|
|
182
171
|
end
|
data/app/models/alchemy/page.rb
CHANGED
|
@@ -35,6 +35,12 @@
|
|
|
35
35
|
# locked_at :datetime
|
|
36
36
|
#
|
|
37
37
|
|
|
38
|
+
require_dependency "alchemy/page/fixed_attributes"
|
|
39
|
+
require_dependency "alchemy/page/page_scopes"
|
|
40
|
+
require_dependency "alchemy/page/page_natures"
|
|
41
|
+
require_dependency "alchemy/page/page_naming"
|
|
42
|
+
require_dependency "alchemy/page/page_elements"
|
|
43
|
+
|
|
38
44
|
module Alchemy
|
|
39
45
|
class Page < BaseRecord
|
|
40
46
|
include Alchemy::Hints
|
|
@@ -109,6 +115,9 @@ module Alchemy
|
|
|
109
115
|
has_many :folded_pages
|
|
110
116
|
has_many :legacy_urls, class_name: "Alchemy::LegacyPageUrl"
|
|
111
117
|
has_many :nodes, class_name: "Alchemy::Node", inverse_of: :page
|
|
118
|
+
has_many :versions, class_name: "Alchemy::PageVersion", inverse_of: :page, dependent: :destroy
|
|
119
|
+
has_one :draft_version, -> { drafts }, class_name: "Alchemy::PageVersion"
|
|
120
|
+
has_one :public_version, -> { published }, class_name: "Alchemy::PageVersion"
|
|
112
121
|
|
|
113
122
|
before_validation :set_language,
|
|
114
123
|
if: -> { language.nil? }
|
|
@@ -117,6 +126,9 @@ module Alchemy
|
|
|
117
126
|
validates_format_of :page_layout, with: /\A[a-z0-9_-]+\z/, unless: -> { page_layout.blank? }
|
|
118
127
|
validates_presence_of :parent, unless: -> { layoutpage? || language_root? }
|
|
119
128
|
|
|
129
|
+
before_create -> { versions.build },
|
|
130
|
+
if: -> { versions.none? }
|
|
131
|
+
|
|
120
132
|
before_save :set_language_code,
|
|
121
133
|
if: -> { language.present? }
|
|
122
134
|
|
|
@@ -126,9 +138,6 @@ module Alchemy
|
|
|
126
138
|
before_save :inherit_restricted_status,
|
|
127
139
|
if: -> { parent && parent.restricted? }
|
|
128
140
|
|
|
129
|
-
before_save :set_published_at,
|
|
130
|
-
if: -> { public_on.present? && published_at.nil? }
|
|
131
|
-
|
|
132
141
|
before_save :set_fixed_attributes,
|
|
133
142
|
if: -> { fixed_attributes.any? }
|
|
134
143
|
|
|
@@ -138,14 +147,22 @@ module Alchemy
|
|
|
138
147
|
after_update -> { nodes.update_all(updated_at: Time.current) }
|
|
139
148
|
|
|
140
149
|
# Concerns
|
|
141
|
-
include
|
|
142
|
-
include
|
|
143
|
-
include
|
|
144
|
-
include
|
|
150
|
+
include PageScopes
|
|
151
|
+
include PageNatures
|
|
152
|
+
include PageNaming
|
|
153
|
+
include PageElements
|
|
145
154
|
|
|
146
155
|
# site_name accessor
|
|
147
156
|
delegate :name, to: :site, prefix: true, allow_nil: true
|
|
148
157
|
|
|
158
|
+
# Old public_on and public_until attributes for historical reasons
|
|
159
|
+
#
|
|
160
|
+
# These attributes now exist on the page versions
|
|
161
|
+
#
|
|
162
|
+
attr_readonly :legacy_public_on, :legacy_public_until
|
|
163
|
+
deprecate :legacy_public_on, deprecator: Alchemy::Deprecation
|
|
164
|
+
deprecate :legacy_public_until, deprecator: Alchemy::Deprecation
|
|
165
|
+
|
|
149
166
|
# Class methods
|
|
150
167
|
#
|
|
151
168
|
class << self
|
|
@@ -165,7 +182,16 @@ module Alchemy
|
|
|
165
182
|
end
|
|
166
183
|
|
|
167
184
|
def alchemy_resource_filters
|
|
168
|
-
|
|
185
|
+
[
|
|
186
|
+
{
|
|
187
|
+
name: :by_page_layout,
|
|
188
|
+
values: PageLayout.all.map { |p| [Alchemy.t(p["name"], scope: "page_layout_names"), p["name"]] },
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
name: :status,
|
|
192
|
+
values: %w[published not_public restricted],
|
|
193
|
+
},
|
|
194
|
+
]
|
|
169
195
|
end
|
|
170
196
|
|
|
171
197
|
def searchable_alchemy_resource_attributes
|
|
@@ -207,11 +233,13 @@ module Alchemy
|
|
|
207
233
|
# @return [Alchemy::Page]
|
|
208
234
|
#
|
|
209
235
|
def copy(source, differences = {})
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
236
|
+
transaction do
|
|
237
|
+
page = Alchemy::Page.new(attributes_from_source_for_copy(source, differences))
|
|
238
|
+
page.tag_list = source.tag_list
|
|
239
|
+
if page.save!
|
|
240
|
+
copy_elements(source, page)
|
|
241
|
+
page
|
|
242
|
+
end
|
|
215
243
|
end
|
|
216
244
|
end
|
|
217
245
|
|
|
@@ -291,7 +319,9 @@ module Alchemy
|
|
|
291
319
|
# Instance methods
|
|
292
320
|
#
|
|
293
321
|
|
|
294
|
-
# Returns elements from
|
|
322
|
+
# Returns elements from pages public version.
|
|
323
|
+
#
|
|
324
|
+
# You can pass another page_version to load elements from in the options.
|
|
295
325
|
#
|
|
296
326
|
# @option options [Array<String>|String] :only
|
|
297
327
|
# Returns only elements with given names
|
|
@@ -310,11 +340,14 @@ module Alchemy
|
|
|
310
340
|
# @option options [Class] :finder (Alchemy::ElementsFinder)
|
|
311
341
|
# A class that will return elements from page.
|
|
312
342
|
# Use this for your custom element loading logic.
|
|
343
|
+
# @option options [Alchemy::PageVersion] :page_version
|
|
344
|
+
# A page version to load elements from.
|
|
345
|
+
# Uses the pages public_version by default.
|
|
313
346
|
#
|
|
314
347
|
# @return [ActiveRecord::Relation]
|
|
315
348
|
def find_elements(options = {})
|
|
316
349
|
finder = options[:finder] || Alchemy::ElementsFinder.new(options)
|
|
317
|
-
finder.elements(
|
|
350
|
+
finder.elements(page_version: options[:page_version] || public_version)
|
|
318
351
|
end
|
|
319
352
|
|
|
320
353
|
# = The url_path for this page
|
|
@@ -423,22 +456,36 @@ module Alchemy
|
|
|
423
456
|
end
|
|
424
457
|
end
|
|
425
458
|
|
|
426
|
-
#
|
|
459
|
+
# Creates a public version of the page.
|
|
427
460
|
#
|
|
428
|
-
# Sets
|
|
429
|
-
# and resets +public_until+ to nil
|
|
461
|
+
# Sets the +published_at+ value to current time
|
|
430
462
|
#
|
|
431
463
|
# The +published_at+ attribute is used as +cache_key+.
|
|
432
464
|
#
|
|
433
|
-
def publish!
|
|
434
|
-
current_time
|
|
435
|
-
|
|
436
|
-
published_at: current_time,
|
|
437
|
-
public_on: already_public_for?(current_time) ? public_on : current_time,
|
|
438
|
-
public_until: still_public_for?(current_time) ? public_until : nil,
|
|
439
|
-
)
|
|
465
|
+
def publish!(current_time = Time.current)
|
|
466
|
+
update(published_at: current_time)
|
|
467
|
+
PublishPageJob.perform_later(self, public_on: current_time)
|
|
440
468
|
end
|
|
441
469
|
|
|
470
|
+
# Sets the public_on date on the published version
|
|
471
|
+
#
|
|
472
|
+
# Builds a new version if none exists yet.
|
|
473
|
+
# Destroys public version if empty time is set
|
|
474
|
+
#
|
|
475
|
+
def public_on=(time)
|
|
476
|
+
if public_version && time.blank?
|
|
477
|
+
public_version.destroy!
|
|
478
|
+
# Need to reset the public version on the instance so we do not need to reload
|
|
479
|
+
self.public_version = nil
|
|
480
|
+
elsif public_version
|
|
481
|
+
public_version.public_on = time
|
|
482
|
+
elsif time.present?
|
|
483
|
+
versions.build(public_on: time)
|
|
484
|
+
end
|
|
485
|
+
end
|
|
486
|
+
|
|
487
|
+
delegate :public_until=, to: :public_version, allow_nil: true
|
|
488
|
+
|
|
442
489
|
# Updates an Alchemy::Page based on a new ordering to be applied to it
|
|
443
490
|
#
|
|
444
491
|
# Note: Page's urls should not be updated (and a legacy URL created) if nesting is OFF
|
|
@@ -460,7 +507,7 @@ module Alchemy
|
|
|
460
507
|
|
|
461
508
|
# Holds an instance of +FixedAttributes+
|
|
462
509
|
def fixed_attributes
|
|
463
|
-
@_fixed_attributes ||=
|
|
510
|
+
@_fixed_attributes ||= FixedAttributes.new(self)
|
|
464
511
|
end
|
|
465
512
|
|
|
466
513
|
# True if given attribute name is defined as fixed
|
|
@@ -479,12 +526,12 @@ module Alchemy
|
|
|
479
526
|
(editor_roles & user.alchemy_roles).any?
|
|
480
527
|
end
|
|
481
528
|
|
|
482
|
-
# Returns the value of +public_on+ attribute
|
|
529
|
+
# Returns the value of +public_on+ attribute from public version
|
|
483
530
|
#
|
|
484
531
|
# If it's a fixed attribute then the fixed value is returned instead
|
|
485
532
|
#
|
|
486
533
|
def public_on
|
|
487
|
-
attribute_fixed?(:public_on) ? fixed_attributes[:public_on] :
|
|
534
|
+
attribute_fixed?(:public_on) ? fixed_attributes[:public_on] : public_version&.public_on
|
|
488
535
|
end
|
|
489
536
|
|
|
490
537
|
# Returns the value of +public_until+ attribute
|
|
@@ -492,7 +539,7 @@ module Alchemy
|
|
|
492
539
|
# If it's a fixed attribute then the fixed value is returned instead
|
|
493
540
|
#
|
|
494
541
|
def public_until
|
|
495
|
-
attribute_fixed?(:public_until) ? fixed_attributes[:public_until] :
|
|
542
|
+
attribute_fixed?(:public_until) ? fixed_attributes[:public_until] : public_version&.public_until
|
|
496
543
|
end
|
|
497
544
|
|
|
498
545
|
# Returns the name of the creator of this page.
|
|
@@ -556,9 +603,5 @@ module Alchemy
|
|
|
556
603
|
def create_legacy_url
|
|
557
604
|
legacy_urls.find_or_create_by(urlname: urlname_before_last_save)
|
|
558
605
|
end
|
|
559
|
-
|
|
560
|
-
def set_published_at
|
|
561
|
-
self.published_at = Time.current
|
|
562
|
-
end
|
|
563
606
|
end
|
|
564
607
|
end
|
|
@@ -1,66 +1,68 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Alchemy
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
# Fixed page attributes are not allowed to be changed by the user.
|
|
7
|
-
#
|
|
8
|
-
# Define fixed page attributes on the page layout definition of a page.
|
|
9
|
-
#
|
|
10
|
-
# == Example
|
|
11
|
-
#
|
|
12
|
-
# # page_layout.yml
|
|
13
|
-
# - name: Index
|
|
14
|
-
# unique: true
|
|
15
|
-
# fixed_attributes:
|
|
16
|
-
# - public_on: nil
|
|
17
|
-
# - public_until: nil
|
|
18
|
-
#
|
|
19
|
-
class Page::FixedAttributes
|
|
20
|
-
attr_reader :page
|
|
21
|
-
|
|
22
|
-
def initialize(page)
|
|
23
|
-
@page = page
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
# All fixed attributes defined on page
|
|
4
|
+
class Page < BaseRecord
|
|
5
|
+
# = Fixed page attributes
|
|
27
6
|
#
|
|
28
|
-
#
|
|
7
|
+
# Fixed page attributes are not allowed to be changed by the user.
|
|
29
8
|
#
|
|
30
|
-
#
|
|
9
|
+
# Define fixed page attributes on the page layout definition of a page.
|
|
31
10
|
#
|
|
32
|
-
|
|
33
|
-
@_attributes ||= page.definition.fetch("fixed_attributes", {}).symbolize_keys
|
|
34
|
-
end
|
|
35
|
-
alias_method :all, :attributes
|
|
36
|
-
|
|
37
|
-
# True if fixed attributes are defined on page
|
|
38
|
-
#
|
|
39
|
-
# Aliased as +#present?+
|
|
11
|
+
# == Example
|
|
40
12
|
#
|
|
41
|
-
#
|
|
13
|
+
# # page_layout.yml
|
|
14
|
+
# - name: Index
|
|
15
|
+
# unique: true
|
|
16
|
+
# fixed_attributes:
|
|
17
|
+
# - public_on: nil
|
|
18
|
+
# - public_until: nil
|
|
42
19
|
#
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
end
|
|
46
|
-
alias_method :present?, :any?
|
|
20
|
+
class FixedAttributes
|
|
21
|
+
attr_reader :page
|
|
47
22
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
#
|
|
52
|
-
def fixed?(name)
|
|
53
|
-
return false if name.nil?
|
|
23
|
+
def initialize(page)
|
|
24
|
+
@page = page
|
|
25
|
+
end
|
|
54
26
|
|
|
55
|
-
attributes
|
|
56
|
-
|
|
27
|
+
# All fixed attributes defined on page
|
|
28
|
+
#
|
|
29
|
+
# Aliased as +#all+
|
|
30
|
+
#
|
|
31
|
+
# @return Hash
|
|
32
|
+
#
|
|
33
|
+
def attributes
|
|
34
|
+
@_attributes ||= page.definition.fetch("fixed_attributes", {}).symbolize_keys
|
|
35
|
+
end
|
|
36
|
+
alias_method :all, :attributes
|
|
57
37
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
38
|
+
# True if fixed attributes are defined on page
|
|
39
|
+
#
|
|
40
|
+
# Aliased as +#present?+
|
|
41
|
+
#
|
|
42
|
+
# @return Boolean
|
|
43
|
+
#
|
|
44
|
+
def any?
|
|
45
|
+
attributes.present?
|
|
46
|
+
end
|
|
47
|
+
alias_method :present?, :any?
|
|
48
|
+
|
|
49
|
+
# True if given attribute name is defined on page
|
|
50
|
+
#
|
|
51
|
+
# @return Boolean
|
|
52
|
+
#
|
|
53
|
+
def fixed?(name)
|
|
54
|
+
return false if name.nil?
|
|
55
|
+
|
|
56
|
+
attributes.key?(name.to_sym)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns the attribute by key
|
|
60
|
+
#
|
|
61
|
+
def [](name)
|
|
62
|
+
return nil if name.nil?
|
|
62
63
|
|
|
63
|
-
|
|
64
|
+
attributes[name.to_sym]
|
|
65
|
+
end
|
|
64
66
|
end
|
|
65
67
|
end
|
|
66
68
|
end
|
|
@@ -1,237 +1,218 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Alchemy
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
inverse_of: :page
|
|
22
|
-
has_many :fixed_elements,
|
|
23
|
-
-> { order(:position).fixed.available },
|
|
24
|
-
class_name: "Alchemy::Element",
|
|
25
|
-
inverse_of: :page
|
|
26
|
-
has_many :dependent_destroyable_elements,
|
|
27
|
-
-> { not_nested },
|
|
28
|
-
class_name: "Alchemy::Element",
|
|
29
|
-
dependent: :destroy
|
|
30
|
-
has_many :contents, through: :elements
|
|
31
|
-
has_and_belongs_to_many :to_be_swept_elements, -> { distinct },
|
|
32
|
-
class_name: "Alchemy::Element",
|
|
33
|
-
join_table: ElementToPage.table_name
|
|
34
|
-
|
|
35
|
-
after_create :generate_elements,
|
|
36
|
-
unless: -> { autogenerate_elements == false }
|
|
37
|
-
|
|
38
|
-
after_update :trash_not_allowed_elements!,
|
|
39
|
-
if: :saved_change_to_page_layout?
|
|
40
|
-
|
|
41
|
-
after_update(if: :saved_change_to_page_layout?) do
|
|
42
|
-
Alchemy::Deprecation.warn(
|
|
43
|
-
"Autogenerating elements on page_layout change is deprecated and will be removed from Alchemy 6.0"
|
|
44
|
-
)
|
|
45
|
-
generate_elements
|
|
46
|
-
end
|
|
47
|
-
end
|
|
4
|
+
class Page < BaseRecord
|
|
5
|
+
module PageElements
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
included do
|
|
9
|
+
attr_accessor :autogenerate_elements
|
|
10
|
+
|
|
11
|
+
with_options(
|
|
12
|
+
class_name: "Alchemy::Element",
|
|
13
|
+
through: :public_version,
|
|
14
|
+
inverse_of: :page,
|
|
15
|
+
source: :elements,
|
|
16
|
+
) do
|
|
17
|
+
has_many :all_elements
|
|
18
|
+
has_many :elements, -> { not_nested.unfixed.available }
|
|
19
|
+
has_many :fixed_elements, -> { fixed.available }
|
|
20
|
+
end
|
|
48
21
|
|
|
49
|
-
|
|
50
|
-
|
|
22
|
+
has_many :contents, through: :elements
|
|
23
|
+
has_and_belongs_to_many :to_be_swept_elements, -> { distinct },
|
|
24
|
+
class_name: "Alchemy::Element",
|
|
25
|
+
join_table: ElementToPage.table_name
|
|
51
26
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
# @param source [Alchemy::Page]
|
|
55
|
-
# @param target [Alchemy::Page]
|
|
56
|
-
# @return [Array]
|
|
57
|
-
#
|
|
58
|
-
def copy_elements(source, target)
|
|
59
|
-
source_elements = source.all_elements.not_nested.not_trashed
|
|
60
|
-
source_elements.order(:position).map do |source_element|
|
|
61
|
-
Element.copy(source_element, {
|
|
62
|
-
page_id: target.id,
|
|
63
|
-
}).tap(&:move_to_bottom)
|
|
64
|
-
end
|
|
27
|
+
after_create :generate_elements,
|
|
28
|
+
unless: -> { autogenerate_elements == false }
|
|
65
29
|
end
|
|
66
|
-
end
|
|
67
30
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
#
|
|
88
|
-
def available_element_definitions(only_element_named = nil)
|
|
89
|
-
@_element_definitions ||= if only_element_named
|
|
90
|
-
definition = Element.definition_by_name(only_element_named)
|
|
91
|
-
element_definitions_by_name(definition["nestable_elements"])
|
|
92
|
-
else
|
|
93
|
-
element_definitions
|
|
31
|
+
module ClassMethods
|
|
32
|
+
# Copy page elements
|
|
33
|
+
#
|
|
34
|
+
# @param source [Alchemy::Page]
|
|
35
|
+
# @param target [Alchemy::Page]
|
|
36
|
+
# @return [Array]
|
|
37
|
+
#
|
|
38
|
+
def copy_elements(source, target)
|
|
39
|
+
repository = source.draft_version.element_repository
|
|
40
|
+
transaction do
|
|
41
|
+
Element.acts_as_list_no_update do
|
|
42
|
+
repository.not_nested.each.with_index(1) do |element, position|
|
|
43
|
+
Alchemy::DuplicateElement.new(element, repository: repository).call(
|
|
44
|
+
page_version_id: target.draft_version.id,
|
|
45
|
+
position: position,
|
|
46
|
+
)
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
94
50
|
end
|
|
51
|
+
end
|
|
95
52
|
|
|
96
|
-
|
|
53
|
+
# All available element definitions that can actually be placed on current page.
|
|
54
|
+
#
|
|
55
|
+
# It extracts all definitions that are unique or limited and already on page.
|
|
56
|
+
#
|
|
57
|
+
# == Example of unique element:
|
|
58
|
+
#
|
|
59
|
+
# - name: headline
|
|
60
|
+
# unique: true
|
|
61
|
+
# contents:
|
|
62
|
+
# - name: headline
|
|
63
|
+
# type: EssenceText
|
|
64
|
+
#
|
|
65
|
+
# == Example of limited element:
|
|
66
|
+
#
|
|
67
|
+
# - name: article
|
|
68
|
+
# amount: 2
|
|
69
|
+
# contents:
|
|
70
|
+
# - name: text
|
|
71
|
+
# type: EssenceRichtext
|
|
72
|
+
#
|
|
73
|
+
def available_element_definitions(only_element_named = nil)
|
|
74
|
+
@_element_definitions ||= if only_element_named
|
|
75
|
+
definition = Element.definition_by_name(only_element_named)
|
|
76
|
+
element_definitions_by_name(definition["nestable_elements"])
|
|
77
|
+
else
|
|
78
|
+
element_definitions
|
|
79
|
+
end
|
|
97
80
|
|
|
98
|
-
|
|
99
|
-
@_existing_element_names = existing_elements.pluck(:name)
|
|
100
|
-
delete_unique_element_definitions!
|
|
101
|
-
delete_outnumbered_element_definitions!
|
|
81
|
+
return [] if @_element_definitions.blank?
|
|
102
82
|
|
|
103
|
-
|
|
104
|
-
|
|
83
|
+
existing_elements = draft_version.elements.not_nested
|
|
84
|
+
@_existing_element_names = existing_elements.pluck(:name)
|
|
85
|
+
delete_unique_element_definitions!
|
|
86
|
+
delete_outnumbered_element_definitions!
|
|
105
87
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
def available_element_names
|
|
109
|
-
@_available_element_names ||= available_element_definitions.map { |e| e["name"] }
|
|
110
|
-
end
|
|
88
|
+
@_element_definitions
|
|
89
|
+
end
|
|
111
90
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
available_element_definitions(parent.name).reject do |e|
|
|
118
|
-
parents_unique_nested_elements.include? e["name"]
|
|
119
|
-
end
|
|
120
|
-
else
|
|
121
|
-
available_element_definitions
|
|
122
|
-
end
|
|
123
|
-
end
|
|
91
|
+
# All names of elements that can actually be placed on current page.
|
|
92
|
+
#
|
|
93
|
+
def available_element_names
|
|
94
|
+
@_available_element_names ||= available_element_definitions.map { |e| e["name"] }
|
|
95
|
+
end
|
|
124
96
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
97
|
+
# Available element definitions excluding nested unique elements.
|
|
98
|
+
#
|
|
99
|
+
def available_elements_within_current_scope(parent)
|
|
100
|
+
@_available_elements = if parent
|
|
101
|
+
parents_unique_nested_elements = parent.nested_elements.where(unique: true).pluck(:name)
|
|
102
|
+
available_element_definitions(parent.name).reject do |e|
|
|
103
|
+
parents_unique_nested_elements.include? e["name"]
|
|
104
|
+
end
|
|
105
|
+
else
|
|
106
|
+
available_element_definitions
|
|
107
|
+
end
|
|
108
|
+
end
|
|
133
109
|
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
110
|
+
# All element definitions defined for page's page layout
|
|
111
|
+
#
|
|
112
|
+
# Warning: Since elements can be unique or limited in number,
|
|
113
|
+
# it is more safe to ask for +available_element_definitions+
|
|
114
|
+
#
|
|
115
|
+
def element_definitions
|
|
116
|
+
@_element_definitions ||= element_definitions_by_name(element_definition_names)
|
|
140
117
|
end
|
|
141
|
-
definitions.uniq { |d| d["name"] }
|
|
142
|
-
end
|
|
143
118
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
def element_definition_names
|
|
154
|
-
definition["elements"] || []
|
|
155
|
-
end
|
|
119
|
+
# All element definitions defined for page's page layout including nestable element definitions
|
|
120
|
+
#
|
|
121
|
+
def descendent_element_definitions
|
|
122
|
+
definitions = element_definitions_by_name(element_definition_names)
|
|
123
|
+
definitions.select { |d| d.key?("nestable_elements") }.each do |d|
|
|
124
|
+
definitions += element_definitions_by_name(d["nestable_elements"])
|
|
125
|
+
end
|
|
126
|
+
definitions.uniq { |d| d["name"] }
|
|
127
|
+
end
|
|
156
128
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
Element.definitions
|
|
169
|
-
else
|
|
170
|
-
Element.definitions.select { |e| names.include? e["name"] }
|
|
129
|
+
# All names of elements that are defined in the page definition.
|
|
130
|
+
#
|
|
131
|
+
# Assign elements to a page in +config/alchemy/page_layouts.yml+.
|
|
132
|
+
#
|
|
133
|
+
# == Example of page_layouts.yml:
|
|
134
|
+
#
|
|
135
|
+
# - name: contact
|
|
136
|
+
# elements: [headline, contactform]
|
|
137
|
+
#
|
|
138
|
+
def element_definition_names
|
|
139
|
+
definition["elements"] || []
|
|
171
140
|
end
|
|
172
|
-
end
|
|
173
141
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
elements.named(definition["feed_elements"])
|
|
184
|
-
end
|
|
142
|
+
# Element definitions with given name(s)
|
|
143
|
+
#
|
|
144
|
+
# @param [Array || String]
|
|
145
|
+
# one or many Alchemy::Element names. Pass +'all'+ to get all Element definitions
|
|
146
|
+
# @return [Array]
|
|
147
|
+
# An Array of element definitions
|
|
148
|
+
#
|
|
149
|
+
def element_definitions_by_name(names)
|
|
150
|
+
return [] if names.blank?
|
|
185
151
|
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
.collect(&:id)
|
|
193
|
-
end
|
|
152
|
+
if names.to_s == "all"
|
|
153
|
+
Element.definitions
|
|
154
|
+
else
|
|
155
|
+
Element.definitions.select { |e| names.include? e["name"] }
|
|
156
|
+
end
|
|
157
|
+
end
|
|
194
158
|
|
|
195
|
-
|
|
159
|
+
# Returns all elements that should be feeded via rss.
|
|
160
|
+
#
|
|
161
|
+
# Define feedable elements in your +page_layouts.yml+:
|
|
162
|
+
#
|
|
163
|
+
# - name: news
|
|
164
|
+
# feed: true
|
|
165
|
+
# feed_elements: [element_name, element_2_name]
|
|
166
|
+
#
|
|
167
|
+
def feed_elements
|
|
168
|
+
elements.named(definition["feed_elements"])
|
|
169
|
+
end
|
|
196
170
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
next if existing_element_names.include?(element_name)
|
|
171
|
+
# Returns an array of all EssenceRichtext contents ids from not folded elements
|
|
172
|
+
#
|
|
173
|
+
def richtext_contents_ids
|
|
174
|
+
Alchemy::Content.joins(:element)
|
|
175
|
+
.where(Element.table_name => { page_version_id: draft_version.id, folded: false })
|
|
176
|
+
.select(&:has_tinymce?)
|
|
177
|
+
.collect(&:id)
|
|
178
|
+
end
|
|
206
179
|
|
|
207
|
-
|
|
180
|
+
# Returns an array of all Richtext ingredients ids from not folded elements
|
|
181
|
+
#
|
|
182
|
+
def richtext_ingredients_ids
|
|
183
|
+
Alchemy::Ingredient.richtexts.joins(:element)
|
|
184
|
+
.where(Element.table_name => { page_version_id: draft_version.id, folded: false })
|
|
185
|
+
.select(&:has_tinymce?)
|
|
186
|
+
.collect(&:id)
|
|
208
187
|
end
|
|
209
|
-
end
|
|
210
188
|
|
|
211
|
-
|
|
212
|
-
def trash_not_allowed_elements!
|
|
213
|
-
not_allowed_elements = elements.where([
|
|
214
|
-
"#{Element.table_name}.name NOT IN (?)",
|
|
215
|
-
element_definition_names,
|
|
216
|
-
])
|
|
217
|
-
not_allowed_elements.to_a.map(&:trash!)
|
|
218
|
-
end
|
|
219
|
-
deprecate :trash_not_allowed_elements!, deprecator: Alchemy::Deprecation
|
|
189
|
+
private
|
|
220
190
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
191
|
+
# Looks in the page_layout descripion, if there are elements to autogenerate.
|
|
192
|
+
#
|
|
193
|
+
# And if so, it generates them.
|
|
194
|
+
#
|
|
195
|
+
def generate_elements
|
|
196
|
+
definition.fetch("autogenerate", []).each do |element_name|
|
|
197
|
+
Element.create(page: self, page_version: draft_version, name: element_name)
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# Deletes unique and already present definitions from @_element_definitions.
|
|
202
|
+
#
|
|
203
|
+
def delete_unique_element_definitions!
|
|
204
|
+
@_element_definitions.delete_if do |element|
|
|
205
|
+
element["unique"] && @_existing_element_names.include?(element["name"])
|
|
206
|
+
end
|
|
226
207
|
end
|
|
227
|
-
end
|
|
228
208
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
209
|
+
# Deletes limited and outnumbered definitions from @_element_definitions.
|
|
210
|
+
#
|
|
211
|
+
def delete_outnumbered_element_definitions!
|
|
212
|
+
@_element_definitions.delete_if do |element|
|
|
213
|
+
outnumbered = @_existing_element_names.select { |name| name == element["name"] }
|
|
214
|
+
element["amount"] && outnumbered.count >= element["amount"].to_i
|
|
215
|
+
end
|
|
235
216
|
end
|
|
236
217
|
end
|
|
237
218
|
end
|