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
|
@@ -66,8 +66,8 @@ module Alchemy
|
|
|
66
66
|
#
|
|
67
67
|
# renders +app/views/alchemy/site_layouts/_default_site.html.erb+ for the site named "Default Site".
|
|
68
68
|
#
|
|
69
|
-
def render_site_layout
|
|
70
|
-
render current_alchemy_site
|
|
69
|
+
def render_site_layout(&block)
|
|
70
|
+
render current_alchemy_site, &block
|
|
71
71
|
rescue ActionView::MissingTemplate
|
|
72
72
|
warning("Site layout for #{current_alchemy_site.try(:name)} not found. Please run `rails g alchemy:site_layouts`")
|
|
73
73
|
""
|
|
@@ -98,14 +98,6 @@ module Alchemy
|
|
|
98
98
|
WARN
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
-
# Returns true if page is in the active branch
|
|
102
|
-
def page_active?(page)
|
|
103
|
-
Alchemy::Deprecation.warn("page_active? helper is deprecated and will be removed from Alchemy 6.0")
|
|
104
|
-
|
|
105
|
-
@_page_ancestors ||= @page.self_and_ancestors.contentpages
|
|
106
|
-
@_page_ancestors.include?(page)
|
|
107
|
-
end
|
|
108
|
-
|
|
109
101
|
# Returns page links in a breadcrumb beginning from root to current page.
|
|
110
102
|
#
|
|
111
103
|
# === Options:
|
|
@@ -127,7 +119,7 @@ module Alchemy
|
|
|
127
119
|
|
|
128
120
|
pages = options[:page].
|
|
129
121
|
self_and_ancestors.contentpages.
|
|
130
|
-
|
|
122
|
+
published
|
|
131
123
|
|
|
132
124
|
if options.delete(:restricted_only)
|
|
133
125
|
pages = pages.restricted
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class BaseJob < ActiveJob::Base
|
|
5
|
+
# Automatically retry jobs that encountered a deadlock
|
|
6
|
+
# retry_on ActiveRecord::Deadlocked
|
|
7
|
+
|
|
8
|
+
# Most jobs are safe to ignore if the underlying records are no longer available
|
|
9
|
+
# discard_on ActiveJob::DeserializationError
|
|
10
|
+
end
|
|
11
|
+
end
|
|
@@ -35,6 +35,10 @@ module Alchemy
|
|
|
35
35
|
has_many :elements, through: :contents
|
|
36
36
|
has_many :pages, through: :elements
|
|
37
37
|
|
|
38
|
+
scope :by_file_type, ->(file_type) { where(file_mime_type: file_type) }
|
|
39
|
+
scope :recent, -> { where("#{table_name}.created_at > ?", Time.current - 24.hours).order(:created_at) }
|
|
40
|
+
scope :without_tag, -> { left_outer_joins(:taggings).where(gutentag_taggings: { id: nil }) }
|
|
41
|
+
|
|
38
42
|
# We need to define this method here to have it available in the validations below.
|
|
39
43
|
class << self
|
|
40
44
|
# The class used to generate URLs for attachments
|
|
@@ -51,6 +55,26 @@ module Alchemy
|
|
|
51
55
|
@_url_class = klass
|
|
52
56
|
end
|
|
53
57
|
|
|
58
|
+
def alchemy_resource_filters
|
|
59
|
+
[
|
|
60
|
+
{
|
|
61
|
+
name: :by_file_type,
|
|
62
|
+
values: distinct.pluck(:file_mime_type).map { |type| [Alchemy.t(type, scope: "mime_types"), type] }.sort_by(&:first),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
name: :misc,
|
|
66
|
+
values: %w(recent last_upload without_tag),
|
|
67
|
+
},
|
|
68
|
+
]
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def last_upload
|
|
72
|
+
last_id = Attachment.maximum(:id)
|
|
73
|
+
return Attachment.all unless last_id
|
|
74
|
+
|
|
75
|
+
where(id: last_id)
|
|
76
|
+
end
|
|
77
|
+
|
|
54
78
|
def searchable_alchemy_resource_attributes
|
|
55
79
|
%w(name file_name)
|
|
56
80
|
end
|
|
@@ -58,13 +82,6 @@ module Alchemy
|
|
|
58
82
|
def allowed_filetypes
|
|
59
83
|
Config.get(:uploader).fetch("allowed_filetypes", {}).fetch("alchemy/attachments", [])
|
|
60
84
|
end
|
|
61
|
-
|
|
62
|
-
def file_types_for_select
|
|
63
|
-
file_types = Alchemy::Attachment.pluck(:file_mime_type).uniq.map do |type|
|
|
64
|
-
[Alchemy.t(type, scope: "mime_types"), type]
|
|
65
|
-
end
|
|
66
|
-
file_types.sort_by(&:first)
|
|
67
|
-
end
|
|
68
85
|
end
|
|
69
86
|
|
|
70
87
|
validates_presence_of :file
|
|
@@ -39,19 +39,14 @@ module Alchemy
|
|
|
39
39
|
scope :essence_selects, -> { where(essence_type: "Alchemy::EssenceSelect") }
|
|
40
40
|
scope :essence_texts, -> { where(essence_type: "Alchemy::EssenceText") }
|
|
41
41
|
scope :named, ->(name) { where(name: name) }
|
|
42
|
-
scope :available, -> { published
|
|
42
|
+
scope :available, -> { published }
|
|
43
43
|
scope :published, -> { joins(:element).merge(Element.published) }
|
|
44
|
-
scope :not_trashed, -> { joins(:element).merge(Element.not_trashed) }
|
|
45
44
|
scope :not_restricted, -> { joins(:element).merge(Element.not_restricted) }
|
|
46
45
|
|
|
47
46
|
delegate :restricted?, to: :page, allow_nil: true
|
|
48
|
-
delegate :trashed?, to: :element, allow_nil: true
|
|
49
|
-
deprecate :trashed?, deprecator: Alchemy::Deprecation
|
|
50
47
|
delegate :public?, to: :element, allow_nil: true
|
|
51
48
|
|
|
52
49
|
class << self
|
|
53
|
-
deprecate :not_trashed, deprecator: Alchemy::Deprecation
|
|
54
|
-
|
|
55
50
|
# Returns the translated label for a content name.
|
|
56
51
|
#
|
|
57
52
|
# Translate it in your locale yml file:
|
|
@@ -7,7 +7,7 @@ module Alchemy
|
|
|
7
7
|
extend ActiveSupport::Concern
|
|
8
8
|
|
|
9
9
|
module ClassMethods
|
|
10
|
-
SKIPPED_ATTRIBUTES_ON_COPY = %w(position created_at updated_at creator_id updater_id id)
|
|
10
|
+
SKIPPED_ATTRIBUTES_ON_COPY = %w(position created_at updated_at creator_id updater_id element_id id)
|
|
11
11
|
|
|
12
12
|
# Builds a new content as descriped in the elements.yml file.
|
|
13
13
|
#
|
|
@@ -19,14 +19,14 @@ module Alchemy
|
|
|
19
19
|
return super if attributes.empty? || element.nil?
|
|
20
20
|
|
|
21
21
|
definition = element.content_definition_for(attributes[:name])
|
|
22
|
-
if definition.blank?
|
|
22
|
+
if definition.blank? && attributes[:essence_type].nil?
|
|
23
23
|
raise ContentDefinitionError, "No definition found in elements.yml for #{attributes.inspect} and #{element.inspect}"
|
|
24
24
|
end
|
|
25
25
|
|
|
26
26
|
super(
|
|
27
|
-
name:
|
|
28
|
-
essence_type: normalize_essence_type(definition[:type]),
|
|
29
|
-
|
|
27
|
+
name: attributes[:name],
|
|
28
|
+
essence_type: attributes[:essence_type] || normalize_essence_type(definition[:type]),
|
|
29
|
+
element: element
|
|
30
30
|
).tap(&:build_essence)
|
|
31
31
|
end
|
|
32
32
|
|
|
@@ -53,18 +53,16 @@ module Alchemy
|
|
|
53
53
|
# @copy.element_id # => 3
|
|
54
54
|
#
|
|
55
55
|
def copy(source, differences = {})
|
|
56
|
-
|
|
57
|
-
source.attributes.
|
|
56
|
+
Content.new(
|
|
57
|
+
source.attributes.with_indifferent_access.
|
|
58
58
|
except(*SKIPPED_ATTRIBUTES_ON_COPY).
|
|
59
|
-
merge(differences.with_indifferent_access)
|
|
60
|
-
)
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
content.essence = new_essence
|
|
67
|
-
content.save
|
|
59
|
+
merge(differences.with_indifferent_access)
|
|
60
|
+
).tap do |new_content|
|
|
61
|
+
new_content.build_essence(
|
|
62
|
+
source.essence.attributes.
|
|
63
|
+
except(*SKIPPED_ATTRIBUTES_ON_COPY)
|
|
64
|
+
)
|
|
65
|
+
new_content.save
|
|
68
66
|
end
|
|
69
67
|
end
|
|
70
68
|
|
|
@@ -117,29 +115,27 @@ module Alchemy
|
|
|
117
115
|
#
|
|
118
116
|
# If an optional type is passed, this type of essence gets created.
|
|
119
117
|
#
|
|
120
|
-
def build_essence(
|
|
121
|
-
self.essence = essence_class
|
|
122
|
-
ingredient: default_value
|
|
123
|
-
|
|
118
|
+
def build_essence(attributes = {})
|
|
119
|
+
self.essence = essence_class.new(
|
|
120
|
+
{ content: self, ingredient: default_value }.merge(attributes)
|
|
121
|
+
)
|
|
124
122
|
end
|
|
125
123
|
|
|
126
124
|
# Creates essence from definition.
|
|
127
125
|
#
|
|
128
126
|
# If an optional type is passed, this type of essence gets created.
|
|
129
127
|
#
|
|
130
|
-
def create_essence!(
|
|
131
|
-
build_essence(
|
|
128
|
+
def create_essence!(attrs = {})
|
|
129
|
+
build_essence(attrs).save!
|
|
132
130
|
save!
|
|
133
131
|
end
|
|
134
132
|
|
|
135
133
|
private
|
|
136
134
|
|
|
137
|
-
# Returns a class constant from definition's type field
|
|
135
|
+
# Returns a class constant from definition's type field or the essence_type column
|
|
138
136
|
#
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
def essence_class(type = nil)
|
|
142
|
-
Content.normalize_essence_type(type || definition["type"]).constantize
|
|
137
|
+
def essence_class
|
|
138
|
+
(essence_type || Content.normalize_essence_type(definition["type"])).constantize
|
|
143
139
|
end
|
|
144
140
|
end
|
|
145
141
|
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
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
|
|
@@ -1,37 +1,39 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Alchemy
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
class Element < BaseRecord
|
|
5
|
+
# Module concerning element definitions
|
|
6
|
+
#
|
|
7
|
+
module Definitions
|
|
8
|
+
extend ActiveSupport::Concern
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
10
|
+
module ClassMethods
|
|
11
|
+
# Returns the definitions from elements.yml file.
|
|
12
|
+
#
|
|
13
|
+
# Place a +elements.yml+ file inside your apps +config/alchemy+ folder to define
|
|
14
|
+
# your own set of elements
|
|
15
|
+
#
|
|
16
|
+
def definitions
|
|
17
|
+
ElementDefinition.all
|
|
18
|
+
end
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
# Returns one element definition by given name.
|
|
21
|
+
#
|
|
22
|
+
def definition_by_name(name)
|
|
23
|
+
ElementDefinition.get(name)
|
|
24
|
+
end
|
|
23
25
|
end
|
|
24
|
-
end
|
|
25
26
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
# The definition of this element.
|
|
28
|
+
#
|
|
29
|
+
def definition
|
|
30
|
+
if definition = self.class.definition_by_name(name)
|
|
31
|
+
definition
|
|
32
|
+
else
|
|
33
|
+
log_warning "Could not find element definition for #{name}. " \
|
|
34
|
+
"Please check your elements.yml file!"
|
|
35
|
+
{}
|
|
36
|
+
end
|
|
35
37
|
end
|
|
36
38
|
end
|
|
37
39
|
end
|