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
|
@@ -1,151 +1,160 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
# Methods concerning contents for elements
|
|
4
|
-
#
|
|
5
3
|
module Alchemy
|
|
6
|
-
|
|
7
|
-
#
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
4
|
+
class Element < BaseRecord
|
|
5
|
+
# Methods concerning contents for elements
|
|
6
|
+
#
|
|
7
|
+
module ElementContents
|
|
8
|
+
# Find first content from element by given name.
|
|
9
|
+
def content_by_name(name)
|
|
10
|
+
contents_by_name(name).first
|
|
11
|
+
end
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
# Find first content from element by given essence type.
|
|
14
|
+
def content_by_type(essence_type)
|
|
15
|
+
contents_by_type(essence_type).first
|
|
16
|
+
end
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
# All contents from element by given name.
|
|
19
|
+
def contents_by_name(name)
|
|
20
|
+
contents.select { |content| content.name == name.to_s }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
alias_method :all_contents_by_name, :contents_by_name
|
|
22
24
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
# All contents from element by given essence type.
|
|
26
|
+
def contents_by_type(essence_type)
|
|
27
|
+
contents.select do |content|
|
|
28
|
+
content.essence_type == Content.normalize_essence_type(essence_type)
|
|
29
|
+
end
|
|
27
30
|
end
|
|
28
|
-
end
|
|
29
|
-
alias_method :all_contents_by_type, :contents_by_type
|
|
30
31
|
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
32
|
+
alias_method :all_contents_by_type, :contents_by_type
|
|
33
|
+
|
|
34
|
+
# Updates all related contents by calling +update_essence+ on each of them.
|
|
35
|
+
#
|
|
36
|
+
# @param contents_attributes [Hash]
|
|
37
|
+
# Hash of contents attributes.
|
|
38
|
+
# The keys has to be the #id of the content to update.
|
|
39
|
+
# The values a Hash of attribute names and values
|
|
40
|
+
#
|
|
41
|
+
# @return [Boolean]
|
|
42
|
+
# True if +errors+ are blank or +contents_attributes+ hash is nil
|
|
43
|
+
#
|
|
44
|
+
# == Example
|
|
45
|
+
#
|
|
46
|
+
# @element.update_contents(
|
|
47
|
+
# "1" => {ingredient: "Title"},
|
|
48
|
+
# "2" => {link: "https://google.com"}
|
|
49
|
+
# )
|
|
50
|
+
#
|
|
51
|
+
def update_contents(contents_attributes)
|
|
52
|
+
return true if contents_attributes.nil?
|
|
53
|
+
|
|
54
|
+
contents.each do |content|
|
|
55
|
+
content_hash = contents_attributes[content.id.to_s] || next
|
|
56
|
+
content.update_essence(content_hash) || errors.add(:base, :essence_validation_failed)
|
|
57
|
+
end
|
|
58
|
+
errors.blank?
|
|
59
|
+
end
|
|
50
60
|
|
|
51
|
-
contents
|
|
52
|
-
|
|
53
|
-
|
|
61
|
+
# Copy current content's contents to given target element
|
|
62
|
+
def copy_contents_to(element)
|
|
63
|
+
contents.map do |content|
|
|
64
|
+
Content.copy(content, element_id: element.id)
|
|
65
|
+
end
|
|
54
66
|
end
|
|
55
|
-
errors.blank?
|
|
56
|
-
end
|
|
57
67
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
68
|
+
# Returns the content that is marked as rss title.
|
|
69
|
+
#
|
|
70
|
+
# Mark a content as rss title in your +elements.yml+ file:
|
|
71
|
+
#
|
|
72
|
+
# - name: news
|
|
73
|
+
# contents:
|
|
74
|
+
# - name: headline
|
|
75
|
+
# type: EssenceText
|
|
76
|
+
# rss_title: true
|
|
77
|
+
#
|
|
78
|
+
def content_for_rss_title
|
|
79
|
+
content_for_rss_meta("title")
|
|
62
80
|
end
|
|
63
|
-
end
|
|
64
81
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
# Returns the content that is marked as rss description.
|
|
83
|
+
#
|
|
84
|
+
# Mark a content as rss description in your +elements.yml+ file:
|
|
85
|
+
#
|
|
86
|
+
# - name: news
|
|
87
|
+
# contents:
|
|
88
|
+
# - name: body
|
|
89
|
+
# type: EssenceRichtext
|
|
90
|
+
# rss_description: true
|
|
91
|
+
#
|
|
92
|
+
def content_for_rss_description
|
|
93
|
+
content_for_rss_meta("description")
|
|
94
|
+
end
|
|
78
95
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
#
|
|
83
|
-
# - name: news
|
|
84
|
-
# contents:
|
|
85
|
-
# - name: body
|
|
86
|
-
# type: EssenceRichtext
|
|
87
|
-
# rss_description: true
|
|
88
|
-
#
|
|
89
|
-
def content_for_rss_description
|
|
90
|
-
content_for_rss_meta("description")
|
|
91
|
-
end
|
|
96
|
+
# Returns the array with the hashes for all element contents in the elements.yml file
|
|
97
|
+
def content_definitions
|
|
98
|
+
return nil if definition.blank?
|
|
92
99
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
return nil if definition.blank?
|
|
100
|
+
definition["contents"]
|
|
101
|
+
end
|
|
96
102
|
|
|
97
|
-
definition
|
|
98
|
-
|
|
103
|
+
# Returns the definition for given content_name
|
|
104
|
+
def content_definition_for(content_name)
|
|
105
|
+
if content_definitions.blank?
|
|
106
|
+
log_warning "Element #{name} is missing the content definition for #{content_name}"
|
|
107
|
+
nil
|
|
108
|
+
else
|
|
109
|
+
content_definitions.detect { |d| d["name"] == content_name.to_s }
|
|
110
|
+
end
|
|
111
|
+
end
|
|
99
112
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
113
|
+
# Returns an array of all EssenceRichtext contents ids from elements
|
|
114
|
+
#
|
|
115
|
+
# This is used to re-initialize the TinyMCE editor in the element editor.
|
|
116
|
+
#
|
|
117
|
+
def richtext_contents_ids
|
|
118
|
+
# This is not very efficient SQL wise I know, but we need to iterate
|
|
119
|
+
# recursivly through all descendent elements and I don't know how to do this
|
|
120
|
+
# in pure SQL. Anyone with a better idea is welcome to submit a patch.
|
|
121
|
+
ids = contents.select(&:has_tinymce?).collect(&:id)
|
|
122
|
+
expanded_nested_elements = nested_elements.expanded
|
|
123
|
+
if expanded_nested_elements.present?
|
|
124
|
+
ids += expanded_nested_elements.collect(&:richtext_contents_ids)
|
|
125
|
+
end
|
|
126
|
+
ids.flatten
|
|
107
127
|
end
|
|
108
|
-
end
|
|
109
128
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
#
|
|
114
|
-
def richtext_contents_ids
|
|
115
|
-
# This is not very efficient SQL wise I know, but we need to iterate
|
|
116
|
-
# recursivly through all descendent elements and I don't know how to do this
|
|
117
|
-
# in pure SQL. Anyone with a better idea is welcome to submit a patch.
|
|
118
|
-
ids = contents.select(&:has_tinymce?).collect(&:id)
|
|
119
|
-
expanded_nested_elements = nested_elements.expanded
|
|
120
|
-
if expanded_nested_elements.present?
|
|
121
|
-
ids += expanded_nested_elements.collect(&:richtext_contents_ids)
|
|
129
|
+
# True, if any of the element's contents has essence validations defined.
|
|
130
|
+
def has_validations?
|
|
131
|
+
!contents.detect(&:has_validations?).blank?
|
|
122
132
|
end
|
|
123
|
-
ids.flatten
|
|
124
|
-
end
|
|
125
133
|
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
134
|
+
# All element contents where the essence validation has failed.
|
|
135
|
+
def contents_with_errors
|
|
136
|
+
contents.select(&:essence_validation_failed?)
|
|
137
|
+
end
|
|
130
138
|
|
|
131
|
-
|
|
132
|
-
def contents_with_errors
|
|
133
|
-
contents.select(&:essence_validation_failed?)
|
|
134
|
-
end
|
|
139
|
+
private
|
|
135
140
|
|
|
136
|
-
|
|
141
|
+
def content_for_rss_meta(type)
|
|
142
|
+
definition = content_definitions.detect { |c| c["rss_#{type}"] }
|
|
143
|
+
return if definition.blank?
|
|
137
144
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
return if definition.blank?
|
|
145
|
+
contents.detect { |content| content.name == definition["name"] }
|
|
146
|
+
end
|
|
141
147
|
|
|
142
|
-
contents
|
|
143
|
-
|
|
148
|
+
# creates the contents for this element as described in the elements.yml
|
|
149
|
+
#
|
|
150
|
+
# If ingredients are defined as well no contents get created,
|
|
151
|
+
# ingredients get created instead.
|
|
152
|
+
def create_contents
|
|
153
|
+
return if definition.fetch(:ingredients, []).any?
|
|
144
154
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
Content.create(attributes.merge(element: self))
|
|
155
|
+
definition.fetch("contents", []).each do |attributes|
|
|
156
|
+
Content.create(attributes.merge(element: self))
|
|
157
|
+
end
|
|
149
158
|
end
|
|
150
159
|
end
|
|
151
160
|
end
|
|
@@ -1,112 +1,125 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Alchemy
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
4
|
+
class Element < BaseRecord
|
|
5
|
+
module ElementEssences
|
|
6
|
+
# Returns the contents essence value (aka. ingredient) for passed content name.
|
|
7
|
+
def ingredient(name)
|
|
8
|
+
ing = ingredient_by_role(name)
|
|
9
|
+
if ing
|
|
10
|
+
Alchemy::Deprecation.warn <<~WARN
|
|
11
|
+
Using `element.ingredient` to get the value of an ingredient is deprecated and will change in Alchemy 6.1
|
|
12
|
+
If you want to read the value of an elements ingredient please use `element.value_for(:ingredient_role)` instead.
|
|
13
|
+
The next version of Alchemy will return a `Alchemy::Ingredient` record instead.
|
|
14
|
+
WARN
|
|
15
|
+
ing.value
|
|
16
|
+
else
|
|
17
|
+
content = content_by_name(name)
|
|
18
|
+
return nil if content.blank?
|
|
9
19
|
|
|
10
|
-
|
|
11
|
-
|
|
20
|
+
content.ingredient
|
|
21
|
+
end
|
|
22
|
+
end
|
|
12
23
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
24
|
+
# True if the element has a content for given name,
|
|
25
|
+
# that has an essence value (aka. ingredient) that is not blank.
|
|
26
|
+
def has_ingredient?(name)
|
|
27
|
+
ingredient(name).present?
|
|
28
|
+
end
|
|
29
|
+
deprecate has_ingredient?: :has_value_for?, deprecator: Alchemy::Deprecation
|
|
18
30
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
# Returns all essence errors in the format of:
|
|
32
|
+
#
|
|
33
|
+
# {
|
|
34
|
+
# content.name => [
|
|
35
|
+
# error_message_for_validation_1,
|
|
36
|
+
# error_message_for_validation_2
|
|
37
|
+
# ]
|
|
38
|
+
# }
|
|
39
|
+
#
|
|
40
|
+
# Get translated error messages with +Element#essence_error_messages+
|
|
41
|
+
#
|
|
42
|
+
def essence_errors
|
|
43
|
+
essence_errors = {}
|
|
44
|
+
contents.each do |content|
|
|
45
|
+
if content.essence_validation_failed?
|
|
46
|
+
essence_errors[content.name] = content.essence.validation_errors
|
|
47
|
+
end
|
|
35
48
|
end
|
|
49
|
+
essence_errors
|
|
36
50
|
end
|
|
37
|
-
essence_errors
|
|
38
|
-
end
|
|
39
51
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
52
|
+
# Essence validation errors
|
|
53
|
+
#
|
|
54
|
+
# == Error messages are translated via I18n
|
|
55
|
+
#
|
|
56
|
+
# Inside your translation file add translations like:
|
|
57
|
+
#
|
|
58
|
+
# alchemy:
|
|
59
|
+
# content_validations:
|
|
60
|
+
# name_of_the_element:
|
|
61
|
+
# name_of_the_content:
|
|
62
|
+
# validation_error_type: Error Message
|
|
63
|
+
#
|
|
64
|
+
# NOTE: +validation_error_type+ has to be one of:
|
|
65
|
+
#
|
|
66
|
+
# * blank
|
|
67
|
+
# * taken
|
|
68
|
+
# * invalid
|
|
69
|
+
#
|
|
70
|
+
# === Example:
|
|
71
|
+
#
|
|
72
|
+
# de:
|
|
73
|
+
# alchemy:
|
|
74
|
+
# content_validations:
|
|
75
|
+
# contactform:
|
|
76
|
+
# email:
|
|
77
|
+
# invalid: 'Die Email hat nicht das richtige Format'
|
|
78
|
+
#
|
|
79
|
+
#
|
|
80
|
+
# == Error message translation fallbacks
|
|
81
|
+
#
|
|
82
|
+
# In order to not translate every single content for every element
|
|
83
|
+
# you can provide default error messages per content name:
|
|
84
|
+
#
|
|
85
|
+
# === Example
|
|
86
|
+
#
|
|
87
|
+
# en:
|
|
88
|
+
# alchemy:
|
|
89
|
+
# content_validations:
|
|
90
|
+
# fields:
|
|
91
|
+
# email:
|
|
92
|
+
# invalid: E-Mail has wrong format
|
|
93
|
+
# blank: E-Mail can't be blank
|
|
94
|
+
#
|
|
95
|
+
# And even further you can provide general field agnostic error messages:
|
|
96
|
+
#
|
|
97
|
+
# === Example
|
|
98
|
+
#
|
|
99
|
+
# en:
|
|
100
|
+
# alchemy:
|
|
101
|
+
# content_validations:
|
|
102
|
+
# errors:
|
|
103
|
+
# invalid: %{field} has wrong format
|
|
104
|
+
# blank: %{field} can't be blank
|
|
105
|
+
#
|
|
106
|
+
def essence_error_messages
|
|
107
|
+
messages = []
|
|
108
|
+
essence_errors.each do |content_name, errors|
|
|
109
|
+
errors.each do |error|
|
|
110
|
+
messages << Alchemy.t(
|
|
111
|
+
"#{name}.#{content_name}.#{error}",
|
|
112
|
+
scope: "content_validations",
|
|
113
|
+
default: [
|
|
114
|
+
"fields.#{content_name}.#{error}".to_sym,
|
|
115
|
+
"errors.#{error}".to_sym,
|
|
116
|
+
],
|
|
117
|
+
field: Content.translated_label_for(content_name, name),
|
|
118
|
+
)
|
|
119
|
+
end
|
|
107
120
|
end
|
|
121
|
+
messages
|
|
108
122
|
end
|
|
109
|
-
messages
|
|
110
123
|
end
|
|
111
124
|
end
|
|
112
125
|
end
|