alchemy_cms 8.3.11 → 8.4.0
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/LICENSE +1 -1
- data/README.md +58 -10
- 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/remote-select.css +1 -0
- data/app/assets/builds/alchemy/admin/tags-autocomplete.css +1 -0
- data/app/assets/builds/alchemy/admin.css +4 -4
- data/app/assets/builds/alchemy/alchemy_admin.min.js +1 -1
- data/app/assets/builds/alchemy/alchemy_admin.min.js.map +1 -1
- data/app/assets/builds/alchemy/dark-theme.css +1 -1
- data/app/assets/builds/alchemy/light-theme.css +1 -1
- data/app/assets/builds/alchemy/preview.min.js +1 -1
- data/app/assets/builds/alchemy/theme.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/content/alchemy-dark/content.min.css +1 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy-dark/skin.min.css +1 -1
- data/app/assets/images/alchemy/icons-sprite.svg +1 -1
- data/app/components/alchemy/admin/dashboard/widgets/greeting.html.erb +1 -1
- data/app/components/alchemy/admin/dashboard/widgets/news_reader.html.erb +38 -0
- data/app/components/alchemy/admin/dashboard/widgets/news_reader.rb +54 -0
- data/app/components/alchemy/admin/dashboard/widgets/online_users.html.erb +1 -1
- data/app/components/alchemy/admin/dashboard/widgets/recent_pages.html.erb +1 -1
- data/app/components/alchemy/admin/dashboard/widgets/system_info.html.erb +11 -3
- data/app/components/alchemy/admin/dashboard/widgets/system_info.rb +6 -1
- data/app/components/alchemy/admin/element_editor.html.erb +1 -1
- data/app/components/alchemy/admin/element_select.rb +30 -20
- data/app/components/alchemy/admin/element_status_indicators.html.erb +16 -0
- data/app/components/alchemy/admin/element_status_indicators.rb +31 -0
- data/app/components/alchemy/admin/link_dialog/external_tab.rb +2 -1
- data/app/components/alchemy/admin/main_navigation/entry.rb +61 -0
- data/app/components/alchemy/admin/main_navigation/menu.rb +21 -0
- data/app/components/alchemy/admin/main_navigation/sub_navigation.rb +31 -0
- data/app/components/alchemy/admin/page_node.html.erb +1 -1
- data/app/components/alchemy/admin/page_permitted_roles_select.html.erb +11 -0
- data/app/components/alchemy/admin/page_permitted_roles_select.rb +35 -0
- data/app/components/alchemy/admin/page_select.rb +18 -8
- data/app/components/alchemy/admin/preview_time_select.rb +1 -1
- data/app/components/alchemy/admin/toolbar_button.rb +43 -20
- data/app/components/alchemy/admin/uploader_button.rb +66 -0
- data/app/components/alchemy/ingredients/base_editor.rb +10 -1
- data/app/components/alchemy/ingredients/boolean_editor.rb +1 -0
- data/app/components/alchemy/ingredients/color_editor.rb +6 -3
- data/app/components/alchemy/ingredients/datetime_view.rb +1 -1
- data/app/components/alchemy/ingredients/picture_view.rb +2 -4
- data/app/components/alchemy/menubar.html.erb +157 -0
- data/app/components/alchemy/menubar.rb +15 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +12 -9
- data/app/controllers/alchemy/admin/base_controller.rb +34 -21
- data/app/controllers/alchemy/admin/dashboard_controller.rb +4 -0
- data/app/controllers/alchemy/admin/elements_controller.rb +4 -23
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/nodes_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pictures_controller.rb +6 -3
- data/app/controllers/alchemy/admin/resources_controller.rb +5 -5
- data/app/controllers/alchemy/admin/sites_controller.rb +1 -1
- data/app/controllers/alchemy/base_controller.rb +24 -16
- data/app/controllers/alchemy/pages_controller.rb +29 -8
- data/app/controllers/alchemy/sitemap_controller.rb +58 -0
- data/app/controllers/concerns/alchemy/admin/csp_protection.rb +43 -0
- data/app/controllers/concerns/alchemy/admin/current_language.rb +1 -1
- data/app/controllers/concerns/alchemy/admin/resource_filter.rb +3 -1
- data/{lib → app/controllers/concerns}/alchemy/controller_actions.rb +13 -0
- data/app/controllers/concerns/alchemy/legacy_page_redirects.rb +4 -4
- data/app/helpers/alchemy/admin/base_helper.rb +4 -1
- data/app/helpers/alchemy/admin/form_helper.rb +0 -2
- data/app/helpers/alchemy/admin/navigation_helper.rb +10 -181
- data/app/helpers/alchemy/elements_block_helper.rb +14 -0
- data/app/helpers/alchemy/resources_helper.rb +15 -1
- data/app/javascript/alchemy_admin/components/attachment_select.js +11 -20
- data/app/javascript/alchemy_admin/components/auto_submit.js +2 -4
- data/app/javascript/alchemy_admin/components/button.js +0 -6
- data/app/javascript/alchemy_admin/components/clear_schedule_button.js +21 -0
- data/app/javascript/alchemy_admin/components/color_select.js +50 -45
- data/app/javascript/alchemy_admin/components/conditional_field.js +50 -0
- data/app/javascript/alchemy_admin/components/dirty_guard.js +77 -0
- data/app/javascript/alchemy_admin/components/dom_id_select.js +17 -10
- data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +7 -2
- data/app/javascript/alchemy_admin/components/element_editor.js +29 -56
- data/app/javascript/alchemy_admin/components/element_select.js +26 -66
- data/app/javascript/alchemy_admin/components/icon.js +9 -4
- data/app/javascript/alchemy_admin/{image_cropper.js → components/image_cropper.js} +39 -53
- data/app/javascript/alchemy_admin/components/image_overlay_link.js +14 -0
- data/app/javascript/alchemy_admin/components/index.js +12 -1
- data/app/javascript/alchemy_admin/components/main_navi_entry.js +75 -0
- data/app/javascript/alchemy_admin/components/menu_node.js +73 -0
- data/app/javascript/alchemy_admin/components/news_reader.js +109 -0
- data/app/javascript/alchemy_admin/components/node_folder.js +36 -0
- data/app/javascript/alchemy_admin/components/node_form.js +34 -0
- data/app/javascript/alchemy_admin/components/node_select.js +25 -30
- data/app/javascript/alchemy_admin/components/node_tree.js +56 -0
- data/app/javascript/alchemy_admin/components/page_node.js +2 -1
- data/app/javascript/alchemy_admin/components/page_select.js +16 -34
- data/app/javascript/alchemy_admin/components/picture_archive.js +92 -0
- data/app/javascript/alchemy_admin/components/picture_editor.js +2 -1
- data/app/javascript/alchemy_admin/components/preview_window.js +11 -3
- data/app/javascript/alchemy_admin/components/remote_select.js +461 -81
- data/app/javascript/alchemy_admin/components/select.js +26 -88
- data/app/javascript/alchemy_admin/components/sitemap.js +2 -1
- data/app/javascript/alchemy_admin/components/sortable_elements.js +2 -1
- data/app/javascript/alchemy_admin/components/tags_autocomplete.js +63 -48
- data/app/javascript/alchemy_admin/components/tinymce.js +2 -1
- data/app/javascript/alchemy_admin/components/uploader/file_upload.js +3 -2
- data/app/javascript/alchemy_admin/components/uploader.js +19 -3
- data/app/javascript/alchemy_admin/confirm_dialog.js +7 -1
- data/app/javascript/alchemy_admin/dialog.js +182 -110
- data/app/javascript/alchemy_admin/hotkeys.js +0 -5
- data/app/javascript/alchemy_admin/i18n.js +3 -9
- data/app/javascript/alchemy_admin/image_overlay.js +58 -42
- data/app/javascript/alchemy_admin/initializer.js +2 -2
- data/app/javascript/alchemy_admin/link_dialog.js +15 -2
- data/app/javascript/alchemy_admin/routes.js +18 -0
- data/app/javascript/alchemy_admin/turbo_stream_actions.js +88 -0
- data/app/javascript/alchemy_admin/utils/ajax.js +14 -3
- data/app/javascript/alchemy_admin/utils/events.js +5 -0
- data/app/javascript/alchemy_admin/utils/json_script.js +12 -0
- data/app/javascript/alchemy_admin/utils/tom_select.js +171 -0
- data/app/javascript/alchemy_admin.js +2 -19
- data/app/javascript/preview.js +131 -113
- data/app/jobs/alchemy/invalidate_elements_cache_job.rb +1 -1
- data/app/models/alchemy/admin/content_security_policy.rb +143 -0
- data/app/models/alchemy/admin/filters/base.rb +7 -0
- data/app/models/alchemy/admin/filters/datepicker.rb +1 -1
- data/app/models/alchemy/admin/filters/select.rb +20 -7
- data/app/models/alchemy/attachment.rb +14 -7
- data/app/models/alchemy/cache_control.rb +104 -0
- data/app/models/alchemy/current.rb +1 -1
- data/app/models/alchemy/eager_loading.rb +0 -2
- data/app/models/alchemy/element.rb +23 -8
- data/app/models/alchemy/element_definition.rb +1 -0
- data/app/models/alchemy/elements_repository.rb +11 -0
- data/app/models/alchemy/folded_page.rb +5 -1
- data/app/models/alchemy/ingredient.rb +26 -0
- data/app/models/alchemy/ingredients/datetime.rb +1 -1
- data/app/models/alchemy/ingredients/link.rb +2 -0
- data/app/models/alchemy/ingredients/picture.rb +3 -0
- data/app/models/alchemy/ingredients/text.rb +2 -0
- data/app/models/alchemy/language.rb +19 -7
- data/app/models/alchemy/legacy_page_url.rb +6 -1
- data/app/models/alchemy/node.rb +51 -0
- data/app/models/alchemy/page/page_natures.rb +41 -8
- data/app/models/alchemy/page/page_scopes.rb +23 -0
- data/app/models/alchemy/page/sitemap_cache_key.rb +56 -0
- data/app/models/alchemy/page.rb +88 -32
- data/app/models/alchemy/page_definition.rb +1 -1
- data/app/models/alchemy/page_mutex.rb +16 -0
- data/app/models/alchemy/page_version.rb +24 -1
- data/app/models/alchemy/permissions.rb +6 -5
- data/app/models/alchemy/picture.rb +15 -7
- data/app/models/alchemy/picture_description.rb +22 -0
- data/app/models/alchemy/picture_thumb.rb +18 -0
- data/app/models/alchemy/publishable/timestamp_resolver.rb +70 -0
- data/app/models/alchemy/resource.rb +1 -1
- data/app/models/alchemy/safe_url_validator.rb +34 -0
- data/app/models/alchemy/site.rb +8 -3
- data/app/models/alchemy/storage_adapter/active_storage/preprocessor.rb +7 -3
- data/app/models/alchemy/storage_adapter/active_storage.rb +4 -1
- data/app/models/alchemy/tag.rb +10 -4
- data/{lib → app/models/concerns}/alchemy/filetypes.rb +5 -0
- data/app/models/concerns/alchemy/picture_thumbnails.rb +61 -6
- data/app/models/concerns/alchemy/publishable.rb +14 -27
- data/app/models/concerns/alchemy/taggable.rb +92 -0
- data/app/models/concerns/alchemy/user_methods.rb +95 -0
- data/app/services/alchemy/duplicate_element.rb +5 -2
- data/app/services/alchemy/element_preloader.rb +1 -2
- data/app/stylesheets/alchemy/_custom-properties.scss +1 -0
- data/app/stylesheets/alchemy/_extends.scss +0 -8
- data/app/stylesheets/alchemy/_themes.scss +10 -9
- data/app/stylesheets/alchemy/admin/_empty-state.scss +29 -0
- data/app/stylesheets/alchemy/admin/_help.scss +10 -0
- data/app/stylesheets/alchemy/admin/_tom-select.scss +104 -15
- data/app/stylesheets/alchemy/admin/archive.scss +56 -11
- data/app/stylesheets/alchemy/admin/base.scss +4 -5
- data/app/stylesheets/alchemy/admin/buttons.scss +12 -1
- data/app/stylesheets/alchemy/admin/dashboard.scss +150 -20
- data/app/stylesheets/alchemy/admin/dialogs.scss +27 -36
- data/app/stylesheets/alchemy/admin/element-select.scss +1 -1
- data/app/stylesheets/alchemy/admin/elements.scss +8 -24
- data/app/stylesheets/alchemy/admin/flash.scss +2 -1
- data/app/stylesheets/alchemy/admin/forms.scss +60 -24
- data/app/stylesheets/alchemy/admin/frame.scss +40 -4
- data/app/stylesheets/alchemy/admin/image_library.scss +9 -12
- data/app/stylesheets/alchemy/admin/navigation.scss +17 -14
- data/app/stylesheets/alchemy/admin/node-select.scss +13 -37
- data/app/stylesheets/alchemy/admin/nodes.scss +1 -1
- data/app/stylesheets/alchemy/admin/page-select.scss +6 -97
- data/app/stylesheets/alchemy/admin/print.scss +0 -4
- data/app/stylesheets/alchemy/admin/remote-select.scss +170 -0
- data/app/stylesheets/alchemy/admin/search.scss +2 -3
- data/app/stylesheets/alchemy/admin/selects.scss +1 -294
- data/app/stylesheets/alchemy/admin/sitemap.scss +2 -1
- data/app/stylesheets/alchemy/admin/tables.scss +7 -1
- data/app/stylesheets/alchemy/admin/tags-autocomplete.scss +17 -0
- data/app/stylesheets/alchemy/admin/tags.scss +0 -13
- data/app/stylesheets/alchemy/admin/toolbar.scss +9 -6
- data/app/stylesheets/alchemy/admin/upload.scss +61 -1
- data/app/stylesheets/alchemy/admin.scss +4 -1
- data/app/types/alchemy/cache_control_type.rb +84 -0
- data/app/views/alchemy/_edit_mode.html.erb +1 -1
- data/app/views/alchemy/_menubar.html.erb +2 -154
- data/app/views/alchemy/_preview_mode_code.html.erb +0 -3
- data/app/views/alchemy/admin/_left_menu.html.erb +18 -5
- data/app/views/alchemy/admin/_top_menu.html.erb +1 -1
- data/app/views/alchemy/admin/attachments/_archive_overlay.html.erb +5 -4
- data/app/views/alchemy/admin/attachments/_file_to_assign.html.erb +3 -3
- data/app/views/alchemy/admin/attachments/_files_list.html.erb +71 -83
- data/app/views/alchemy/admin/attachments/assign.turbo_stream.erb +8 -0
- data/app/views/alchemy/admin/attachments/index.html.erb +32 -7
- data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +1 -1
- data/app/views/alchemy/admin/crop.html.erb +20 -20
- data/app/views/alchemy/admin/dashboard/_footer.html.erb +0 -3
- data/app/views/alchemy/admin/dashboard/_top.html.erb +1 -0
- data/app/views/alchemy/admin/dashboard/help.html.erb +84 -57
- data/app/views/alchemy/admin/dashboard/license.html.erb +6 -0
- data/app/views/alchemy/admin/elements/_footer.html.erb +0 -1
- data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_header.html.erb +2 -19
- data/app/views/alchemy/admin/elements/_preview_text_quote.html.erb +3 -0
- data/app/views/alchemy/admin/elements/_schedule.html.erb +7 -7
- data/app/views/alchemy/admin/elements/_toolbar.html.erb +1 -1
- data/app/views/alchemy/admin/elements/create.turbo_stream.erb +1 -1
- data/app/views/alchemy/admin/elements/index.html.erb +1 -1
- data/app/views/alchemy/admin/elements/publish.turbo_stream.erb +2 -2
- data/app/views/alchemy/admin/elements/update.turbo_stream.erb +31 -0
- data/app/views/alchemy/admin/ingredients/update.turbo_stream.erb +6 -3
- data/app/views/alchemy/admin/languages/_table.html.erb +0 -2
- data/app/views/alchemy/admin/languages/index.html.erb +25 -29
- data/app/views/alchemy/admin/layoutpages/_layoutpage.html.erb +1 -3
- data/app/views/alchemy/admin/layoutpages/edit.html.erb +1 -1
- data/app/views/alchemy/admin/layoutpages/index.html.erb +26 -7
- data/app/views/alchemy/admin/nodes/_form.html.erb +3 -21
- data/app/views/alchemy/admin/nodes/_node.html.erb +93 -88
- data/app/views/alchemy/admin/nodes/index.html.erb +15 -23
- data/app/views/alchemy/admin/pages/_create_language_form.html.erb +2 -6
- data/app/views/alchemy/admin/pages/_form.html.erb +2 -1
- data/app/views/alchemy/admin/pages/_publication_fields.html.erb +1 -1
- data/app/views/alchemy/admin/pages/_table.html.erb +2 -4
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +2 -1
- data/app/views/alchemy/admin/pages/edit.html.erb +10 -21
- data/app/views/alchemy/admin/pages/flush.turbo_stream.erb +1 -1
- data/app/views/alchemy/admin/pages/index.html.erb +26 -31
- data/app/views/alchemy/admin/pages/info.html.erb +2 -2
- data/app/views/alchemy/admin/pages/update.turbo_stream.erb +2 -2
- data/app/views/alchemy/admin/partials/_language_tree_select.html.erb +1 -1
- data/app/views/alchemy/admin/partials/_routes.html.erb +24 -48
- data/app/views/alchemy/admin/pictures/_archive.html.erb +3 -17
- 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 -3
- data/app/views/alchemy/admin/pictures/_form.html.erb +0 -1
- data/app/views/alchemy/admin/pictures/_infos.html.erb +5 -1
- data/app/views/alchemy/admin/pictures/_picture.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +3 -4
- data/app/views/alchemy/admin/pictures/assign.turbo_stream.erb +7 -0
- data/app/views/alchemy/admin/pictures/index.html.erb +38 -30
- data/app/views/alchemy/admin/pictures/show.html.erb +41 -41
- data/app/views/alchemy/admin/resources/_empty_state.html.erb +27 -0
- data/app/views/alchemy/admin/resources/_pagination.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_per_page_select.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_resource_table.html.erb +0 -2
- data/app/views/alchemy/admin/resources/_resource_usage_info.html.erb +3 -2
- data/app/views/alchemy/admin/resources/index.html.erb +32 -13
- data/app/views/alchemy/admin/sites/index.html.erb +24 -22
- data/app/views/alchemy/admin/styleguide/index.html.erb +35 -12
- data/app/views/alchemy/admin/tags/index.html.erb +27 -25
- data/app/views/alchemy/admin/tinymce/_setup.html.erb +6 -8
- data/app/views/alchemy/admin/translations/_en.js +3 -0
- data/app/views/alchemy/admin/uploader/_button.html.erb +11 -17
- data/app/views/alchemy/admin/uploader/_setup.html.erb +3 -3
- data/app/views/alchemy/pages/sitemap.xml.erb +2 -0
- data/app/views/kaminari/alchemy/_first_page.html.erb +1 -2
- data/app/views/kaminari/alchemy/_gap.html.erb +0 -1
- data/app/views/kaminari/alchemy/_last_page.html.erb +1 -2
- data/app/views/kaminari/alchemy/_next_page.html.erb +1 -2
- data/app/views/kaminari/alchemy/_page.html.erb +1 -2
- data/app/views/kaminari/alchemy/_paginator.html.erb +0 -1
- data/app/views/kaminari/alchemy/_prev_page.html.erb +1 -2
- data/app/views/layouts/alchemy/admin/dialog.html.erb +3 -0
- data/app/views/layouts/alchemy/admin.html.erb +5 -6
- data/config/brakeman.ignore +23 -1
- data/config/importmap.rb +10 -8
- data/config/initializers/dragonfly.rb +21 -17
- data/config/locales/alchemy.en.yml +52 -11
- data/config/routes.rb +3 -3
- data/db/migrate/20260727120000_add_permitted_roles_to_alchemy_pages.rb +23 -0
- data/db/migrate/20260820120000_add_cached_tag_list_to_taggables.rb +57 -0
- data/lib/alchemy/configuration/number_option.rb +13 -0
- data/lib/alchemy/configuration/proc_option.rb +13 -0
- data/lib/alchemy/configuration.rb +2 -0
- data/lib/alchemy/configurations/admin_components.rb +30 -0
- data/lib/alchemy/configurations/format_matchers.rb +7 -1
- data/lib/alchemy/configurations/main.rb +103 -4
- data/lib/alchemy/configurations/menu.rb +91 -0
- data/lib/alchemy/configurations/menu_item.rb +123 -0
- data/lib/alchemy/configurations/sitemap.rb +1 -0
- data/lib/alchemy/configurations/uploader.rb +15 -1
- data/lib/alchemy/engine.rb +0 -6
- data/lib/alchemy/i18n.rb +26 -0
- data/lib/alchemy/modules.rb +52 -50
- data/lib/alchemy/tasks/generate_thumbnails.rb +99 -0
- data/lib/alchemy/tasks/tidy.rb +1 -1
- data/lib/alchemy/test_support/capybara_helpers.rb +48 -37
- data/lib/alchemy/test_support/current_language_shared_examples.rb +1 -1
- data/lib/alchemy/test_support/factories/dummy_user_factory.rb +4 -1
- data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +132 -0
- data/lib/alchemy/test_support/shared_ingredient_examples.rb +52 -0
- data/lib/alchemy/test_support/shared_publishable_examples.rb +29 -4
- data/lib/alchemy/upgrader/eight_four.rb +237 -0
- data/lib/alchemy/upgrader/register_module_rewriter.rb +177 -0
- data/lib/alchemy/upgrader.rb +2 -1
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy.rb +0 -1
- data/lib/alchemy_cms.rb +0 -13
- data/lib/generators/alchemy/elements/templates/view.html.erb +1 -1
- data/lib/generators/alchemy/elements/templates/view.html.haml +1 -1
- data/lib/generators/alchemy/elements/templates/view.html.slim +1 -1
- data/lib/generators/alchemy/install/install_generator.rb +2 -13
- data/lib/generators/alchemy/install/templates/alchemy.rb.tt +24 -9
- data/lib/generators/alchemy/module/module_generator.rb +1 -0
- data/lib/generators/alchemy/module/templates/ability.rb.tt +1 -1
- data/lib/generators/alchemy/module/templates/module_config.rb.tt +16 -18
- data/lib/generators/alchemy/user_columns_migration/templates/migration.rb.tt +19 -0
- data/lib/generators/alchemy/user_columns_migration/user_columns_migration_generator.rb +30 -0
- data/lib/tasks/alchemy/thumbnails.rake +16 -29
- data/lib/tasks/alchemy/upgrade.rake +45 -3
- data/vendor/javascript/cropperjs.min.js +3 -3
- data/vendor/javascript/floating-ui.min.js +1 -1
- data/vendor/javascript/tinymce.min.js +5 -5
- data/vendor/javascript/tom-select.min.js +1 -1
- metadata +85 -65
- data/app/javascript/alchemy_admin/components/action.js +0 -50
- data/app/javascript/alchemy_admin/dirty.js +0 -55
- data/app/javascript/alchemy_admin/node_tree.js +0 -82
- data/app/javascript/alchemy_admin/picture_selector.js +0 -67
- data/app/javascript/alchemy_admin/templates/compiled.js +0 -1
- data/app/javascript/alchemy_admin/templates/node_folder.hbs +0 -3
- data/app/stylesheets/alchemy/admin/attachment-select.scss +0 -19
- data/app/views/alchemy/admin/_main_navi.html.erb +0 -6
- data/app/views/alchemy/admin/attachments/assign.js.erb +0 -15
- data/app/views/alchemy/admin/pages/_page_layout_filter.html.erb +0 -30
- data/app/views/alchemy/admin/partials/_main_navigation_entry.html.erb +0 -20
- data/app/views/alchemy/admin/partials/_sub_navigation.html.erb +0 -11
- data/app/views/alchemy/admin/pictures/assign.js.erb +0 -13
- data/app/views/alchemy/base/error_notice.js.erb +0 -1
- data/app/views/alchemy/base/permission_denied.js.erb +0 -3
- data/app/views/alchemy/base/redirect.js.erb +0 -11
- data/config/alchemy/config.yml +0 -217
- data/config/alchemy/modules.yml +0 -82
- data/config/initializers/rails_live_reload.rb +0 -13
- data/lib/alchemy/dev_support/live_reload_watcher.rb +0 -5
- data/lib/alchemy/taggable.rb +0 -49
- data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +0 -34
- data/vendor/assets/stylesheets/alchemy_admin/select2.css +0 -1
- data/vendor/javascript/handlebars.min.js +0 -29
- data/vendor/javascript/jquery.min.js +0 -2
- data/vendor/javascript/rails-ujs.min.js +0 -1
- data/vendor/javascript/select2.min.js +0 -23
- /data/{lib → app/controllers/concerns}/alchemy/ability_helper.rb +0 -0
- /data/{lib → app/controllers/concerns}/alchemy/admin/locale.rb +0 -0
- /data/{lib → app/controllers/concerns}/alchemy/admin/preview_time.rb +0 -0
- /data/{lib → app/controllers/concerns}/alchemy/admin/timezone.rb +0 -0
- /data/{lib → app/controllers/concerns}/alchemy/configuration_methods.rb +0 -0
- /data/{lib → app/models}/alchemy/admin/preview_url.rb +0 -0
- /data/{lib → app/models/concerns}/alchemy/hints.rb +0 -0
- /data/{lib → app/models/concerns}/alchemy/name_conversions.rb +0 -0
- /data/{lib → app/services}/alchemy/elements_finder.rb +0 -0
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
current_page: a page object for the currently displayed page
|
|
5
5
|
num_pages: total number of pages
|
|
6
6
|
per_page: number of items to fetch per page
|
|
7
|
-
remote: data-remote
|
|
8
7
|
-%>
|
|
9
8
|
<% if current_page.first? %>
|
|
10
9
|
<span class="first-page disabled">
|
|
@@ -12,7 +11,7 @@
|
|
|
12
11
|
</span>
|
|
13
12
|
<% else %>
|
|
14
13
|
<sl-tooltip content="<%= Alchemy.t(:first, scope: 'pagination') %>" placement="top-start">
|
|
15
|
-
<%= link_to url,
|
|
14
|
+
<%= link_to url, class: 'first-page' do %>
|
|
16
15
|
<%= render_icon("skip-left") %>
|
|
17
16
|
<% end %>
|
|
18
17
|
</sl-tooltip>
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
current_page: a page object for the currently displayed page
|
|
5
5
|
num_pages: total number of pages
|
|
6
6
|
per_page: number of items to fetch per page
|
|
7
|
-
remote: data-remote
|
|
8
7
|
-%>
|
|
9
8
|
<% if current_page.last? %>
|
|
10
9
|
<span class="last-page disabled">
|
|
@@ -12,7 +11,7 @@
|
|
|
12
11
|
</span>
|
|
13
12
|
<% else %>
|
|
14
13
|
<sl-tooltip content="<%= Alchemy.t(:last, scope: 'pagination') %>" placement="top-end">
|
|
15
|
-
<%= link_to url,
|
|
14
|
+
<%= link_to url, class: 'last-page' do %>
|
|
16
15
|
<%= render_icon("skip-right") %>
|
|
17
16
|
<% end %>
|
|
18
17
|
</sl-tooltip>
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
current_page: a page object for the currently displayed page
|
|
5
5
|
num_pages: total number of pages
|
|
6
6
|
per_page: number of items to fetch per page
|
|
7
|
-
remote: data-remote
|
|
8
7
|
-%>
|
|
9
8
|
<% if current_page.last? -%>
|
|
10
9
|
<span class="next_page disabled">
|
|
@@ -12,7 +11,7 @@
|
|
|
12
11
|
</span>
|
|
13
12
|
<% else -%>
|
|
14
13
|
<sl-tooltip content="<%= Alchemy.t(:next_page, scope: 'pagination') %>" placement="top-end">
|
|
15
|
-
<%= link_to url, rel: 'next',
|
|
14
|
+
<%= link_to url, rel: 'next', class: 'next_page' do %>
|
|
16
15
|
<%= render_icon("arrow-right-s") %>
|
|
17
16
|
<% end %>
|
|
18
17
|
</sl-tooltip>
|
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
current_page: a page object for the currently displayed page
|
|
6
6
|
num_pages: total number of pages
|
|
7
7
|
per_page: number of items to fetch per page
|
|
8
|
-
remote: data-remote
|
|
9
8
|
-%>
|
|
10
9
|
<% if page.current? -%>
|
|
11
10
|
<span class="page current"><%= page -%></span>
|
|
12
11
|
<% else -%>
|
|
13
|
-
<%= link_to page, url, opts = {
|
|
12
|
+
<%= link_to page, url, opts = {rel: page.next? ? 'next' : page.prev? ? 'prev' : nil, class: 'page'} -%>
|
|
14
13
|
<% end -%>
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
current_page: a page object for the currently displayed page
|
|
5
5
|
num_pages: total number of pages
|
|
6
6
|
per_page: number of items to fetch per page
|
|
7
|
-
remote: data-remote
|
|
8
7
|
-%>
|
|
9
8
|
<% if current_page.first? -%>
|
|
10
9
|
<span class="previous_page disabled">
|
|
@@ -12,7 +11,7 @@
|
|
|
12
11
|
</span>
|
|
13
12
|
<% else -%>
|
|
14
13
|
<sl-tooltip class="pagination-label" content="<%= Alchemy.t(:previous_page, scope: 'pagination') %>" placement="top-start">
|
|
15
|
-
<%= link_to url, rel: 'prev',
|
|
14
|
+
<%= link_to url, rel: 'prev', class: 'previous_page' do %>
|
|
16
15
|
<%= render_icon("arrow-left-s") %>
|
|
17
16
|
<% end %>
|
|
18
17
|
</sl-tooltip>
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
<link rel="shortcut icon" href="<%= asset_path('alchemy/favicon.ico') %>">
|
|
9
9
|
<link rel="preload" href="<%= asset_path("alchemy/icons-sprite.svg") %>" as="image" type="<%= Mime::Type.lookup_by_extension(:svg) %>" crossorigin>
|
|
10
10
|
<%= csrf_meta_tag %>
|
|
11
|
+
<%= csp_meta_tag %>
|
|
11
12
|
<meta name="robots" content="noindex">
|
|
12
13
|
<meta name="turbo-prefetch" content="false">
|
|
13
14
|
<meta name="turbo-cache-control" content="no-cache">
|
|
@@ -18,21 +19,19 @@
|
|
|
18
19
|
<%= stylesheet_link_tag(stylesheet, 'data-turbo-track' => true) %>
|
|
19
20
|
<% end %>
|
|
20
21
|
<%= yield :stylesheets %>
|
|
21
|
-
|
|
22
|
+
<%= tag.script(nonce: content_security_policy_nonce) do %>
|
|
22
23
|
// Global Alchemy JavaScript object.
|
|
23
24
|
var Alchemy = {};
|
|
24
|
-
// Store regular expression for external link url matching.
|
|
25
|
-
Alchemy.link_url_regexp = <%= link_url_regexp.inspect %>;
|
|
26
25
|
// JS translations
|
|
27
26
|
<%= alchemy_admin_js_translations %>
|
|
28
|
-
|
|
27
|
+
<% end %>
|
|
29
28
|
<%= render 'alchemy/admin/tinymce/setup' %>
|
|
30
29
|
<%= render 'alchemy/admin/partials/routes' %>
|
|
31
30
|
<%= javascript_importmap_tags("alchemy_admin", importmap: Alchemy.importmap) %>
|
|
32
31
|
<% Alchemy.config.admin_js_imports.each do |path| %>
|
|
33
|
-
|
|
32
|
+
<%= tag.script(type: "module", nonce: content_security_policy_nonce) do %>
|
|
34
33
|
import "<%= path %>"
|
|
35
|
-
|
|
34
|
+
<% end %>
|
|
36
35
|
<% end %>
|
|
37
36
|
<%= yield :javascript_includes %>
|
|
38
37
|
</head>
|
data/config/brakeman.ignore
CHANGED
|
@@ -55,7 +55,29 @@
|
|
|
55
55
|
79
|
|
56
56
|
],
|
|
57
57
|
"note": ""
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
"warning_type": "Format Validation",
|
|
61
|
+
"warning_code": 30,
|
|
62
|
+
"fingerprint": "c9db7cc83a6210bf4a715ce9f9ffabca1145d51db9fc1ca1e45d965e7151751b",
|
|
63
|
+
"check_name": "ValidationRegex",
|
|
64
|
+
"message": "Insufficient validation for `url` using `/\\A(\\/|\\D[a-z+\\d.-]+:)/`. Use `\\A` and `\\z` as anchors",
|
|
65
|
+
"file": "app/models/alchemy/node.rb",
|
|
66
|
+
"line": 99,
|
|
67
|
+
"link": "https://brakemanscanner.org/docs/warning_types/format_validation/",
|
|
68
|
+
"code": ":url, { :format => ({ :with => /\\A(\\/|\\D[a-z+\\d.-]+:)/ }), :unless => (lambda do\n url.nil?\n end) }",
|
|
69
|
+
"render_path": null,
|
|
70
|
+
"location": {
|
|
71
|
+
"type": "model",
|
|
72
|
+
"model": "Alchemy::Node"
|
|
73
|
+
},
|
|
74
|
+
"user_input": null,
|
|
75
|
+
"confidence": "High",
|
|
76
|
+
"cwe_id": [
|
|
77
|
+
777
|
|
78
|
+
],
|
|
79
|
+
"note": "VALID_URL_REGEX deliberately only checks the url prefix (a path or a scheme). It is anchored with \\A, so the multiline bypass does not apply, and executable schemes are rejected by Alchemy::SafeUrlValidator."
|
|
58
80
|
}
|
|
59
81
|
],
|
|
60
|
-
"brakeman_version": "
|
|
82
|
+
"brakeman_version": "8.1.0"
|
|
61
83
|
}
|
data/config/importmap.rb
CHANGED
|
@@ -2,15 +2,11 @@ pin "@ungap/custom-elements", to: "ungap-custom-elements.min.js", preload: true
|
|
|
2
2
|
pin "clipboard", to: "clipboard.min.js", preload: true
|
|
3
3
|
pin "cropperjs", to: "cropperjs.min.js", preload: true
|
|
4
4
|
pin "flatpickr", to: "flatpickr.min.js", preload: true # @4.6.13
|
|
5
|
-
pin "handlebars", to: "handlebars.min.js", preload: true # @4.7.8
|
|
6
|
-
pin "jquery", to: "jquery.min.js", preload: true
|
|
7
5
|
pin "keymaster", to: "keymaster.min.js", preload: true
|
|
8
|
-
pin "select2", to: "select2.min.js", preload: true
|
|
9
6
|
pin "sortablejs", to: "sortable.min.js", preload: true # @1.15.1
|
|
10
7
|
pin "@floating-ui/dom", to: "floating-ui.min.js", preload: true
|
|
11
8
|
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
|
|
12
9
|
pin "shoelace", to: "shoelace.min.js", preload: true
|
|
13
|
-
pin "@rails/ujs", to: "rails-ujs.min.js", preload: true # @7.1.2
|
|
14
10
|
pin "tinymce", to: "tinymce.min.js", preload: true
|
|
15
11
|
pin "tom-select", to: "tom-select.min.js", preload: true
|
|
16
12
|
|
|
@@ -27,8 +23,14 @@ pin "alchemy_admin/components/tinymce"
|
|
|
27
23
|
pin "alchemy_admin/components/remote_select"
|
|
28
24
|
pin "alchemy_admin/components/select"
|
|
29
25
|
pin "alchemy_admin/i18n"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
pin "alchemy_admin/
|
|
26
|
+
# Dependencies of the standalone components above. The admin bundle inlines
|
|
27
|
+
# these, but the source files import them by name, so importing a component on
|
|
28
|
+
# its own fails without pins of their own.
|
|
29
|
+
pin "alchemy_admin/components/icon"
|
|
30
|
+
pin "alchemy_admin/growler"
|
|
31
|
+
pin "alchemy_admin/utils/ajax"
|
|
32
|
+
pin "alchemy_admin/utils/dom_helpers"
|
|
33
|
+
pin "alchemy_admin/utils/json_script"
|
|
34
|
+
pin "alchemy_admin/utils/tom_select"
|
|
35
|
+
pin "tom-select/utils", to: "tom-select.min.js"
|
|
34
36
|
pin "alchemy_admin/utils/events", to: "alchemy/alchemy_admin.min.js"
|
|
@@ -1,24 +1,28 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
# Dragonfly is only needed for the legacy dragonfly storage adapter
|
|
4
|
+
# and needs to be added to the app's Gemfile.
|
|
5
|
+
if defined?(::Dragonfly)
|
|
6
|
+
require "alchemy/dragonfly/processors/crop_resize"
|
|
7
|
+
require "alchemy/dragonfly/processors/auto_orient"
|
|
8
|
+
require "alchemy/dragonfly/processors/thumbnail"
|
|
6
9
|
|
|
7
|
-
# Logger
|
|
8
|
-
Dragonfly.logger = Rails.logger
|
|
10
|
+
# Logger
|
|
11
|
+
Dragonfly.logger = Rails.logger
|
|
9
12
|
|
|
10
|
-
# Add model functionality
|
|
11
|
-
if defined?(ActiveRecord::Base)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
end
|
|
13
|
+
# Add model functionality
|
|
14
|
+
if defined?(ActiveRecord::Base)
|
|
15
|
+
ActiveRecord::Base.extend Dragonfly::Model
|
|
16
|
+
ActiveRecord::Base.extend Dragonfly::Model::Validations
|
|
17
|
+
end
|
|
15
18
|
|
|
16
|
-
# Dragonfly 1.4.0 only allows `quality` as argument to `encode`
|
|
17
|
-
Dragonfly::ImageMagick::Processors::Encode::WHITELISTED_ARGS << "flatten"
|
|
18
|
-
Dragonfly::ImageMagick::Processors::Encode::WHITELISTED_ARGS << "background"
|
|
19
|
+
# Dragonfly 1.4.0 only allows `quality` as argument to `encode`
|
|
20
|
+
Dragonfly::ImageMagick::Processors::Encode::WHITELISTED_ARGS << "flatten"
|
|
21
|
+
Dragonfly::ImageMagick::Processors::Encode::WHITELISTED_ARGS << "background"
|
|
19
22
|
|
|
20
|
-
Rails.application.config.after_initialize do
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
Rails.application.config.after_initialize do
|
|
24
|
+
Dragonfly.app(:alchemy_pictures).add_processor(:crop_resize, Alchemy::Dragonfly::Processors::CropResize.new)
|
|
25
|
+
Dragonfly.app(:alchemy_pictures).add_processor(:auto_orient, Alchemy::Dragonfly::Processors::AutoOrient.new)
|
|
26
|
+
Dragonfly.app(:alchemy_pictures).add_processor(:thumbnail, Alchemy::Dragonfly::Processors::Thumbnail.new)
|
|
27
|
+
end
|
|
24
28
|
end
|
|
@@ -79,9 +79,13 @@ en:
|
|
|
79
79
|
application/x-rar: RAR Archive
|
|
80
80
|
application/x-shockwave-flash: Flash Movie
|
|
81
81
|
application/zip: ZIP Archive
|
|
82
|
+
audio/aac: AAC Audio
|
|
83
|
+
audio/flac: FLAC Audio
|
|
82
84
|
audio/mp4: MPEG-4 Audio
|
|
83
85
|
audio/mpeg: MP3 Audio
|
|
86
|
+
audio/ogg: Ogg Audio
|
|
84
87
|
audio/wav: WAV Audio
|
|
88
|
+
audio/webm: WebM Audio
|
|
85
89
|
audio/x-wav: WAV Audio
|
|
86
90
|
image/avif: AVIF Image
|
|
87
91
|
image/gif: GIF Image
|
|
@@ -95,6 +99,7 @@ en:
|
|
|
95
99
|
text/x-vcard: vCard
|
|
96
100
|
video/mp4: MPEG-4 Video
|
|
97
101
|
video/mpeg: MPEG Video
|
|
102
|
+
video/ogg: Ogg Video
|
|
98
103
|
video/quicktime: Quicktime Video
|
|
99
104
|
video/webm: WebM Video
|
|
100
105
|
video/x-flv: Flash Video
|
|
@@ -236,6 +241,9 @@ en:
|
|
|
236
241
|
published: "published"
|
|
237
242
|
user_counts:
|
|
238
243
|
online: "online"
|
|
244
|
+
news_reader:
|
|
245
|
+
widget_title: "News"
|
|
246
|
+
no_news: "no news"
|
|
239
247
|
elements:
|
|
240
248
|
toolbar:
|
|
241
249
|
hide: Hide
|
|
@@ -364,6 +372,7 @@ en:
|
|
|
364
372
|
"Site successfully updated": "Website successfully updated."
|
|
365
373
|
"Size": "Size"
|
|
366
374
|
"Successfully deleted element": "Successfully deleted %{element}"
|
|
375
|
+
updated_ingredient: "Successfully updated %{ingredient}"
|
|
367
376
|
"Tags": "Tags"
|
|
368
377
|
"This page is locked": "This page is currently locked by %{name}"
|
|
369
378
|
"Title": "Title"
|
|
@@ -491,8 +500,11 @@ en:
|
|
|
491
500
|
"Leave the page": "Leave the page"
|
|
492
501
|
"Open upload form": "Open upload form"
|
|
493
502
|
"Select all pictures": "Select all pictures"
|
|
503
|
+
resources: Resources
|
|
504
|
+
resource_links:
|
|
505
|
+
guidelines: "Guidelines"
|
|
506
|
+
homepage: "Homepage"
|
|
494
507
|
hide_element_content: "Hide this elements content."
|
|
495
|
-
homepage_does_not_exist: "This language has no homepage yet"
|
|
496
508
|
dashboard: "Dashboard"
|
|
497
509
|
image_alt_tag: "Alt-tag"
|
|
498
510
|
image_caption: "Caption"
|
|
@@ -509,6 +521,9 @@ en:
|
|
|
509
521
|
language_pages_copied: "Language tree successfully copied."
|
|
510
522
|
left: "left"
|
|
511
523
|
legacy_url_info_text: "A link is a redirect from an old URL to the current URL of this page. This redirect happens with a <a href='https://support.google.com/webmasters/answer/93633' target='_blank'>301 status code</a>."
|
|
524
|
+
license: "License"
|
|
525
|
+
licenses:
|
|
526
|
+
BSD-3-Clause: "BSD-3-Clause"
|
|
512
527
|
link_image: "Link this image."
|
|
513
528
|
link_overlay_tab_label:
|
|
514
529
|
anchor: "Anchor"
|
|
@@ -550,14 +565,33 @@ en:
|
|
|
550
565
|
"no users": "no users"
|
|
551
566
|
no_default_language_found: "No default language found."
|
|
552
567
|
no_element_given: "No element given."
|
|
553
|
-
no_files_in_archive: "You do not have any files in your archive."
|
|
554
|
-
no_images_in_archive: "You don't have any images in your archive."
|
|
555
568
|
no_more_elements_to_add: "No more elements available."
|
|
556
569
|
no_resource_found:
|
|
557
|
-
default:
|
|
558
|
-
|
|
559
|
-
alchemy/
|
|
560
|
-
|
|
570
|
+
default: |
|
|
571
|
+
<h2>You have no %{resource} yet.</h2>
|
|
572
|
+
alchemy/attachment: |
|
|
573
|
+
<h2>You do not have any files in your archive yet.</h2>
|
|
574
|
+
<p>Use the button below or drag files into this window to upload your first file.</p>
|
|
575
|
+
alchemy/language: |
|
|
576
|
+
<h2>Your website does not have any languages yet.</h2>
|
|
577
|
+
<p>You need at least one language to work with. Please create one below.</p>
|
|
578
|
+
alchemy/node: |
|
|
579
|
+
<h2>You do not have any menus yet.</h2>
|
|
580
|
+
<p>With menus your users can navigate your website. You can have multiple menus on your website. How many depends on your website template.</p>
|
|
581
|
+
<p>Please chose which menu you want to create first.</p>
|
|
582
|
+
alchemy/page: |
|
|
583
|
+
<h2>This language has no homepage yet</h2>
|
|
584
|
+
<p>Please create one below.</p>
|
|
585
|
+
alchemy/picture: |
|
|
586
|
+
<h2>You don't have any images in your archive yet.</h2>
|
|
587
|
+
<p>Use the button below or drag images into this window to upload your first image.</p>
|
|
588
|
+
alchemy/site: |
|
|
589
|
+
<h2>You do not have any websites yet.</h2>
|
|
590
|
+
<p>In order to serve requests you need to create a website first.</p>
|
|
591
|
+
<p>This form has been filled with sensible defaults that work in most cases.</p>
|
|
592
|
+
layoutpages: |
|
|
593
|
+
<h2>This language has no global pages yet</h2>
|
|
594
|
+
<p>Global pages hold content that is shared across all pages, like headers or footers.</p>
|
|
561
595
|
no_search_results: "Your search did not return any results."
|
|
562
596
|
no_unpublished_changes: "No unpublished changes"
|
|
563
597
|
"not a valid image": "This is not an valid image."
|
|
@@ -575,7 +609,7 @@ en:
|
|
|
575
609
|
"true": "Page is being edited at the moment."
|
|
576
610
|
"false": ""
|
|
577
611
|
restricted:
|
|
578
|
-
"true": "Page is only accessible by
|
|
612
|
+
"true": "Page is only accessible by users with one of its roles."
|
|
579
613
|
"false": "Page is accessible by all visitors."
|
|
580
614
|
scheduled:
|
|
581
615
|
public_on: "Page is visible from %{public_on}"
|
|
@@ -685,11 +719,11 @@ en:
|
|
|
685
719
|
unlocked_page: "Unlocked page %{name}."
|
|
686
720
|
uploader:
|
|
687
721
|
alchemy/attachment:
|
|
688
|
-
button_label: Upload
|
|
722
|
+
button_label: Upload files
|
|
689
723
|
upload_success: "File %{name} successfully uploaded"
|
|
690
724
|
upload_failure: "File %{name} upload error: %{error}"
|
|
691
725
|
alchemy/picture:
|
|
692
|
-
button_label: Upload
|
|
726
|
+
button_label: Upload images
|
|
693
727
|
upload_success: "Picture %{name} uploaded successfully"
|
|
694
728
|
upload_failure: "Error while uploading %{name}: %{error}"
|
|
695
729
|
error: "Could not upload file! %{error}"
|
|
@@ -711,7 +745,7 @@ en:
|
|
|
711
745
|
"stay logged in": "Stay Logged In"
|
|
712
746
|
"or to completely": "or completely"
|
|
713
747
|
"Are you sure?": "Are you sure?"
|
|
714
|
-
"Create": "Create"
|
|
748
|
+
"Create": "Create %{resource_name}"
|
|
715
749
|
"Edit": "Edit"
|
|
716
750
|
"Delete": "Delete"
|
|
717
751
|
"Successfully created": "Successfully created"
|
|
@@ -764,6 +798,8 @@ en:
|
|
|
764
798
|
|
|
765
799
|
# Translations for error messages.
|
|
766
800
|
errors:
|
|
801
|
+
messages:
|
|
802
|
+
invalid_url_scheme: "uses the not allowed scheme %{scheme}:"
|
|
767
803
|
attributes:
|
|
768
804
|
public_until:
|
|
769
805
|
must_be_after_public_on: "must be after visible from date"
|
|
@@ -851,6 +887,9 @@ en:
|
|
|
851
887
|
conflicting_wildcard_param_key: 'has a conflicting wildcard param "%{param}" already used by the "%{conflicting_layout}" page layout'
|
|
852
888
|
descendants:
|
|
853
889
|
still_attached_to_nodes: "The following descendant pages are still attached to menu nodes: %{page_names}. Please remove them first."
|
|
890
|
+
permitted_roles:
|
|
891
|
+
blank: "needs at least one role a restricted page is accessible for"
|
|
892
|
+
inclusion: "contains roles that are not configured: %{roles}"
|
|
854
893
|
alchemy/element:
|
|
855
894
|
attributes:
|
|
856
895
|
page_version_id:
|
|
@@ -968,6 +1007,7 @@ en:
|
|
|
968
1007
|
public_on: Visible from
|
|
969
1008
|
public_until: Visible until
|
|
970
1009
|
restricted: "restricted"
|
|
1010
|
+
permitted_roles: "except for users with one of these roles"
|
|
971
1011
|
searchable: "show in search"
|
|
972
1012
|
robot_follow: "robot may follow links"
|
|
973
1013
|
robot_index: "allow robot to index"
|
|
@@ -982,6 +1022,7 @@ en:
|
|
|
982
1022
|
image_file_height: "Height"
|
|
983
1023
|
image_file_width: "Width"
|
|
984
1024
|
image_file_dimensions: "Dimensions"
|
|
1025
|
+
image_file_format: "File type"
|
|
985
1026
|
image_file_size: "Filesize"
|
|
986
1027
|
name: "Name"
|
|
987
1028
|
tag_list: Tags
|
data/config/routes.rb
CHANGED
|
@@ -5,12 +5,13 @@ require "alchemy/routing_constraints"
|
|
|
5
5
|
Alchemy::Engine.routes.draw do
|
|
6
6
|
root to: "pages#index"
|
|
7
7
|
|
|
8
|
-
get "/sitemap.xml", to: "
|
|
8
|
+
get "/sitemap.xml", to: "sitemap#show", format: "xml"
|
|
9
9
|
|
|
10
10
|
scope Alchemy.admin_path, constraints: Alchemy.admin_constraints do
|
|
11
11
|
get "/", to: redirect("#{Alchemy.admin_path}/dashboard"), as: :admin
|
|
12
12
|
get "/dashboard", to: "admin/dashboard#index", as: :admin_dashboard
|
|
13
13
|
get "/dashboard/info", to: "admin/dashboard#info", as: :dashboard_info
|
|
14
|
+
get "/dashboard/license", to: "admin/dashboard#license", as: :dashboard_license
|
|
14
15
|
get "/dashboard/widgets/:id", to: "admin/dashboard/widgets#show", as: :admin_dashboard_widget
|
|
15
16
|
get "/help", to: "admin/dashboard#help", as: :help
|
|
16
17
|
get "/update_check" => "admin/update_checks#show", :as => :update_check
|
|
@@ -25,7 +26,6 @@ Alchemy::Engine.routes.draw do
|
|
|
25
26
|
collection do
|
|
26
27
|
post :flush
|
|
27
28
|
post :copy_language_tree
|
|
28
|
-
get :create_language
|
|
29
29
|
get :link
|
|
30
30
|
get :tree
|
|
31
31
|
end
|
|
@@ -84,7 +84,7 @@ Alchemy::Engine.routes.draw do
|
|
|
84
84
|
resources :legacy_page_urls
|
|
85
85
|
resources :languages do
|
|
86
86
|
collection do
|
|
87
|
-
|
|
87
|
+
post :switch
|
|
88
88
|
end
|
|
89
89
|
end
|
|
90
90
|
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddPermittedRolesToAlchemyPages < ActiveRecord::Migration[7.2]
|
|
4
|
+
# Only SQLite needs this: it has no real ALTER TABLE, so the remove_column in
|
|
5
|
+
# down rebuilds the table via DROP TABLE, whose implicit DELETE cascades into
|
|
6
|
+
# alchemy_page_versions, alchemy_elements and alchemy_ingredients. The
|
|
7
|
+
# adapter's PRAGMA foreign_keys = OFF guard against that is a silent no-op
|
|
8
|
+
# inside a transaction.
|
|
9
|
+
disable_ddl_transaction! if connection.adapter_name.match?(/sqlite/i)
|
|
10
|
+
|
|
11
|
+
def up
|
|
12
|
+
# Adding a nullable column is a plain ALTER TABLE ADD COLUMN, so up can keep
|
|
13
|
+
# the atomicity the declaration above gives up for both directions.
|
|
14
|
+
connection.transaction do
|
|
15
|
+
add_column :alchemy_pages, :permitted_roles, :text, if_not_exists: true
|
|
16
|
+
execute(%(UPDATE #{quote_table_name("alchemy_pages")} SET permitted_roles = '["member"]' WHERE permitted_roles IS NULL))
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def down
|
|
21
|
+
remove_column :alchemy_pages, :permitted_roles, if_exists: true
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class AddCachedTagListToTaggables < ActiveRecord::Migration[7.2]
|
|
4
|
+
# Only SQLite needs this: it has no real ALTER TABLE, so the remove_column
|
|
5
|
+
# calls in down rebuild the tables via DROP TABLE, whose implicit DELETE
|
|
6
|
+
# cascades into every child row, emptying alchemy_ingredients among others.
|
|
7
|
+
# The adapter's PRAGMA foreign_keys = OFF guard against that is a silent
|
|
8
|
+
# no-op inside a transaction.
|
|
9
|
+
disable_ddl_transaction! if connection.adapter_name.match?(/sqlite/i)
|
|
10
|
+
|
|
11
|
+
TAGGABLE_MODELS = %w[
|
|
12
|
+
Alchemy::Element
|
|
13
|
+
Alchemy::Page
|
|
14
|
+
Alchemy::Picture
|
|
15
|
+
Alchemy::Attachment
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
def up
|
|
19
|
+
# Adding nullable columns is a plain ALTER TABLE ADD COLUMN, so up can keep
|
|
20
|
+
# the atomicity the declaration above gives up for both directions.
|
|
21
|
+
connection.transaction do
|
|
22
|
+
TAGGABLE_MODELS.each do |model_name|
|
|
23
|
+
add_column model_name.constantize.table_name, :cached_tag_list, :text, if_not_exists: true
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
say_with_time "Backfilling cached_tag_list from existing taggings" do
|
|
27
|
+
backfill
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def down
|
|
33
|
+
TAGGABLE_MODELS.each do |model_name|
|
|
34
|
+
remove_column model_name.constantize.table_name, :cached_tag_list, if_exists: true
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
|
|
40
|
+
def backfill
|
|
41
|
+
TAGGABLE_MODELS.each do |model_name|
|
|
42
|
+
table = model_name.constantize.table_name
|
|
43
|
+
# Throwaway model so writing the raw JSON bypasses the real model's
|
|
44
|
+
# serializer and picks up the freshly added column without touching it.
|
|
45
|
+
klass = Class.new(ActiveRecord::Base) { self.table_name = table }
|
|
46
|
+
|
|
47
|
+
Gutentag::Tagging
|
|
48
|
+
.where(taggable_type: model_name)
|
|
49
|
+
.joins(:tag)
|
|
50
|
+
.pluck(:taggable_id, "gutentag_tags.name")
|
|
51
|
+
.group_by(&:first)
|
|
52
|
+
.each do |taggable_id, rows|
|
|
53
|
+
klass.where(id: taggable_id).update_all(cached_tag_list: rows.map(&:last).uniq.to_json)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
end
|
|
@@ -8,7 +8,9 @@ require "alchemy/configuration/collection_option"
|
|
|
8
8
|
require "alchemy/configuration/configuration_option"
|
|
9
9
|
require "alchemy/configuration/class_option"
|
|
10
10
|
require "alchemy/configuration/integer_option"
|
|
11
|
+
require "alchemy/configuration/number_option"
|
|
11
12
|
require "alchemy/configuration/pathname_option"
|
|
13
|
+
require "alchemy/configuration/proc_option"
|
|
12
14
|
require "alchemy/configuration/regexp_option"
|
|
13
15
|
require "alchemy/configuration/string_option"
|
|
14
16
|
require "alchemy/configuration/symbol_option"
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Alchemy
|
|
4
|
+
module Configurations
|
|
5
|
+
# === Admin Component Registry
|
|
6
|
+
#
|
|
7
|
+
# Overridable admin ViewComponents, keyed by role. Extensions can swap a
|
|
8
|
+
# component for their own by assigning a different class name in an
|
|
9
|
+
# initializer:
|
|
10
|
+
#
|
|
11
|
+
# Alchemy.config.admin_components.element_publish_button =
|
|
12
|
+
# "MyEngine::Admin::CustomPublishButton"
|
|
13
|
+
#
|
|
14
|
+
# Core renders these through the registry instead of referencing the class
|
|
15
|
+
# directly, so the swap takes effect everywhere the component is used.
|
|
16
|
+
class AdminComponents < Alchemy::Configuration
|
|
17
|
+
# The per-element publish control rendered in the element toolbar.
|
|
18
|
+
option :element_publish_button, :class, default: "Alchemy::Admin::PublishElementButton"
|
|
19
|
+
|
|
20
|
+
# The page publication/visibility fields rendered in the page settings form.
|
|
21
|
+
option :page_publication_fields, :class, default: "Alchemy::Admin::PagePublicationFields"
|
|
22
|
+
|
|
23
|
+
# The visibility state badges rendered in the element header.
|
|
24
|
+
option :element_status_indicators, :class, default: "Alchemy::Admin::ElementStatusIndicators"
|
|
25
|
+
|
|
26
|
+
# The status badges rendered for a page in the page listing and sitemap.
|
|
27
|
+
option :page_status_indicators, :class, default: "Alchemy::Admin::PageStatusIndicators"
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -3,9 +3,15 @@
|
|
|
3
3
|
module Alchemy
|
|
4
4
|
module Configurations
|
|
5
5
|
class FormatMatchers < Alchemy::Configuration
|
|
6
|
+
# Naming the hierarchical schemes explicitly matters: a shape match like
|
|
7
|
+
# `[a-z]+://` also admits `javascript://%0aalert(1)`, which browsers
|
|
8
|
+
# execute. This is the link dialog's client side check only, the server
|
|
9
|
+
# side boundary is Alchemy.config.allowed_url_schemes.
|
|
10
|
+
LINK_URL = /^((mailto|tel):|(https?|ftp):\/\/|\/)/
|
|
11
|
+
|
|
6
12
|
option :email, :regexp, default: /\A[^@\s]+@([^@\s]+\.)+[^@\s]+\z/
|
|
7
13
|
option :url, :regexp, default: /\A[a-z0-9]+([-.]{1}[a-z0-9]+)*\.[a-z]{2,5}(:[0-9]{1,5})?(\/.*)?\z/ix
|
|
8
|
-
option :link_url, :regexp, default:
|
|
14
|
+
option :link_url, :regexp, default: LINK_URL
|
|
9
15
|
end
|
|
10
16
|
end
|
|
11
17
|
end
|