alchemy_cms 5.2.4 → 6.0.0.b1
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 +80 -25
- data/Gemfile +4 -2
- 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/admin.scss +1 -1
- data/app/assets/stylesheets/alchemy/archive.scss +4 -4
- 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/assets/stylesheets/tinymce/skins/alchemy/skin.min.css.scss +6 -6
- data/app/controllers/alchemy/admin/attachments_controller.rb +6 -2
- data/app/controllers/alchemy/admin/base_controller.rb +5 -7
- data/app/controllers/alchemy/admin/elements_controller.rb +58 -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 +6 -13
- data/app/controllers/alchemy/admin/pictures_controller.rb +35 -9
- data/app/controllers/alchemy/api/elements_controller.rb +10 -5
- data/app/controllers/alchemy/api/pages_controller.rb +2 -4
- 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 +23 -1
- data/app/decorators/alchemy/ingredient_editor.rb +154 -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 +22 -7
- 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 +1 -1
- data/app/models/alchemy/content/factory.rb +23 -27
- data/app/models/alchemy/content.rb +1 -6
- 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 +100 -98
- data/app/models/alchemy/element/element_ingredients.rb +176 -0
- data/app/models/alchemy/element/presenters.rb +89 -87
- data/app/models/alchemy/element.rb +40 -73
- 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 +219 -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/node.rb +1 -1
- 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 +113 -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.rb +67 -33
- data/app/models/alchemy/page_version.rb +58 -0
- 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/alchemy/picture.rb +4 -2
- 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 +1 -2
- 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/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/{trash.js.erb → destroy.js.erb} +1 -3
- 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/edit.html.erb +36 -24
- 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/pictures/_filter_and_size_bar.html.erb +4 -8
- 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/resources/_form.html.erb +1 -0
- 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_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 +50 -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 +25 -0
- data/app/views/alchemy/ingredients/_node_view.html.erb +1 -0
- data/app/views/alchemy/ingredients/_page_editor.html.erb +24 -0
- data/app/views/alchemy/ingredients/_page_view.html.erb +4 -0
- data/app/views/alchemy/ingredients/_picture_editor.html.erb +59 -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 +29 -0
- data/app/views/alchemy/ingredients/_select_view.html.erb +1 -0
- data/app/views/alchemy/ingredients/_text_editor.html.erb +19 -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 +23 -15
- 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/engine.rb +1 -1
- data/lib/alchemy/essence.rb +1 -2
- 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/tasks/tidy.rb +29 -0
- 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 +57 -0
- data/lib/alchemy/test_support.rb +2 -11
- data/lib/alchemy/tinymce.rb +17 -0
- data/lib/alchemy/upgrader/five_point_zero.rb +0 -32
- 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 +51 -0
- data/lib/alchemy/version.rb +1 -1
- 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/install_generator.rb +1 -2
- 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/tidy.rake +12 -0
- data/lib/tasks/alchemy/upgrade.rake +21 -15
- data/package/admin.js +9 -1
- data/package/src/file_editors.js +28 -0
- 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/package.json +3 -2
- data/vendor/assets/javascripts/jquery_plugins/jquery.Jcrop.min.js +3 -18
- data/vendor/assets/stylesheets/jquery.Jcrop.min.scss +2 -28
- metadata +285 -56
- 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/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/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 -20
|
@@ -1,108 +1,110 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Alchemy
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
class Element < BaseRecord
|
|
5
|
+
# Methods used for presenting an Alchemy Element.
|
|
6
|
+
#
|
|
7
|
+
module Presenters
|
|
8
|
+
extend ActiveSupport::Concern
|
|
9
|
+
|
|
10
|
+
module ClassMethods
|
|
11
|
+
# Human name for displaying elements in select boxes and element editor views.
|
|
12
|
+
#
|
|
13
|
+
# The name is beeing translated from given name value as described in +config/alchemy/elements.yml+
|
|
14
|
+
#
|
|
15
|
+
# Translate the name in your +config/locales+ language file.
|
|
16
|
+
#
|
|
17
|
+
# == Example:
|
|
18
|
+
#
|
|
19
|
+
# de:
|
|
20
|
+
# alchemy:
|
|
21
|
+
# element_names:
|
|
22
|
+
# contactform: 'Kontakt Formular'
|
|
23
|
+
#
|
|
24
|
+
# If no translation is found a humanized name is used.
|
|
25
|
+
#
|
|
26
|
+
def display_name_for(name)
|
|
27
|
+
Alchemy.t(name, scope: "element_names", default: name.to_s.humanize)
|
|
28
|
+
end
|
|
29
|
+
end
|
|
8
30
|
|
|
9
|
-
|
|
10
|
-
# Human name for displaying elements in select boxes and element editor views.
|
|
31
|
+
# Returns the translated name
|
|
11
32
|
#
|
|
12
|
-
#
|
|
33
|
+
# @see Alchemy::Element::Presenters#display_name_for
|
|
13
34
|
#
|
|
14
|
-
|
|
35
|
+
def display_name
|
|
36
|
+
self.class.display_name_for(definition["name"] || name)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Returns a preview text for element.
|
|
15
40
|
#
|
|
16
|
-
#
|
|
41
|
+
# It's taken from the first Content found in the +elements.yml+ definition file.
|
|
17
42
|
#
|
|
18
|
-
#
|
|
19
|
-
# alchemy:
|
|
20
|
-
# element_names:
|
|
21
|
-
# contactform: 'Kontakt Formular'
|
|
43
|
+
# You can flag a Content as +as_element_title+ to take this as preview.
|
|
22
44
|
#
|
|
23
|
-
#
|
|
45
|
+
# @param maxlength [Fixnum] (60)
|
|
46
|
+
# Length of characters after the text will be cut off.
|
|
24
47
|
#
|
|
25
|
-
def
|
|
26
|
-
|
|
48
|
+
def preview_text(maxlength = 60)
|
|
49
|
+
preview_text_from_preview_content(maxlength) || preview_text_from_nested_elements(maxlength)
|
|
27
50
|
end
|
|
28
|
-
end
|
|
29
51
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# A Element described as:
|
|
58
|
-
#
|
|
59
|
-
# - name: funky_element
|
|
60
|
-
# display_name: Funky Element
|
|
61
|
-
# contents:
|
|
62
|
-
# - name: headline
|
|
63
|
-
# type: EssenceText
|
|
64
|
-
# - name: text
|
|
65
|
-
# type EssenceRichtext
|
|
66
|
-
# as_element_title: true
|
|
67
|
-
#
|
|
68
|
-
# With "I want to tell you a funky story" as stripped_body for the EssenceRichtext Content produces:
|
|
69
|
-
#
|
|
70
|
-
# Funky Element: I want to tell ...
|
|
71
|
-
#
|
|
72
|
-
# @param maxlength [Fixnum] (30)
|
|
73
|
-
# Length of characters after the text will be cut off.
|
|
74
|
-
#
|
|
75
|
-
def display_name_with_preview_text(maxlength = 30)
|
|
76
|
-
"#{display_name}: #{preview_text(maxlength)}"
|
|
77
|
-
end
|
|
52
|
+
# Generates a preview text containing Element#display_name and Element#preview_text.
|
|
53
|
+
#
|
|
54
|
+
# It is displayed inside the head of the Element in the Elements.list overlay window from the Alchemy Admin::Page#edit view.
|
|
55
|
+
#
|
|
56
|
+
# === Example
|
|
57
|
+
#
|
|
58
|
+
# A Element described as:
|
|
59
|
+
#
|
|
60
|
+
# - name: funky_element
|
|
61
|
+
# display_name: Funky Element
|
|
62
|
+
# contents:
|
|
63
|
+
# - name: headline
|
|
64
|
+
# type: EssenceText
|
|
65
|
+
# - name: text
|
|
66
|
+
# type EssenceRichtext
|
|
67
|
+
# as_element_title: true
|
|
68
|
+
#
|
|
69
|
+
# With "I want to tell you a funky story" as stripped_body for the EssenceRichtext Content produces:
|
|
70
|
+
#
|
|
71
|
+
# Funky Element: I want to tell ...
|
|
72
|
+
#
|
|
73
|
+
# @param maxlength [Fixnum] (30)
|
|
74
|
+
# Length of characters after the text will be cut off.
|
|
75
|
+
#
|
|
76
|
+
def display_name_with_preview_text(maxlength = 30)
|
|
77
|
+
"#{display_name}: #{preview_text(maxlength)}"
|
|
78
|
+
end
|
|
78
79
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
80
|
+
# Returns a dom id used for elements html id tag.
|
|
81
|
+
#
|
|
82
|
+
def dom_id
|
|
83
|
+
"#{name}_#{id}"
|
|
84
|
+
end
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
86
|
+
# The content that's used for element's preview text.
|
|
87
|
+
#
|
|
88
|
+
# It tries to find one of element's contents that is defined +as_element_title+.
|
|
89
|
+
# Takes element's first content if no content is defined +as_element_title+.
|
|
90
|
+
#
|
|
91
|
+
# @return (Alchemy::Content)
|
|
92
|
+
#
|
|
93
|
+
def preview_content
|
|
94
|
+
@_preview_content ||= contents.detect(&:preview_content?) || contents.first
|
|
95
|
+
end
|
|
95
96
|
|
|
96
|
-
|
|
97
|
+
private
|
|
97
98
|
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
def preview_text_from_nested_elements(maxlength)
|
|
100
|
+
return if all_nested_elements.empty?
|
|
100
101
|
|
|
101
|
-
|
|
102
|
-
|
|
102
|
+
all_nested_elements.first.preview_text(maxlength)
|
|
103
|
+
end
|
|
103
104
|
|
|
104
|
-
|
|
105
|
-
|
|
105
|
+
def preview_text_from_preview_content(maxlength)
|
|
106
|
+
preview_content.try!(:preview_text, maxlength)
|
|
107
|
+
end
|
|
106
108
|
end
|
|
107
109
|
end
|
|
108
110
|
end
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
# id :integer not null, primary key
|
|
8
8
|
# name :string
|
|
9
9
|
# position :integer
|
|
10
|
-
#
|
|
10
|
+
# page_version_id :integer not null
|
|
11
11
|
# public :boolean default(TRUE)
|
|
12
12
|
# fixed :boolean default(FALSE)
|
|
13
13
|
# folded :boolean default(FALSE)
|
|
@@ -20,6 +20,12 @@
|
|
|
20
20
|
# parent_element_id :integer
|
|
21
21
|
#
|
|
22
22
|
|
|
23
|
+
require_dependency "alchemy/element/definitions"
|
|
24
|
+
require_dependency "alchemy/element/element_contents"
|
|
25
|
+
require_dependency "alchemy/element/element_ingredients"
|
|
26
|
+
require_dependency "alchemy/element/element_essences"
|
|
27
|
+
require_dependency "alchemy/element/presenters"
|
|
28
|
+
|
|
23
29
|
module Alchemy
|
|
24
30
|
class Element < BaseRecord
|
|
25
31
|
NAME_REGEXP = /\A[a-z0-9_-]+\z/
|
|
@@ -34,39 +40,31 @@ module Alchemy
|
|
|
34
40
|
"nestable_elements",
|
|
35
41
|
"contents",
|
|
36
42
|
"hint",
|
|
43
|
+
"ingredients",
|
|
37
44
|
"taggable",
|
|
38
45
|
"compact",
|
|
39
46
|
"message",
|
|
40
47
|
"deprecated",
|
|
41
48
|
].freeze
|
|
42
49
|
|
|
43
|
-
|
|
44
|
-
"cached_tag_list",
|
|
45
|
-
"created_at",
|
|
46
|
-
"creator_id",
|
|
47
|
-
"id",
|
|
48
|
-
"folded",
|
|
49
|
-
"position",
|
|
50
|
-
"updated_at",
|
|
51
|
-
"updater_id",
|
|
52
|
-
].freeze
|
|
53
|
-
|
|
54
|
-
# All Elements that share the same page id and parent element id and are fixed or not are considered a list.
|
|
50
|
+
# All Elements that share the same page version and parent element and are fixed or not are considered a list.
|
|
55
51
|
#
|
|
56
|
-
# If
|
|
52
|
+
# If parent_element_id is nil (typical case for a simple page),
|
|
57
53
|
# then all elements on that page are still in one list,
|
|
58
54
|
# because acts_as_list correctly creates this statement:
|
|
59
55
|
#
|
|
60
|
-
# WHERE
|
|
56
|
+
# WHERE page_version_id = 1 and fixed = FALSE AND parent_element_id = NULL
|
|
61
57
|
#
|
|
62
|
-
acts_as_list scope: [:
|
|
58
|
+
acts_as_list scope: [:page_version_id, :fixed, :parent_element_id]
|
|
63
59
|
|
|
64
|
-
stampable stamper_class_name: Alchemy.
|
|
60
|
+
stampable stamper_class_name: Alchemy.user_class_name
|
|
65
61
|
|
|
66
62
|
has_many :contents, dependent: :destroy, inverse_of: :element
|
|
67
63
|
|
|
64
|
+
before_destroy :delete_all_nested_elements
|
|
65
|
+
|
|
68
66
|
has_many :all_nested_elements,
|
|
69
|
-
-> { order(:position)
|
|
67
|
+
-> { order(:position) },
|
|
70
68
|
class_name: "Alchemy::Element",
|
|
71
69
|
foreign_key: :parent_element_id,
|
|
72
70
|
dependent: :destroy
|
|
@@ -78,7 +76,8 @@ module Alchemy
|
|
|
78
76
|
dependent: :destroy,
|
|
79
77
|
inverse_of: :parent_element
|
|
80
78
|
|
|
81
|
-
belongs_to :
|
|
79
|
+
belongs_to :page_version, touch: true, inverse_of: :elements
|
|
80
|
+
has_one :page, through: :page_version
|
|
82
81
|
|
|
83
82
|
# A nested element belongs to a parent element.
|
|
84
83
|
belongs_to :parent_element,
|
|
@@ -101,11 +100,10 @@ module Alchemy
|
|
|
101
100
|
|
|
102
101
|
after_update :touch_touchable_pages
|
|
103
102
|
|
|
104
|
-
scope :trashed, -> { where(position: nil).order("updated_at DESC") }
|
|
105
|
-
scope :not_trashed, -> { where.not(position: nil) }
|
|
106
103
|
scope :published, -> { where(public: true) }
|
|
104
|
+
scope :hidden, -> { where(public: false) }
|
|
107
105
|
scope :not_restricted, -> { joins(:page).merge(Page.not_restricted) }
|
|
108
|
-
scope :available, -> { published
|
|
106
|
+
scope :available, -> { published }
|
|
109
107
|
scope :named, ->(names) { where(name: names) }
|
|
110
108
|
scope :excluded, ->(names) { where.not(name: names) }
|
|
111
109
|
scope :fixed, -> { where(fixed: true) }
|
|
@@ -118,16 +116,14 @@ module Alchemy
|
|
|
118
116
|
delegate :restricted?, to: :page, allow_nil: true
|
|
119
117
|
|
|
120
118
|
# Concerns
|
|
121
|
-
include
|
|
122
|
-
include
|
|
123
|
-
include
|
|
124
|
-
include
|
|
119
|
+
include Definitions
|
|
120
|
+
include ElementContents
|
|
121
|
+
include ElementEssences
|
|
122
|
+
include ElementIngredients
|
|
123
|
+
include Presenters
|
|
125
124
|
|
|
126
125
|
# class methods
|
|
127
126
|
class << self
|
|
128
|
-
deprecate :trashed, deprecator: Alchemy::Deprecation
|
|
129
|
-
deprecate :not_trashed, deprecator: Alchemy::Deprecation
|
|
130
|
-
|
|
131
127
|
# Builds a new element as described in +/config/alchemy/elements.yml+
|
|
132
128
|
#
|
|
133
129
|
# - Returns a new Alchemy::Element object if no name is given in attributes,
|
|
@@ -159,26 +155,7 @@ module Alchemy
|
|
|
159
155
|
# @copy.public? # => false
|
|
160
156
|
#
|
|
161
157
|
def copy(source_element, differences = {})
|
|
162
|
-
|
|
163
|
-
.except(*SKIPPED_ATTRIBUTES_ON_COPY)
|
|
164
|
-
.merge(differences)
|
|
165
|
-
.merge({
|
|
166
|
-
autogenerate_contents: false,
|
|
167
|
-
autogenerate_nested_elements: false,
|
|
168
|
-
tag_list: source_element.tag_list,
|
|
169
|
-
})
|
|
170
|
-
|
|
171
|
-
new_element = create!(attributes)
|
|
172
|
-
|
|
173
|
-
if source_element.contents.any?
|
|
174
|
-
source_element.copy_contents_to(new_element)
|
|
175
|
-
end
|
|
176
|
-
|
|
177
|
-
if source_element.nested_elements.any?
|
|
178
|
-
source_element.copy_nested_elements_to(new_element)
|
|
179
|
-
end
|
|
180
|
-
|
|
181
|
-
new_element
|
|
158
|
+
Alchemy::DuplicateElement.new(source_element).call(differences)
|
|
182
159
|
end
|
|
183
160
|
|
|
184
161
|
def all_from_clipboard(clipboard)
|
|
@@ -225,19 +202,6 @@ module Alchemy
|
|
|
225
202
|
end
|
|
226
203
|
end
|
|
227
204
|
|
|
228
|
-
# Trashing an element means nullifying its position, folding and unpublishing it.
|
|
229
|
-
def trash!
|
|
230
|
-
self.public = false
|
|
231
|
-
self.folded = true
|
|
232
|
-
remove_from_list
|
|
233
|
-
end
|
|
234
|
-
deprecate :trash!, deprecator: Alchemy::Deprecation
|
|
235
|
-
|
|
236
|
-
def trashed?
|
|
237
|
-
position.nil?
|
|
238
|
-
end
|
|
239
|
-
deprecate :trashed?, deprecator: Alchemy::Deprecation
|
|
240
|
-
|
|
241
205
|
# Returns true if the definition of this element has a taggable true value.
|
|
242
206
|
def taggable?
|
|
243
207
|
definition["taggable"] == true
|
|
@@ -322,22 +286,12 @@ module Alchemy
|
|
|
322
286
|
definition.fetch("nestable_elements", [])
|
|
323
287
|
end
|
|
324
288
|
|
|
325
|
-
# Copy all nested elements from current element to given target element.
|
|
326
|
-
def copy_nested_elements_to(target_element)
|
|
327
|
-
nested_elements.map do |nested_element|
|
|
328
|
-
Element.copy(nested_element, {
|
|
329
|
-
parent_element_id: target_element.id,
|
|
330
|
-
page_id: target_element.page_id,
|
|
331
|
-
})
|
|
332
|
-
end
|
|
333
|
-
end
|
|
334
|
-
|
|
335
289
|
private
|
|
336
290
|
|
|
337
291
|
def generate_nested_elements
|
|
338
292
|
definition.fetch("autogenerate", []).each do |nestable_element|
|
|
339
293
|
if nestable_elements.include?(nestable_element)
|
|
340
|
-
Element.create(
|
|
294
|
+
Element.create(page_version: page_version, parent_element_id: id, name: nestable_element)
|
|
341
295
|
else
|
|
342
296
|
log_warning("Element '#{nestable_element}' not a nestable element for '#{name}'. Skipping!")
|
|
343
297
|
end
|
|
@@ -358,5 +312,18 @@ module Alchemy
|
|
|
358
312
|
|
|
359
313
|
touchable_pages.each(&:touch)
|
|
360
314
|
end
|
|
315
|
+
|
|
316
|
+
def delete_all_nested_elements
|
|
317
|
+
deeply_nested_elements = descendent_elements(self).flatten
|
|
318
|
+
DeleteElements.new(deeply_nested_elements).call
|
|
319
|
+
nested_elements.reset
|
|
320
|
+
all_nested_elements.reset
|
|
321
|
+
end
|
|
322
|
+
|
|
323
|
+
def descendent_elements(element)
|
|
324
|
+
element.all_nested_elements + element.all_nested_elements.map do |nested_element|
|
|
325
|
+
descendent_elements(nested_element)
|
|
326
|
+
end
|
|
327
|
+
end
|
|
361
328
|
end
|
|
362
329
|
end
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
# Mimics ActiveRecord query interface
|
|
5
|
+
# but does this on the preloaded elements
|
|
6
|
+
class ElementsRepository
|
|
7
|
+
include Enumerable
|
|
8
|
+
|
|
9
|
+
# An empty set of elements
|
|
10
|
+
def self.none
|
|
11
|
+
new([])
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# @param [ActiveRecord::Relation]
|
|
15
|
+
def initialize(elements)
|
|
16
|
+
@elements = elements.to_a
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# All visible elements
|
|
20
|
+
# @return [Alchemy::ElementRepository]
|
|
21
|
+
def visible
|
|
22
|
+
self.class.new select(&:public)
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# All not fixed elements
|
|
26
|
+
# @return [Alchemy::ElementRepository]
|
|
27
|
+
def hidden
|
|
28
|
+
self.class.new reject(&:public)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
# All elements with given name(s)
|
|
32
|
+
# @param [Array<String|Symbol>|String|Symbol]
|
|
33
|
+
# @return [Alchemy::ElementRepository]
|
|
34
|
+
def named(*names)
|
|
35
|
+
names.flatten!
|
|
36
|
+
self.class.new(select { |e| e.name.in?(names.map!(&:to_s)) })
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# Filter elements by given attribute and value
|
|
40
|
+
# @param [Array|Hash]
|
|
41
|
+
# @return [Alchemy::ElementRepository]
|
|
42
|
+
def where(attrs)
|
|
43
|
+
self.class.new(
|
|
44
|
+
select do |element|
|
|
45
|
+
attrs.all? do |attr, value|
|
|
46
|
+
element.public_send(attr) == value
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# All elements excluding those wth given name(s)
|
|
53
|
+
# @param [Array<String|Symbol>|String|Symbol]
|
|
54
|
+
# @return [Alchemy::ElementRepository]
|
|
55
|
+
def excluded(*names)
|
|
56
|
+
names.flatten!
|
|
57
|
+
self.class.new(reject { |e| e.name.in?(names.map!(&:to_s)) })
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# All fixed elements
|
|
61
|
+
# @return [Alchemy::ElementRepository]
|
|
62
|
+
def fixed
|
|
63
|
+
self.class.new select(&:fixed)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# All not fixed elements
|
|
67
|
+
# @return [Alchemy::ElementRepository]
|
|
68
|
+
def unfixed
|
|
69
|
+
self.class.new reject(&:fixed)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# All folded elements
|
|
73
|
+
# @return [Alchemy::ElementRepository]
|
|
74
|
+
def folded
|
|
75
|
+
self.class.new select(&:folded)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# All expanded elements
|
|
79
|
+
# @return [Alchemy::ElementRepository]
|
|
80
|
+
def expanded
|
|
81
|
+
self.class.new reject(&:folded)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# All not nested top level elements
|
|
85
|
+
# @return [Alchemy::ElementRepository]
|
|
86
|
+
def not_nested
|
|
87
|
+
self.class.new(select { |e| e.parent_element_id.nil? })
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Elements in reversed order
|
|
91
|
+
# @return [Alchemy::ElementRepository]
|
|
92
|
+
def reverse
|
|
93
|
+
self.class.new elements.reverse
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# Elements in random order
|
|
97
|
+
# @return [Alchemy::ElementRepository]
|
|
98
|
+
def random
|
|
99
|
+
self.class.new Array(elements).shuffle
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
# Elements off setted by
|
|
103
|
+
# @return [Alchemy::ElementRepository]
|
|
104
|
+
def offset(offset)
|
|
105
|
+
self.class.new elements[offset.to_i..-1]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# Elements limitted by
|
|
109
|
+
# @return [Alchemy::ElementRepository]
|
|
110
|
+
def limit(limit)
|
|
111
|
+
self.class.new elements[0..(limit.to_i - 1)]
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def children_of(parent)
|
|
115
|
+
self.class.new(select { |e| e.parent_element_id == parent.id })
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
def each(&blk)
|
|
119
|
+
elements.each(&blk)
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
private
|
|
123
|
+
|
|
124
|
+
attr_reader :elements
|
|
125
|
+
end
|
|
126
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class EssenceHeadline < BaseRecord
|
|
5
|
+
acts_as_essence
|
|
6
|
+
|
|
7
|
+
after_initialize :set_level_and_size
|
|
8
|
+
|
|
9
|
+
def preview_text(maxlength = 30)
|
|
10
|
+
"H#{level}: #{body}"[0..maxlength - 1]
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def level_options
|
|
14
|
+
levels.map { |level| ["H#{level}", level] }
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def size_options
|
|
18
|
+
sizes.map { |size| ["H#{size}", size] }
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
private
|
|
22
|
+
|
|
23
|
+
def content_settings
|
|
24
|
+
content&.settings || {}
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def levels
|
|
28
|
+
content_settings.fetch(:levels, (1..6))
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def sizes
|
|
32
|
+
content_settings.fetch(:sizes, [])
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def set_level_and_size
|
|
36
|
+
self.level ||= levels.first
|
|
37
|
+
self.size ||= sizes.first
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|