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,85 +1,87 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Alchemy
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
class Page < BaseRecord
|
|
5
|
+
module PageNaming
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
include NameConversions
|
|
8
|
+
RESERVED_URLNAMES = %w(admin messages new)
|
|
8
9
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
included do
|
|
11
|
+
before_validation :set_urlname,
|
|
12
|
+
if: :renamed?,
|
|
13
|
+
unless: -> { name.blank? }
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
validates :name,
|
|
16
|
+
presence: true
|
|
17
|
+
validates :urlname,
|
|
18
|
+
uniqueness: { scope: [:language_id, :layoutpage], if: -> { urlname.present? } },
|
|
19
|
+
exclusion: { in: RESERVED_URLNAMES },
|
|
20
|
+
length: { minimum: 3, if: -> { urlname.present? } }
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
22
|
+
before_save :set_title,
|
|
23
|
+
if: -> { title.blank? }
|
|
23
24
|
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
after_update :update_descendants_urlnames,
|
|
26
|
+
if: :saved_change_to_urlname?
|
|
26
27
|
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
after_move :update_urlname!
|
|
29
|
+
end
|
|
29
30
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
# Returns true if name or urlname has changed.
|
|
32
|
+
def renamed?
|
|
33
|
+
name_changed? || urlname_changed?
|
|
34
|
+
end
|
|
34
35
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
36
|
+
# Makes a slug of all ancestors urlnames including mine and delimit them be slash.
|
|
37
|
+
# So the whole path is stored as urlname in the database.
|
|
38
|
+
def update_urlname!
|
|
39
|
+
new_urlname = nested_url_name
|
|
40
|
+
if urlname != new_urlname
|
|
41
|
+
legacy_urls.create(urlname: urlname)
|
|
42
|
+
update_column(:urlname, new_urlname)
|
|
43
|
+
end
|
|
42
44
|
end
|
|
43
|
-
end
|
|
44
45
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
# Returns always the last part of a urlname path
|
|
47
|
+
def slug
|
|
48
|
+
urlname.to_s.split("/").last
|
|
49
|
+
end
|
|
49
50
|
|
|
50
|
-
|
|
51
|
+
private
|
|
51
52
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
53
|
+
def update_descendants_urlnames
|
|
54
|
+
reload
|
|
55
|
+
descendants.each(&:update_urlname!)
|
|
56
|
+
end
|
|
56
57
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
58
|
+
# Sets the urlname to a url friendly slug.
|
|
59
|
+
# Either from name, or if present, from urlname.
|
|
60
|
+
# The urlname contains the whole path including parent urlnames.
|
|
61
|
+
def set_urlname
|
|
62
|
+
self[:urlname] = nested_url_name
|
|
63
|
+
end
|
|
63
64
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
def set_title
|
|
66
|
+
self[:title] = name
|
|
67
|
+
end
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
69
|
+
# Converts the given name into an url friendly string.
|
|
70
|
+
#
|
|
71
|
+
# Names shorter than 3 will be filled up with dashes,
|
|
72
|
+
# so it does not collidate with the language code.
|
|
73
|
+
#
|
|
74
|
+
def converted_url_name
|
|
75
|
+
url_name = convert_to_urlname(slug.blank? ? name : slug)
|
|
76
|
+
url_name.rjust(3, "-")
|
|
77
|
+
end
|
|
77
78
|
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
79
|
+
def nested_url_name
|
|
80
|
+
if parent&.language_root?
|
|
81
|
+
converted_url_name
|
|
82
|
+
else
|
|
83
|
+
[parent&.urlname, converted_url_name].compact.join("/")
|
|
84
|
+
end
|
|
83
85
|
end
|
|
84
86
|
end
|
|
85
87
|
end
|
|
@@ -1,171 +1,168 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
3
|
module Alchemy
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
def taggable?
|
|
17
|
-
definition["taggable"] == true
|
|
18
|
-
end
|
|
4
|
+
class Page < BaseRecord
|
|
5
|
+
module PageNatures
|
|
6
|
+
extend ActiveSupport::Concern
|
|
7
|
+
|
|
8
|
+
# Determines if this page has a public version and this version is public.
|
|
9
|
+
#
|
|
10
|
+
# @see PageVersion#public?
|
|
11
|
+
# @returns Boolean
|
|
12
|
+
def public?
|
|
13
|
+
language.public? && !!public_version&.public?
|
|
14
|
+
end
|
|
19
15
|
|
|
20
|
-
|
|
16
|
+
def expiration_time
|
|
17
|
+
public_until ? public_until - Time.current : nil
|
|
18
|
+
end
|
|
21
19
|
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
20
|
+
def taggable?
|
|
21
|
+
definition["taggable"] == true
|
|
22
|
+
end
|
|
25
23
|
|
|
26
|
-
|
|
27
|
-
return unless Alchemy.user_class < ActiveRecord::Base
|
|
24
|
+
deprecate :taggable?, deprecator: Alchemy::Deprecation
|
|
28
25
|
|
|
29
|
-
|
|
30
|
-
|
|
26
|
+
def rootpage?
|
|
27
|
+
!new_record? && parent_id.blank?
|
|
28
|
+
end
|
|
31
29
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
end
|
|
30
|
+
def folded?(user_id)
|
|
31
|
+
return unless Alchemy.user_class < ActiveRecord::Base
|
|
35
32
|
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
# # config/alchemy/page_layouts.yml
|
|
39
|
-
# - name: contact
|
|
40
|
-
# editable_by:
|
|
41
|
-
# - freelancer
|
|
42
|
-
# - admin
|
|
43
|
-
#
|
|
44
|
-
# @returns Array
|
|
45
|
-
#
|
|
46
|
-
def has_limited_editors?
|
|
47
|
-
definition["editable_by"].present?
|
|
48
|
-
end
|
|
33
|
+
folded_pages.where(user_id: user_id, folded: true).any?
|
|
34
|
+
end
|
|
49
35
|
|
|
50
|
-
|
|
51
|
-
|
|
36
|
+
def contains_feed?
|
|
37
|
+
definition["feed"]
|
|
38
|
+
end
|
|
52
39
|
|
|
53
|
-
|
|
54
|
-
|
|
40
|
+
# Returns an Array of Alchemy roles which are able to edit this template
|
|
41
|
+
#
|
|
42
|
+
# # config/alchemy/page_layouts.yml
|
|
43
|
+
# - name: contact
|
|
44
|
+
# editable_by:
|
|
45
|
+
# - freelancer
|
|
46
|
+
# - admin
|
|
47
|
+
#
|
|
48
|
+
# @returns Array
|
|
49
|
+
#
|
|
50
|
+
def has_limited_editors?
|
|
51
|
+
definition["editable_by"].present?
|
|
52
|
+
end
|
|
55
53
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
locked_by? && locked_at?
|
|
59
|
-
end
|
|
54
|
+
def editor_roles
|
|
55
|
+
return unless has_limited_editors?
|
|
60
56
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
def status
|
|
64
|
-
{
|
|
65
|
-
public: public?,
|
|
66
|
-
locked: locked?,
|
|
67
|
-
restricted: restricted?,
|
|
68
|
-
}
|
|
69
|
-
end
|
|
57
|
+
definition["editable_by"]
|
|
58
|
+
end
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
def status_title(status_type)
|
|
76
|
-
Alchemy.t(status[status_type].to_s, scope: "page_states.#{status_type}")
|
|
77
|
-
end
|
|
60
|
+
# True if page locked_at timestamp and locked_by id are set
|
|
61
|
+
def locked?
|
|
62
|
+
locked_by? && locked_at?
|
|
63
|
+
end
|
|
78
64
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
65
|
+
# Returns a Hash describing the status of the Page.
|
|
66
|
+
#
|
|
67
|
+
def status
|
|
68
|
+
{
|
|
69
|
+
public: public?,
|
|
70
|
+
locked: locked?,
|
|
71
|
+
restricted: restricted?,
|
|
72
|
+
}
|
|
85
73
|
end
|
|
86
|
-
definition
|
|
87
|
-
end
|
|
88
74
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
75
|
+
# Returns the translated status for given status type.
|
|
76
|
+
#
|
|
77
|
+
# @param [Symbol] status_type
|
|
78
|
+
#
|
|
79
|
+
def status_title(status_type)
|
|
80
|
+
Alchemy.t(status[status_type].to_s, scope: "page_states.#{status_type}")
|
|
81
|
+
end
|
|
95
82
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
83
|
+
# Returns the self#page_layout definition from config/alchemy/page_layouts.yml file.
|
|
84
|
+
def definition
|
|
85
|
+
definition = PageLayout.get(page_layout)
|
|
86
|
+
if definition.nil?
|
|
87
|
+
log_warning "Page definition for `#{page_layout}` not found. Please check `page_layouts.yml` file."
|
|
88
|
+
return {}
|
|
89
|
+
end
|
|
90
|
+
definition
|
|
91
|
+
end
|
|
101
92
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
#
|
|
108
|
-
def cache_key
|
|
109
|
-
if Page.current_preview == self
|
|
110
|
-
"alchemy/pages/#{id}-#{updated_at}"
|
|
111
|
-
else
|
|
112
|
-
"alchemy/pages/#{id}-#{published_at}"
|
|
93
|
+
# Returns translated name of the pages page_layout value.
|
|
94
|
+
# Page layout names are defined inside the config/alchemy/page_layouts.yml file.
|
|
95
|
+
# Translate the name in your config/locales language yml file.
|
|
96
|
+
def layout_display_name
|
|
97
|
+
Alchemy.t(page_layout, scope: "page_layout_names")
|
|
113
98
|
end
|
|
114
|
-
end
|
|
115
99
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
def published_at
|
|
122
|
-
read_attribute(:published_at) || updated_at
|
|
123
|
-
end
|
|
100
|
+
# Returns the name for the layout partial
|
|
101
|
+
#
|
|
102
|
+
def layout_partial_name
|
|
103
|
+
page_layout.parameterize.underscore
|
|
104
|
+
end
|
|
124
105
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
#
|
|
139
|
-
# == Note:
|
|
140
|
-
#
|
|
141
|
-
# This only sets the cache control headers and skips rendering of the page body,
|
|
142
|
-
# if the cache is fresh.
|
|
143
|
-
# This does not disable the fragment caching in the views.
|
|
144
|
-
# So if you don't want a page and it's elements to be cached,
|
|
145
|
-
# then be sure to not use <% cache element %> in the views.
|
|
146
|
-
#
|
|
147
|
-
# @returns Boolean
|
|
148
|
-
#
|
|
149
|
-
def cache_page?
|
|
150
|
-
return false unless caching_enabled?
|
|
151
|
-
|
|
152
|
-
page_layout = PageLayout.get(self.page_layout)
|
|
153
|
-
page_layout["cache"] != false && page_layout["searchresults"] != true
|
|
154
|
-
end
|
|
106
|
+
# Returns the key that's taken for cache path.
|
|
107
|
+
#
|
|
108
|
+
# Uses the +published_at+ value that's updated when the user publishes the page.
|
|
109
|
+
#
|
|
110
|
+
# If the page is the current preview it uses the updated_at value as cache key.
|
|
111
|
+
#
|
|
112
|
+
def cache_key
|
|
113
|
+
if Page.current_preview == self
|
|
114
|
+
"alchemy/pages/#{id}-#{updated_at}"
|
|
115
|
+
else
|
|
116
|
+
"alchemy/pages/#{id}-#{published_at}"
|
|
117
|
+
end
|
|
118
|
+
end
|
|
155
119
|
|
|
156
|
-
|
|
120
|
+
# We use the published_at value for the cache_key.
|
|
121
|
+
#
|
|
122
|
+
# If no published_at value is set yet, i.e. because it was never published,
|
|
123
|
+
# we return the updated_at value.
|
|
124
|
+
#
|
|
125
|
+
def published_at
|
|
126
|
+
read_attribute(:published_at) || updated_at
|
|
127
|
+
end
|
|
157
128
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
129
|
+
# Returns true if the page cache control headers should be set.
|
|
130
|
+
#
|
|
131
|
+
# == Disable Alchemy's page caching globally
|
|
132
|
+
#
|
|
133
|
+
# # config/alchemy/config.yml
|
|
134
|
+
# ...
|
|
135
|
+
# cache_pages: false
|
|
136
|
+
#
|
|
137
|
+
# == Disable caching on page layout level
|
|
138
|
+
#
|
|
139
|
+
# # config/alchemy/page_layouts.yml
|
|
140
|
+
# - name: contact
|
|
141
|
+
# cache: false
|
|
142
|
+
#
|
|
143
|
+
# == Note:
|
|
144
|
+
#
|
|
145
|
+
# This only sets the cache control headers and skips rendering of the page body,
|
|
146
|
+
# if the cache is fresh.
|
|
147
|
+
# This does not disable the fragment caching in the views.
|
|
148
|
+
# So if you don't want a page and it's elements to be cached,
|
|
149
|
+
# then be sure to not use <% cache element %> in the views.
|
|
150
|
+
#
|
|
151
|
+
# @returns Boolean
|
|
152
|
+
#
|
|
153
|
+
def cache_page?
|
|
154
|
+
return false unless caching_enabled?
|
|
155
|
+
|
|
156
|
+
page_layout = PageLayout.get(self.page_layout)
|
|
157
|
+
page_layout["cache"] != false && page_layout["searchresults"] != true
|
|
158
|
+
end
|
|
162
159
|
|
|
163
|
-
|
|
164
|
-
!public_on.nil? && public_on <= time
|
|
165
|
-
end
|
|
160
|
+
private
|
|
166
161
|
|
|
167
|
-
|
|
168
|
-
|
|
162
|
+
def caching_enabled?
|
|
163
|
+
Alchemy::Config.get(:cache_pages) &&
|
|
164
|
+
Rails.application.config.action_controller.perform_caching
|
|
165
|
+
end
|
|
169
166
|
end
|
|
170
167
|
end
|
|
171
168
|
end
|