alchemy_cms 7.4.16 → 8.0.0.a
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/CHANGELOG.md +87 -77
- data/Gemfile +13 -7
- data/README.md +13 -5
- data/alchemy_cms.gemspec +14 -5
- data/app/assets/builds/alchemy/admin/page-select.css +1 -1
- data/app/assets/builds/alchemy/admin/print.css +1 -1
- data/app/assets/builds/alchemy/admin.css +2 -2
- data/app/assets/builds/alchemy/custom-properties.css +1 -1
- data/app/assets/builds/alchemy/welcome.css +1 -1
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy/content.min.css +1 -0
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -1
- data/app/assets/config/alchemy_manifest.js +0 -2
- data/app/assets/images/alchemy/icons-sprite.svg +1 -0
- data/app/components/alchemy/admin/resource/applied_filter.rb +29 -0
- data/app/components/alchemy/admin/resource/checkbox_filter.rb +36 -0
- data/app/components/alchemy/admin/resource/datepicker_filter.rb +42 -0
- data/app/components/alchemy/admin/resource/select_filter.rb +43 -0
- data/app/components/alchemy/admin/toolbar_button.rb +6 -3
- data/app/components/alchemy/ingredients/datetime_view.rb +2 -4
- data/app/components/alchemy/ingredients/number_view.rb +18 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +8 -15
- data/app/controllers/alchemy/admin/clipboard_controller.rb +2 -6
- data/app/controllers/alchemy/admin/elements_controller.rb +1 -1
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -4
- data/app/controllers/alchemy/admin/pages_controller.rb +15 -17
- data/app/controllers/alchemy/admin/pictures_controller.rb +9 -5
- data/app/controllers/alchemy/admin/resources_controller.rb +16 -106
- data/app/controllers/alchemy/api/nodes_controller.rb +1 -1
- data/app/controllers/alchemy/api/pages_controller.rb +0 -2
- data/app/controllers/alchemy/attachments_controller.rb +43 -14
- data/app/controllers/alchemy/messages_controller.rb +1 -1
- data/app/controllers/alchemy/pages_controller.rb +7 -2
- data/app/controllers/concerns/alchemy/admin/resource_filter.rb +92 -0
- data/app/decorators/alchemy/element_editor.rb +5 -48
- data/app/decorators/alchemy/ingredient_editor.rb +4 -62
- data/app/helpers/alchemy/admin/base_helper.rb +14 -84
- data/app/helpers/alchemy/admin/elements_helper.rb +4 -4
- data/app/helpers/alchemy/admin/pages_helper.rb +1 -1
- data/app/helpers/alchemy/base_helper.rb +0 -30
- data/app/helpers/alchemy/elements_block_helper.rb +0 -14
- data/app/helpers/alchemy/pages_helper.rb +2 -2
- data/{lib → app/helpers}/alchemy/resources_helper.rb +6 -46
- data/app/javascript/alchemy_admin/components/action.js +2 -0
- data/app/javascript/alchemy_admin/components/alchemy_html_element.js +3 -3
- data/app/javascript/alchemy_admin/components/datepicker.js +10 -2
- data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +7 -7
- data/app/javascript/alchemy_admin/components/element_editor.js +1 -1
- data/app/javascript/alchemy_admin/components/index.js +1 -3
- data/app/javascript/alchemy_admin/components/remote_select.js +4 -1
- data/app/javascript/alchemy_admin/components/tags_autocomplete.js +5 -1
- data/app/javascript/alchemy_admin/components/tinymce.js +4 -2
- data/app/javascript/alchemy_admin/components/update_check.js +42 -0
- data/app/javascript/alchemy_admin/components/uploader/file_upload.js +15 -8
- data/app/javascript/alchemy_admin/components/uploader/progress.js +12 -6
- data/app/javascript/alchemy_admin/components/uploader.js +4 -2
- data/app/javascript/alchemy_admin/confirm_dialog.js +27 -57
- data/app/javascript/alchemy_admin/dirty.js +3 -2
- data/app/javascript/alchemy_admin/i18n.js +15 -16
- data/app/javascript/alchemy_admin/initializer.js +1 -49
- data/app/javascript/alchemy_admin/link_dialog.js +1 -6
- data/app/javascript/alchemy_admin/utils/ajax.js +51 -44
- data/app/javascript/alchemy_admin.js +3 -8
- data/app/models/alchemy/admin/filters/base.rb +38 -0
- data/app/models/alchemy/admin/filters/checkbox.rb +24 -0
- data/app/models/alchemy/admin/filters/datepicker.rb +53 -0
- data/app/models/alchemy/admin/filters/select.rb +70 -0
- data/app/models/alchemy/admin/resource_name.rb +27 -0
- data/app/models/alchemy/attachment.rb +49 -37
- data/app/models/alchemy/base_record.rb +2 -0
- data/app/models/alchemy/element/definitions.rb +1 -1
- data/app/models/alchemy/element/element_ingredients.rb +6 -6
- data/app/models/alchemy/element/presenters.rb +3 -12
- data/app/models/alchemy/element.rb +9 -27
- data/app/models/alchemy/element_definition.rb +160 -0
- data/app/models/alchemy/ingredient.rb +11 -44
- data/app/models/alchemy/ingredient_definition.rb +134 -0
- data/app/models/alchemy/ingredient_validator.rb +7 -3
- data/app/models/alchemy/ingredients/number.rb +19 -0
- data/app/models/alchemy/language/code.rb +0 -1
- data/app/models/alchemy/language.rb +0 -14
- data/app/models/alchemy/message.rb +3 -7
- data/app/models/alchemy/node.rb +1 -22
- data/app/models/alchemy/page/{page_layouts.rb → definitions.rb} +12 -19
- data/app/models/alchemy/page/fixed_attributes.rb +1 -1
- data/app/models/alchemy/page/page_elements.rb +13 -14
- data/app/models/alchemy/page/page_naming.rb +0 -1
- data/app/models/alchemy/page/page_natures.rb +7 -7
- data/app/models/alchemy/page/page_scopes.rb +1 -1
- data/app/models/alchemy/page.rb +11 -33
- data/app/models/alchemy/page_definition.rb +115 -0
- data/app/models/alchemy/picture.rb +71 -88
- data/app/models/alchemy/picture_variant.rb +115 -5
- data/{lib → app/models}/alchemy/resource.rb +4 -18
- data/{lib → app/models}/alchemy/searchable_resource.rb +15 -0
- data/app/models/alchemy/site/layout.rb +5 -6
- data/app/models/alchemy/site.rb +0 -15
- data/app/models/alchemy/storage_adapter/active_storage/attachment_url.rb +41 -0
- data/app/models/alchemy/storage_adapter/active_storage/picture_url.rb +55 -0
- data/app/models/alchemy/storage_adapter/active_storage/preprocessor.rb +40 -0
- data/app/models/alchemy/storage_adapter/active_storage.rb +173 -0
- data/app/models/alchemy/{attachment/url.rb → storage_adapter/dragonfly/attachment_url.rb} +12 -12
- data/app/models/alchemy/{picture/url.rb → storage_adapter/dragonfly/picture_url.rb} +28 -12
- data/app/models/alchemy/{picture → storage_adapter/dragonfly}/preprocessor.rb +4 -4
- data/app/models/alchemy/storage_adapter/dragonfly.rb +183 -0
- data/app/models/alchemy/storage_adapter.rb +74 -0
- data/app/models/concerns/alchemy/picture_thumbnails.rb +19 -6
- data/app/serializers/alchemy/element_serializer.rb +0 -1
- data/app/serializers/alchemy/page_tree_serializer.rb +1 -17
- data/app/services/alchemy/dragonfly_to_image_processing.rb +100 -0
- data/app/stylesheets/alchemy/_defaults.scss +3 -0
- data/app/stylesheets/alchemy/_extends.scss +69 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/_mixins.scss +33 -49
- data/app/stylesheets/alchemy/_variables.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/archive.scss +20 -37
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/base.scss +16 -14
- data/app/stylesheets/alchemy/admin/buttons.scss +160 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/clipboard.scss +2 -2
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dashboard.scss +13 -16
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dialogs.scss +23 -16
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/elements.scss +150 -105
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/errors.scss +5 -5
- data/app/stylesheets/alchemy/admin/filters.scss +58 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flatpickr.scss +53 -60
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/form_fields.scss +21 -7
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/forms.scss +31 -19
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/frame.scss +20 -16
- data/app/stylesheets/alchemy/admin/hints.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/icons.scss +10 -1
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/image_library.scss +10 -8
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/images.scss +1 -1
- data/app/stylesheets/alchemy/admin/labels.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/lists.scss +3 -3
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/navigation.scss +61 -55
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/nodes.scss +21 -18
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/notices.scss +18 -18
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/page-select.scss +2 -2
- data/app/stylesheets/alchemy/admin/pagination.scss +144 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/preview_window.scss +8 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/print.scss +1 -1
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/resource_info.scss +8 -5
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/search.scss +9 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/selects.scss +49 -37
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/shoelace.scss +5 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/sitemap.scss +38 -33
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tables.scss +6 -4
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tags.scss +6 -4
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/toolbar.scss +12 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/typography.scss +2 -2
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/upload.scss +7 -5
- data/app/stylesheets/alchemy/admin.scss +44 -0
- data/app/stylesheets/alchemy/custom-properties.css +244 -0
- data/app/stylesheets/alchemy/welcome.scss +75 -0
- data/app/{assets/stylesheets → stylesheets}/tinymce/skins/content/alchemy/content.scss +8 -9
- data/app/stylesheets/tinymce/skins/ui/alchemy/content.scss +1 -0
- data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/skin.scss +133 -136
- data/app/views/alchemy/admin/attachments/_files_list.html.erb +2 -2
- data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +1 -1
- data/app/views/alchemy/admin/{elements/_clipboard_button.html.erb → clipboard/_button.html.erb} +3 -5
- data/app/views/alchemy/admin/clipboard/_update_nested_element_button.turbo_stream.erb +11 -0
- data/app/views/alchemy/admin/clipboard/clear.turbo_stream.erb +4 -0
- data/app/views/alchemy/admin/clipboard/index.html.erb +15 -13
- data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +18 -0
- data/app/views/alchemy/admin/clipboard/remove.turbo_stream.erb +9 -0
- data/app/views/alchemy/admin/dashboard/info.html.erb +17 -31
- data/app/views/alchemy/admin/elements/_element.html.erb +4 -8
- data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_header.html.erb +1 -0
- data/app/views/alchemy/admin/elements/_toolbar.html.erb +4 -6
- data/app/views/alchemy/admin/elements/create.turbo_stream.erb +2 -1
- data/app/views/alchemy/admin/elements/index.html.erb +2 -2
- data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +3 -16
- data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +1 -10
- data/app/views/alchemy/admin/languages/_form.html.erb +1 -1
- data/app/views/alchemy/admin/languages/_table.html.erb +1 -1
- data/app/views/alchemy/admin/languages/index.html.erb +5 -2
- data/app/views/alchemy/admin/layoutpages/index.html.erb +1 -12
- data/app/views/alchemy/admin/pages/_form.html.erb +2 -2
- data/app/views/alchemy/admin/pages/_page.html.erb +2 -3
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +1 -15
- data/app/views/alchemy/admin/pages/index.html.erb +1 -1
- data/app/views/alchemy/admin/pages/info.html.erb +1 -1
- data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +9 -12
- data/app/views/alchemy/admin/partials/_search_form.html.erb +4 -10
- data/app/views/alchemy/admin/pictures/_archive.html.erb +4 -7
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +2 -1
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_form.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/index.html.erb +2 -7
- data/app/views/alchemy/admin/resources/_applied_filters.html.erb +8 -0
- data/app/views/alchemy/admin/resources/_filter_bar.html.erb +11 -21
- data/app/views/alchemy/admin/resources/_pagination.html.erb +6 -0
- data/app/views/alchemy/admin/resources/_per_page_select.html.erb +4 -2
- data/app/views/alchemy/admin/resources/_resource_table.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_table_header.html.erb +1 -15
- data/app/views/alchemy/admin/sites/index.html.erb +5 -1
- data/app/views/alchemy/admin/styleguide/index.html.erb +8 -0
- data/app/views/alchemy/admin/tags/index.html.erb +1 -1
- data/app/views/alchemy/admin/tinymce/_setup.html.erb +7 -7
- data/app/{javascript/alchemy_admin/locales/en.js → views/alchemy/admin/translations/_en.js} +5 -2
- data/app/views/alchemy/admin/uploader/_button.html.erb +1 -1
- data/app/views/alchemy/admin/uploader/_setup.html.erb +4 -4
- data/app/views/alchemy/ingredients/_number_editor.html.erb +24 -0
- data/app/views/alchemy/ingredients/_page_editor.html.erb +1 -0
- data/app/views/alchemy/ingredients/_richtext_editor.html.erb +1 -0
- data/app/views/alchemy/ingredients/_select_editor.html.erb +2 -1
- data/app/views/alchemy/no_index.html.erb +31 -0
- data/app/views/alchemy/welcome.html.erb +12 -10
- data/app/views/kaminari/alchemy/_first_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_last_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_next_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_paginator.html.erb +18 -13
- data/app/views/kaminari/alchemy/_prev_page.html.erb +5 -3
- data/app/views/layouts/alchemy/admin.html.erb +5 -9
- data/bun.lockb +0 -0
- data/bundles/remixicon.mjs +153 -0
- data/config/alchemy/config.yml +3 -2
- data/config/initializers/dragonfly.rb +0 -1
- data/config/initializers/mime_types.rb +1 -0
- data/config/locales/alchemy.en.yml +32 -14
- data/config/routes.rb +0 -2
- data/eslint.config.js +2 -1
- data/lib/alchemy/admin/preview_url.rb +4 -5
- data/lib/alchemy/cache_digests/template_tracker.rb +6 -9
- data/lib/alchemy/config_missing.rb +14 -0
- data/lib/alchemy/configuration/base_option.rb +24 -0
- data/lib/alchemy/configuration/boolean_option.rb +16 -0
- data/lib/alchemy/configuration/class_option.rb +15 -0
- data/lib/alchemy/configuration/class_set_option.rb +46 -0
- data/lib/alchemy/configuration/integer_list_option.rb +13 -0
- data/lib/alchemy/configuration/integer_option.rb +12 -0
- data/lib/alchemy/configuration/list_option.rb +22 -0
- data/lib/alchemy/configuration/regexp_option.rb +11 -0
- data/lib/alchemy/configuration/string_list_option.rb +13 -0
- data/lib/alchemy/configuration/string_option.rb +11 -0
- data/lib/alchemy/configuration.rb +115 -0
- data/lib/alchemy/configuration_methods.rb +3 -1
- data/lib/alchemy/configurations/default_language.rb +12 -0
- data/lib/alchemy/configurations/default_site.rb +10 -0
- data/lib/alchemy/configurations/format_matchers.rb +11 -0
- data/lib/alchemy/configurations/mailer.rb +16 -0
- data/lib/alchemy/configurations/main.rb +216 -0
- data/lib/alchemy/configurations/preview.rb +32 -0
- data/lib/alchemy/configurations/sitemap.rb +10 -0
- data/lib/alchemy/configurations/uploader.rb +34 -0
- data/lib/alchemy/controller_actions.rb +1 -2
- data/lib/alchemy/engine.rb +65 -17
- data/lib/alchemy/hints.rb +3 -7
- data/lib/alchemy/name_conversions.rb +0 -6
- data/lib/alchemy/on_page_layout.rb +2 -2
- data/lib/alchemy/permissions.rb +0 -10
- data/lib/alchemy/propshaft/tinymce_asset.rb +15 -0
- data/lib/alchemy/seeder.rb +6 -12
- data/lib/alchemy/tasks/usage.rb +4 -4
- data/lib/alchemy/test_support/config_stubbing.rb +1 -7
- data/lib/alchemy/test_support/factories/attachment_factory.rb +13 -2
- data/lib/alchemy/test_support/factories/language_factory.rb +5 -9
- data/lib/alchemy/test_support/factories/page_factory.rb +2 -4
- data/lib/alchemy/test_support/factories/picture_factory.rb +30 -2
- data/lib/alchemy/test_support/factories/site_factory.rb +2 -2
- data/lib/alchemy/test_support/having_crop_action_examples.rb +2 -2
- data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +80 -26
- data/lib/alchemy/test_support/shared_ingredient_examples.rb +5 -5
- data/lib/alchemy/upgrader/.keep +0 -0
- data/lib/alchemy/upgrader/eight_zero.rb +14 -0
- data/lib/alchemy/upgrader.rb +33 -20
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy.rb +192 -170
- data/lib/alchemy_cms.rb +1 -7
- data/lib/generators/alchemy/ingredient/ingredient_generator.rb +0 -3
- data/lib/generators/alchemy/install/files/_article.html.erb +6 -4
- data/lib/generators/alchemy/install/files/alchemy.en.yml +22 -3
- data/lib/generators/alchemy/install/files/application.html.erb +5 -0
- data/lib/generators/alchemy/install/install_generator.rb +5 -14
- data/lib/generators/alchemy/install/templates/alchemy.rb.tt +196 -0
- data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +0 -1
- data/lib/generators/alchemy/install/templates/elements.yml.tt +3 -1
- data/lib/generators/alchemy/install/templates/menus.yml.tt +1 -1
- data/lib/generators/alchemy/install/templates/page_layouts.yml.tt +2 -2
- data/lib/generators/alchemy/page_layouts/page_layouts_generator.rb +2 -2
- data/lib/tasks/alchemy/assets.rake +14 -0
- data/lib/tasks/alchemy/upgrade.rake +12 -47
- data/lib/tasks/alchemy/usage.rake +0 -2
- data/vendor/javascript/tinymce.min.js +1 -1
- data/vitest.config.js +21 -0
- metadata +184 -183
- data/app/assets/builds/alchemy/admin/page-select.css.map +0 -1
- data/app/assets/builds/alchemy/admin/print.css.map +0 -1
- data/app/assets/builds/alchemy/admin.css.map +0 -1
- data/app/assets/builds/alchemy/custom-properties.css.map +0 -1
- data/app/assets/builds/alchemy/welcome.css.map +0 -1
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css.map +0 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css.map +0 -1
- data/app/assets/javascripts/alchemy/admin.js +0 -10
- data/app/assets/stylesheets/alchemy/_defaults.scss +0 -3
- data/app/assets/stylesheets/alchemy/_deprecated_variables.scss +0 -45
- data/app/assets/stylesheets/alchemy/_deprecation.scss +0 -17
- data/app/assets/stylesheets/alchemy/_extends.scss +0 -62
- data/app/assets/stylesheets/alchemy/_variables.scss +0 -201
- data/app/assets/stylesheets/alchemy/admin/buttons.scss +0 -123
- data/app/assets/stylesheets/alchemy/admin/hints.scss +0 -5
- data/app/assets/stylesheets/alchemy/admin/labels.scss +0 -3
- data/app/assets/stylesheets/alchemy/admin/pagination.scss +0 -92
- data/app/assets/stylesheets/alchemy/admin.scss +0 -42
- data/app/assets/stylesheets/alchemy/custom-properties.css +0 -98
- data/app/assets/stylesheets/alchemy/welcome.scss +0 -57
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.css +0 -711
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.css +0 -705
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.css +0 -29
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.css +0 -677
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.min.css +0 -7
- data/app/controllers/alchemy/elements_controller.rb +0 -32
- data/app/models/alchemy/element/dom_id.rb +0 -31
- data/app/models/alchemy/picture/calculations.rb +0 -49
- data/app/models/alchemy/picture/transformations.rb +0 -115
- data/app/views/alchemy/admin/attachments/destroy.js.erb +0 -1
- data/app/views/alchemy/admin/clipboard/clear.js.erb +0 -3
- data/app/views/alchemy/admin/clipboard/insert.js.erb +0 -29
- data/app/views/alchemy/admin/clipboard/remove.js.erb +0 -10
- data/app/views/alchemy/admin/resources/_filter.html.erb +0 -12
- data/app/views/alchemy/admin/resources/_resource.html.erb +0 -34
- data/app/views/alchemy/admin/resources/_table.html.erb +0 -29
- data/app/views/alchemy/elements/show.html.erb +0 -1
- data/app/views/alchemy/elements/show.js.erb +0 -1
- data/app/views/alchemy/ingredients/_audio_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_boolean_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_datetime_view.html.erb +0 -3
- data/app/views/alchemy/ingredients/_file_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_headline_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_html_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_link_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_node_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_page_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_picture_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_richtext_view.html.erb +0 -3
- data/app/views/alchemy/ingredients/_select_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_text_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_video_view.html.erb +0 -3
- data/babel.config.js +0 -12
- data/config/initializers/assets.rb +0 -4
- data/lib/alchemy/config.rb +0 -114
- data/lib/alchemy/element_definition.rb +0 -73
- data/lib/alchemy/page_layout.rb +0 -73
- data/lib/alchemy/resource_filter.rb +0 -40
- data/lib/alchemy/upgrader/seven_point_four.rb +0 -26
- data/lib/alchemy/upgrader/seven_point_three.rb +0 -52
- data/lib/generators/alchemy/ingredient/templates/view.html.erb +0 -1
- data/lib/generators/alchemy/install/files/alchemy_admin.js +0 -1
- data/lib/generators/alchemy/install/files/all.js +0 -11
- data/lib/generators/alchemy/install/files/article.css +0 -25
- data/vendor/assets/images/remixicon.symbol.svg +0 -11
- /data/app/{assets/stylesheets → stylesheets}/alchemy/_fonts.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachment-select.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachments.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flash.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/list_filter.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/node-select.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/spinner.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/skintool.json +0 -0
- /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/fonts/tinymce-mobile.woff +0 -0
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class ElementDefinition
|
|
5
|
+
include ActiveModel::Model
|
|
6
|
+
include ActiveModel::Attributes
|
|
7
|
+
include Alchemy::Hints
|
|
8
|
+
|
|
9
|
+
extend ActiveModel::Translation
|
|
10
|
+
|
|
11
|
+
attribute :name, :string
|
|
12
|
+
attribute :unique, :boolean, default: false
|
|
13
|
+
attribute :amount, :integer, default: Float::INFINITY
|
|
14
|
+
attribute :taggable, :boolean, default: false
|
|
15
|
+
attribute :compact, :boolean, default: false
|
|
16
|
+
attribute :fixed, :boolean, default: false
|
|
17
|
+
attribute :ingredients, default: []
|
|
18
|
+
attribute :nestable_elements, default: []
|
|
19
|
+
attribute :autogenerate, default: []
|
|
20
|
+
attribute :deprecated
|
|
21
|
+
attribute :message
|
|
22
|
+
attribute :warning
|
|
23
|
+
attribute :hint
|
|
24
|
+
|
|
25
|
+
validates :name,
|
|
26
|
+
presence: true,
|
|
27
|
+
format: {
|
|
28
|
+
with: /\A[a-z_-]+\z/
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
delegate :blank?, to: :name
|
|
32
|
+
|
|
33
|
+
class << self
|
|
34
|
+
# Returns the definitions from elements.yml file.
|
|
35
|
+
#
|
|
36
|
+
# Place a +elements.yml+ file inside your apps +config/alchemy+ folder to define
|
|
37
|
+
# your own set of elements
|
|
38
|
+
#
|
|
39
|
+
def all
|
|
40
|
+
@definitions ||= read_definitions_file.map { new(**_1) }
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Add additional page definitions to collection.
|
|
44
|
+
#
|
|
45
|
+
# Useful for extending the elements from an Alchemy module.
|
|
46
|
+
#
|
|
47
|
+
# === Usage Example
|
|
48
|
+
#
|
|
49
|
+
# Call +Alchemy::ElementDefinition.add(your_definition)+ in your engine.rb file.
|
|
50
|
+
#
|
|
51
|
+
# @param [Array || Hash]
|
|
52
|
+
# You can pass a single element definition as Hash, or a collection of elements as Array.
|
|
53
|
+
#
|
|
54
|
+
def add(definition)
|
|
55
|
+
all
|
|
56
|
+
@definitions += Array.wrap(definition).map { new(**_1) }
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# Returns one element definition by given name.
|
|
60
|
+
#
|
|
61
|
+
def get(name)
|
|
62
|
+
return new if name.blank?
|
|
63
|
+
|
|
64
|
+
all.detect { _1.name.casecmp(name).zero? }
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def reset!
|
|
68
|
+
@definitions = nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# The absolute +elements.yml+ file path
|
|
72
|
+
# @return [Pathname]
|
|
73
|
+
def definitions_file_path
|
|
74
|
+
Rails.root.join("config", "alchemy", "elements.yml")
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
private
|
|
78
|
+
|
|
79
|
+
def definitions_file
|
|
80
|
+
File.read(definitions_file_path)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def definitions_file_exist?
|
|
84
|
+
File.exist?(definitions_file_path)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Reads the element definitions from +config/alchemy/elements.yml+.
|
|
88
|
+
#
|
|
89
|
+
def read_definitions_file
|
|
90
|
+
if definitions_file_exist?
|
|
91
|
+
YAML.safe_load(
|
|
92
|
+
ERB.new(definitions_file).result,
|
|
93
|
+
permitted_classes: YAML_PERMITTED_CLASSES,
|
|
94
|
+
aliases: true
|
|
95
|
+
) || []
|
|
96
|
+
else
|
|
97
|
+
raise LoadError,
|
|
98
|
+
"Could not find elements.yml file! Please run `rails generate alchemy:install`"
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def attributes
|
|
104
|
+
super.with_indifferent_access
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def ingredients
|
|
108
|
+
super.map { IngredientDefinition.new(**_1) }
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Returns a deprecation notice for elements marked deprecated
|
|
112
|
+
#
|
|
113
|
+
# You can either use localizations or pass a String as notice
|
|
114
|
+
# in the element definition.
|
|
115
|
+
#
|
|
116
|
+
# == Custom deprecation notices
|
|
117
|
+
#
|
|
118
|
+
# Use general element deprecation notice
|
|
119
|
+
#
|
|
120
|
+
# - name: old_element
|
|
121
|
+
# deprecated: true
|
|
122
|
+
#
|
|
123
|
+
# Add a translation to your locale file for a per element notice.
|
|
124
|
+
#
|
|
125
|
+
# en:
|
|
126
|
+
# alchemy:
|
|
127
|
+
# element_deprecation_notices:
|
|
128
|
+
# old_element: Foo baz widget is deprecated
|
|
129
|
+
#
|
|
130
|
+
# or use the global translation that apply to all deprecated elements.
|
|
131
|
+
#
|
|
132
|
+
# en:
|
|
133
|
+
# alchemy:
|
|
134
|
+
# element_deprecation_notice: Foo baz widget is deprecated
|
|
135
|
+
#
|
|
136
|
+
# or pass string as deprecation notice.
|
|
137
|
+
#
|
|
138
|
+
# - name: old_element
|
|
139
|
+
# deprecated: This element will be removed soon.
|
|
140
|
+
#
|
|
141
|
+
def deprecation_notice
|
|
142
|
+
case deprecated
|
|
143
|
+
when String
|
|
144
|
+
deprecated
|
|
145
|
+
when TrueClass
|
|
146
|
+
Alchemy.t(
|
|
147
|
+
name,
|
|
148
|
+
scope: :element_deprecation_notices,
|
|
149
|
+
default: Alchemy.t(:element_deprecated)
|
|
150
|
+
)
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
private
|
|
155
|
+
|
|
156
|
+
def hint_translation_scope
|
|
157
|
+
:element_hints
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
@@ -4,8 +4,6 @@ module Alchemy
|
|
|
4
4
|
class Ingredient < BaseRecord
|
|
5
5
|
class DefinitionError < StandardError; end
|
|
6
6
|
|
|
7
|
-
include Hints
|
|
8
|
-
|
|
9
7
|
self.table_name = "alchemy_ingredients"
|
|
10
8
|
|
|
11
9
|
attribute :data, :json
|
|
@@ -16,7 +14,7 @@ module Alchemy
|
|
|
16
14
|
has_one :page, through: :element, class_name: "Alchemy::Page"
|
|
17
15
|
|
|
18
16
|
after_initialize :set_default_value,
|
|
19
|
-
if: -> { definition.
|
|
17
|
+
if: -> { definition.default && value.nil? }
|
|
20
18
|
|
|
21
19
|
validates :type, presence: true
|
|
22
20
|
validates :role, presence: true, uniqueness: {scope: :element_id, case_sensitive: false}
|
|
@@ -38,6 +36,8 @@ module Alchemy
|
|
|
38
36
|
scope :texts, -> { where(type: "Alchemy::Ingredients::Text") }
|
|
39
37
|
scope :videos, -> { where(type: "Alchemy::Ingredients::Video") }
|
|
40
38
|
|
|
39
|
+
delegate :has_hint?, :hint, to: :definition
|
|
40
|
+
|
|
41
41
|
class << self
|
|
42
42
|
# Defines getter and setter method aliases for related object
|
|
43
43
|
#
|
|
@@ -54,7 +54,7 @@ module Alchemy
|
|
|
54
54
|
|
|
55
55
|
define_method :"#{name}_id=" do |id|
|
|
56
56
|
self.related_object_id = id
|
|
57
|
-
self.related_object_type =
|
|
57
|
+
self.related_object_type = class_name
|
|
58
58
|
end
|
|
59
59
|
end
|
|
60
60
|
|
|
@@ -97,15 +97,15 @@ module Alchemy
|
|
|
97
97
|
|
|
98
98
|
# Settings for this ingredient from the +elements.yml+ definition.
|
|
99
99
|
def settings
|
|
100
|
-
definition
|
|
100
|
+
definition.settings
|
|
101
101
|
end
|
|
102
102
|
|
|
103
103
|
# Definition hash for this ingredient from +elements.yml+ file.
|
|
104
104
|
#
|
|
105
105
|
def definition
|
|
106
|
-
return
|
|
106
|
+
return IngredientDefinition.new unless element
|
|
107
107
|
|
|
108
|
-
element.ingredient_definition_for(role) ||
|
|
108
|
+
element.ingredient_definition_for(role) || IngredientDefinition.new
|
|
109
109
|
end
|
|
110
110
|
|
|
111
111
|
# The first 30 characters of the value
|
|
@@ -118,12 +118,6 @@ module Alchemy
|
|
|
118
118
|
value.to_s[0..maxlength - 1]
|
|
119
119
|
end
|
|
120
120
|
|
|
121
|
-
# The path to the view partial of the ingredient
|
|
122
|
-
# @return [String]
|
|
123
|
-
def to_partial_path
|
|
124
|
-
"alchemy/ingredients/#{partial_name}_view"
|
|
125
|
-
end
|
|
126
|
-
|
|
127
121
|
# The demodulized underscored class name of the ingredient
|
|
128
122
|
# @return [String]
|
|
129
123
|
def partial_name
|
|
@@ -132,17 +126,12 @@ module Alchemy
|
|
|
132
126
|
|
|
133
127
|
# @return [Boolean]
|
|
134
128
|
def has_validations?
|
|
135
|
-
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
# @return [Boolean]
|
|
139
|
-
def has_hint?
|
|
140
|
-
!!definition[:hint]
|
|
129
|
+
definition.validate.any?
|
|
141
130
|
end
|
|
142
131
|
|
|
143
132
|
# @return [Boolean]
|
|
144
133
|
def deprecated?
|
|
145
|
-
!!definition
|
|
134
|
+
!!definition.deprecated
|
|
146
135
|
end
|
|
147
136
|
|
|
148
137
|
# @return [Boolean]
|
|
@@ -152,7 +141,7 @@ module Alchemy
|
|
|
152
141
|
|
|
153
142
|
# @return [Boolean]
|
|
154
143
|
def preview_ingredient?
|
|
155
|
-
!!definition
|
|
144
|
+
!!definition.as_element_title
|
|
156
145
|
end
|
|
157
146
|
|
|
158
147
|
# The view component of the ingredient with mapped options.
|
|
@@ -169,30 +158,8 @@ module Alchemy
|
|
|
169
158
|
@_view_component_class ||= "#{self.class.name}View".constantize
|
|
170
159
|
end
|
|
171
160
|
|
|
172
|
-
def hint_translation_attribute
|
|
173
|
-
role
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
def hint_translation_scope
|
|
177
|
-
"ingredient_hints"
|
|
178
|
-
end
|
|
179
|
-
|
|
180
161
|
def set_default_value
|
|
181
|
-
self.value = default_value
|
|
182
|
-
end
|
|
183
|
-
|
|
184
|
-
# Returns the default value from ingredient definition
|
|
185
|
-
#
|
|
186
|
-
# If the value is a symbol it gets passed through i18n
|
|
187
|
-
# inside the +alchemy.default_ingredient_texts+ scope
|
|
188
|
-
def default_value
|
|
189
|
-
default = definition[:default]
|
|
190
|
-
case default
|
|
191
|
-
when Symbol
|
|
192
|
-
Alchemy.t(default, scope: :default_ingredient_texts)
|
|
193
|
-
else
|
|
194
|
-
default
|
|
195
|
-
end
|
|
162
|
+
self.value = definition.default_value
|
|
196
163
|
end
|
|
197
164
|
end
|
|
198
165
|
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
class IngredientDefinition
|
|
5
|
+
include ActiveModel::Model
|
|
6
|
+
include ActiveModel::Attributes
|
|
7
|
+
include Alchemy::Hints
|
|
8
|
+
|
|
9
|
+
extend ActiveModel::Translation
|
|
10
|
+
|
|
11
|
+
attribute :role, :string
|
|
12
|
+
attribute :type, :string
|
|
13
|
+
attribute :as_element_title, :boolean, default: false
|
|
14
|
+
attribute :settings, default: {}
|
|
15
|
+
attribute :validate, default: []
|
|
16
|
+
attribute :group, :string
|
|
17
|
+
attribute :default
|
|
18
|
+
attribute :deprecated
|
|
19
|
+
attribute :hint
|
|
20
|
+
|
|
21
|
+
validates :role,
|
|
22
|
+
presence: true,
|
|
23
|
+
format: {
|
|
24
|
+
with: /\A[a-z_-]+\z/
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
validates :type,
|
|
28
|
+
presence: true,
|
|
29
|
+
format: {
|
|
30
|
+
with: /\A[A-Z][a-zA-Z]+\z/
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
delegate :blank?, to: :role
|
|
34
|
+
|
|
35
|
+
def attributes
|
|
36
|
+
super.with_indifferent_access
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def settings
|
|
40
|
+
super.with_indifferent_access
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def validate
|
|
44
|
+
super.map do |validation|
|
|
45
|
+
case validation
|
|
46
|
+
when Hash
|
|
47
|
+
validation.with_indifferent_access
|
|
48
|
+
else
|
|
49
|
+
validation
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Returns the default value from ingredient definition
|
|
55
|
+
#
|
|
56
|
+
# If the value is a symbol it gets passed through i18n
|
|
57
|
+
# inside the +alchemy.default_ingredient_texts+ scope
|
|
58
|
+
def default_value
|
|
59
|
+
case default
|
|
60
|
+
when Symbol
|
|
61
|
+
Alchemy.t(default, scope: :default_ingredient_texts)
|
|
62
|
+
when String
|
|
63
|
+
default
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Returns a deprecation notice for ingredients marked deprecated
|
|
68
|
+
#
|
|
69
|
+
# You can either use localizations or pass a String as notice
|
|
70
|
+
# in the ingredient definition.
|
|
71
|
+
#
|
|
72
|
+
# == Custom deprecation notices
|
|
73
|
+
#
|
|
74
|
+
# Use general ingredient deprecation notice
|
|
75
|
+
#
|
|
76
|
+
# - name: element_name
|
|
77
|
+
# ingredients:
|
|
78
|
+
# - role: old_ingredient
|
|
79
|
+
# type: Text
|
|
80
|
+
# deprecated: true
|
|
81
|
+
#
|
|
82
|
+
# Add a translation to your locale file for a per ingredient notice.
|
|
83
|
+
#
|
|
84
|
+
# en:
|
|
85
|
+
# alchemy:
|
|
86
|
+
# ingredient_deprecation_notices:
|
|
87
|
+
# old_ingredient: Foo baz widget is deprecated
|
|
88
|
+
#
|
|
89
|
+
# You can scope the translation per element as well.
|
|
90
|
+
#
|
|
91
|
+
# en:
|
|
92
|
+
# alchemy:
|
|
93
|
+
# ingredient_deprecation_notices:
|
|
94
|
+
# element_name:
|
|
95
|
+
# old_ingredient: Elements foo baz widget is deprecated
|
|
96
|
+
#
|
|
97
|
+
# or use the global translation that apply to all deprecated ingredients.
|
|
98
|
+
#
|
|
99
|
+
# en:
|
|
100
|
+
# alchemy:
|
|
101
|
+
# ingredient_deprecation_notice: Foo baz widget is deprecated
|
|
102
|
+
#
|
|
103
|
+
# or pass string as deprecation notice.
|
|
104
|
+
#
|
|
105
|
+
# - name: element_name
|
|
106
|
+
# ingredients:
|
|
107
|
+
# - role: old_ingredient
|
|
108
|
+
# type: Text
|
|
109
|
+
# deprecated: This ingredient will be removed soon.
|
|
110
|
+
#
|
|
111
|
+
def deprecation_notice(element_name: nil)
|
|
112
|
+
case deprecated
|
|
113
|
+
when String
|
|
114
|
+
deprecated
|
|
115
|
+
when TrueClass
|
|
116
|
+
Alchemy.t(
|
|
117
|
+
role,
|
|
118
|
+
scope: [:ingredient_deprecation_notices, element_name].compact,
|
|
119
|
+
default: Alchemy.t(:ingredient_deprecated)
|
|
120
|
+
)
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
private
|
|
125
|
+
|
|
126
|
+
def hint_translation_scope
|
|
127
|
+
:ingredient_hints
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
def hint_translation_attribute
|
|
131
|
+
role
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -64,7 +64,7 @@ module Alchemy
|
|
|
64
64
|
attr_reader :ingredient
|
|
65
65
|
|
|
66
66
|
def validations
|
|
67
|
-
ingredient.definition.
|
|
67
|
+
ingredient.definition.validate
|
|
68
68
|
end
|
|
69
69
|
|
|
70
70
|
def validate_presence(*)
|
|
@@ -80,8 +80,12 @@ module Alchemy
|
|
|
80
80
|
end
|
|
81
81
|
|
|
82
82
|
def validate_format(format)
|
|
83
|
-
matcher =
|
|
84
|
-
|
|
83
|
+
matcher = if format.is_a?(String) || format.is_a?(Symbol)
|
|
84
|
+
Alchemy.config.format_matchers.get(format)
|
|
85
|
+
else
|
|
86
|
+
format
|
|
87
|
+
end
|
|
88
|
+
if !ingredient.value.to_s.match?(matcher)
|
|
85
89
|
ingredient.errors.add(:value, :invalid)
|
|
86
90
|
end
|
|
87
91
|
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
module Ingredients
|
|
5
|
+
# A number
|
|
6
|
+
#
|
|
7
|
+
# Either a decimal or integer number
|
|
8
|
+
#
|
|
9
|
+
class Number < Alchemy::Ingredient
|
|
10
|
+
allow_settings %i[
|
|
11
|
+
input_type
|
|
12
|
+
step
|
|
13
|
+
min
|
|
14
|
+
max
|
|
15
|
+
unit
|
|
16
|
+
]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -71,20 +71,6 @@ module Alchemy
|
|
|
71
71
|
on_site(Current.site)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
|
-
# Store the current language in the current thread.
|
|
75
|
-
# @deprecated Use {Alchemy::Current#language=} instead.
|
|
76
|
-
def current=(language)
|
|
77
|
-
Current.language = language
|
|
78
|
-
end
|
|
79
|
-
deprecate "current=": :"Alchemy::Current.language=", deprecator: Alchemy::Deprecation
|
|
80
|
-
|
|
81
|
-
# Current language from current thread or default.
|
|
82
|
-
# @deprecated Use {Alchemy::Current#language} instead.
|
|
83
|
-
def current
|
|
84
|
-
Current.language
|
|
85
|
-
end
|
|
86
|
-
deprecate current: :"Alchemy::Current.language", deprecator: Alchemy::Deprecation
|
|
87
|
-
|
|
88
74
|
# The root page of the current language.
|
|
89
75
|
def current_root_page
|
|
90
76
|
Current.language&.pages&.language_roots&.first
|
|
@@ -16,23 +16,19 @@ module Alchemy
|
|
|
16
16
|
class Message
|
|
17
17
|
include ActiveModel::Model
|
|
18
18
|
|
|
19
|
-
def self.config
|
|
20
|
-
Alchemy::Config.get(:mailer)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
19
|
attr_accessor :contact_form_id, :ip
|
|
24
20
|
|
|
25
|
-
config
|
|
21
|
+
Alchemy.config.mailer.fields.each do |field|
|
|
26
22
|
attr_accessor field.to_sym
|
|
27
23
|
end
|
|
28
24
|
|
|
29
|
-
config
|
|
25
|
+
Alchemy.config.mailer.validate_fields.each do |field|
|
|
30
26
|
validates_presence_of field
|
|
31
27
|
|
|
32
28
|
case field.to_sym
|
|
33
29
|
when /email/
|
|
34
30
|
validates_format_of field,
|
|
35
|
-
with: Alchemy
|
|
31
|
+
with: Alchemy.config.format_matchers.email,
|
|
36
32
|
if: -> { send(field).present? }
|
|
37
33
|
when :email_confirmation
|
|
38
34
|
validates_confirmation_of :email
|
data/app/models/alchemy/node.rb
CHANGED
|
@@ -7,7 +7,7 @@ module Alchemy
|
|
|
7
7
|
before_destroy :check_if_related_node_ingredients_present
|
|
8
8
|
|
|
9
9
|
acts_as_nested_set scope: "language_id", touch: true
|
|
10
|
-
stampable stamper_class_name: Alchemy.
|
|
10
|
+
stampable stamper_class_name: Alchemy.user_class_name
|
|
11
11
|
|
|
12
12
|
belongs_to :language, class_name: "Alchemy::Language"
|
|
13
13
|
belongs_to :page, class_name: "Alchemy::Page", optional: true, inverse_of: :nodes
|
|
@@ -19,27 +19,6 @@ module Alchemy
|
|
|
19
19
|
foreign_key: :related_object_id,
|
|
20
20
|
inverse_of: :related_object
|
|
21
21
|
|
|
22
|
-
# All nodes of the current site whose language is published.
|
|
23
|
-
scope :from_current_public_site, -> {
|
|
24
|
-
joins(:language)
|
|
25
|
-
.where(Alchemy::Language.table_name => {site_id: Alchemy::Current.site})
|
|
26
|
-
.merge(Alchemy::Language.published)
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
# Nodes an anonymous visitor may see: external links or nodes attached to a
|
|
30
|
-
# published, unrestricted page, scoped to the current site.
|
|
31
|
-
scope :available_to_guests, -> {
|
|
32
|
-
from_current_public_site.where(page: nil)
|
|
33
|
-
.or(from_current_public_site.where(page: Alchemy::Page.published.not_restricted))
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
# Like #available_to_guests but including nodes attached to restricted pages,
|
|
37
|
-
# which logged in members are allowed to see.
|
|
38
|
-
scope :available_to_members, -> {
|
|
39
|
-
from_current_public_site.where(page: nil)
|
|
40
|
-
.or(from_current_public_site.where(page: Alchemy::Page.published))
|
|
41
|
-
}
|
|
42
|
-
|
|
43
22
|
before_validation :translate_root_menu_name, if: -> { root? }
|
|
44
23
|
before_validation :set_menu_type_from_root, unless: -> { root? }
|
|
45
24
|
|
|
@@ -4,13 +4,13 @@ module Alchemy
|
|
|
4
4
|
class Page < BaseRecord
|
|
5
5
|
# Module concerning page layouts
|
|
6
6
|
#
|
|
7
|
-
module
|
|
7
|
+
module Definitions
|
|
8
8
|
extend ActiveSupport::Concern
|
|
9
9
|
|
|
10
10
|
module ClassMethods
|
|
11
11
|
# Register a custom page layouts repository
|
|
12
12
|
#
|
|
13
|
-
# The default repository is Alchemy::
|
|
13
|
+
# The default repository is Alchemy::PageDefinition
|
|
14
14
|
#
|
|
15
15
|
def layouts_repository=(klass)
|
|
16
16
|
@_layouts_repository = klass
|
|
@@ -19,8 +19,10 @@ module Alchemy
|
|
|
19
19
|
# Returns page layouts ready for Rails' select form helper.
|
|
20
20
|
#
|
|
21
21
|
def layouts_for_select(language_id, layoutpages: false)
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
layouts = selectable_layouts(language_id, layoutpages: layoutpages)
|
|
23
|
+
layouts.map do |layout|
|
|
24
|
+
[layout.human_name, layout.name]
|
|
25
|
+
end
|
|
24
26
|
end
|
|
25
27
|
|
|
26
28
|
# Returns all layouts that can be used for creating a new page.
|
|
@@ -36,9 +38,9 @@ module Alchemy
|
|
|
36
38
|
@language_id = language_id
|
|
37
39
|
layouts_repository.all.select do |layout|
|
|
38
40
|
if layoutpages
|
|
39
|
-
layout
|
|
41
|
+
layout.layoutpage && layout_available?(layout)
|
|
40
42
|
else
|
|
41
|
-
!layout
|
|
43
|
+
!layout.layoutpage && layout_available?(layout)
|
|
42
44
|
end
|
|
43
45
|
end
|
|
44
46
|
end
|
|
@@ -62,28 +64,19 @@ module Alchemy
|
|
|
62
64
|
private
|
|
63
65
|
|
|
64
66
|
def layouts_repository
|
|
65
|
-
@_layouts_repository ||=
|
|
66
|
-
end
|
|
67
|
-
|
|
68
|
-
# Maps given layouts for Rails select form helper.
|
|
69
|
-
#
|
|
70
|
-
def mapped_layouts_for_select(layouts)
|
|
71
|
-
layouts.each do |layout|
|
|
72
|
-
@map_array << [human_layout_name(layout["name"]), layout["name"]]
|
|
73
|
-
end
|
|
74
|
-
@map_array
|
|
67
|
+
@_layouts_repository ||= PageDefinition
|
|
75
68
|
end
|
|
76
69
|
|
|
77
70
|
# Returns true if the given layout is unique and not already taken or it should be hidden.
|
|
78
71
|
#
|
|
79
72
|
def layout_available?(layout)
|
|
80
|
-
!layout
|
|
73
|
+
!layout.hide && !already_taken?(layout) && available_on_site?(layout)
|
|
81
74
|
end
|
|
82
75
|
|
|
83
76
|
# Returns true if this layout is unique and already taken by another page.
|
|
84
77
|
#
|
|
85
78
|
def already_taken?(layout)
|
|
86
|
-
layout
|
|
79
|
+
layout.unique && page_with_layout_existing?(layout.name)
|
|
87
80
|
end
|
|
88
81
|
|
|
89
82
|
# Returns true if one page already has the given layout
|
|
@@ -106,7 +99,7 @@ module Alchemy
|
|
|
106
99
|
return false unless Alchemy::Current.site
|
|
107
100
|
|
|
108
101
|
Alchemy::Current.site.definition.blank? ||
|
|
109
|
-
Alchemy::Current.site.definition.fetch("page_layouts", []).include?(layout
|
|
102
|
+
Alchemy::Current.site.definition.fetch("page_layouts", []).include?(layout.name)
|
|
110
103
|
end
|
|
111
104
|
end
|
|
112
105
|
end
|