alchemy_cms 7.4.10 → 8.0.0.b
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.
Potentially problematic release.
This version of alchemy_cms might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/README.md +16 -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/dark-theme.css +1 -0
- data/app/assets/builds/alchemy/light-theme.css +1 -0
- data/app/assets/builds/alchemy/theme.css +1 -0
- 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 -0
- 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/builds/tinymce/skins/ui/alchemy-dark/content.min.css +1 -0
- data/app/assets/builds/tinymce/skins/ui/alchemy-dark/skin.min.css +1 -0
- data/app/assets/config/alchemy_manifest.js +0 -2
- data/app/assets/images/alchemy/element_icons/default.svg +1 -0
- data/app/assets/images/alchemy/icons-sprite.svg +1 -0
- data/app/components/alchemy/admin/element_select.rb +39 -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 +5 -2
- data/app/components/alchemy/ingredients/number_view.rb +18 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +10 -15
- data/app/controllers/alchemy/admin/clipboard_controller.rb +2 -6
- data/app/controllers/alchemy/admin/elements_controller.rb +3 -1
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +15 -15
- data/app/controllers/alchemy/admin/pictures_controller.rb +27 -37
- data/app/controllers/alchemy/admin/resources_controller.rb +16 -106
- 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 +26 -4
- data/app/controllers/concerns/alchemy/admin/resource_filter.rb +93 -0
- data/app/decorators/alchemy/element_editor.rb +5 -48
- data/app/decorators/alchemy/ingredient_editor.rb +3 -53
- data/app/helpers/alchemy/admin/attachments_helper.rb +5 -5
- data/app/helpers/alchemy/admin/base_helper.rb +14 -84
- 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 +1 -1
- data/{lib → app/helpers}/alchemy/resources_helper.rb +5 -45
- 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/auto_submit.js +20 -0
- data/app/javascript/alchemy_admin/components/datepicker.js +18 -7
- data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +8 -7
- data/app/javascript/alchemy_admin/components/element_editor.js +25 -15
- data/app/javascript/alchemy_admin/components/element_select.js +43 -0
- data/app/javascript/alchemy_admin/components/index.js +3 -0
- data/app/javascript/alchemy_admin/components/link_buttons.js +6 -2
- data/app/javascript/alchemy_admin/components/remote_select.js +9 -2
- data/app/javascript/alchemy_admin/components/tags_autocomplete.js +5 -1
- data/app/javascript/alchemy_admin/components/tinymce.js +93 -14
- 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/dialog.js +1 -1
- data/app/javascript/alchemy_admin/dirty.js +3 -2
- data/app/javascript/alchemy_admin/file_editors.js +1 -1
- data/app/javascript/alchemy_admin/i18n.js +15 -16
- data/app/javascript/alchemy_admin/image_loader.js +4 -2
- data/app/javascript/alchemy_admin/initializer.js +1 -49
- data/app/javascript/alchemy_admin/picture_editors.js +7 -4
- data/app/javascript/alchemy_admin/picture_selector.js +4 -4
- data/app/javascript/alchemy_admin/utils/ajax.js +51 -44
- data/app/javascript/alchemy_admin.js +3 -8
- data/app/jobs/alchemy/delete_picture_job.rb +12 -0
- 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 -44
- 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 +10 -27
- data/app/models/alchemy/element_definition.rb +190 -0
- data/app/models/alchemy/ingredient.rb +10 -43
- 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.rb +2 -21
- data/app/models/alchemy/message.rb +3 -7
- data/app/models/alchemy/node.rb +1 -1
- 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 +3 -11
- data/app/models/alchemy/page/page_natures.rb +20 -15
- data/app/models/alchemy/page/page_scopes.rb +1 -1
- data/app/models/alchemy/page.rb +12 -39
- data/app/models/alchemy/page_definition.rb +115 -0
- data/app/models/alchemy/picture.rb +71 -99
- 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 -5
- data/app/models/alchemy/site.rb +1 -21
- 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/storage_adapter/dragonfly/picture_url.rb +75 -0
- data/app/models/alchemy/{picture → storage_adapter/dragonfly}/preprocessor.rb +4 -4
- data/app/models/alchemy/storage_adapter/dragonfly.rb +205 -0
- data/app/models/alchemy/storage_adapter.rb +74 -0
- data/app/models/concerns/alchemy/picture_thumbnails.rb +19 -6
- data/app/models/concerns/alchemy/relatable_resource.rb +28 -0
- data/app/serializers/alchemy/element_serializer.rb +0 -1
- data/app/services/alchemy/dragonfly_to_image_processing.rb +100 -0
- data/app/stylesheets/alchemy/_custom-properties.scss +162 -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 +40 -68
- data/app/stylesheets/alchemy/_themes.scss +540 -0
- data/app/stylesheets/alchemy/_variables.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/archive.scss +45 -42
- data/app/stylesheets/alchemy/admin/attachments.scss +17 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/base.scss +20 -15
- data/app/stylesheets/alchemy/admin/buttons.scss +135 -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 +33 -16
- data/app/stylesheets/alchemy/admin/element-select.scss +11 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/elements.scss +239 -133
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/errors.scss +5 -5
- data/app/stylesheets/alchemy/admin/filters.scss +57 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flatpickr.scss +54 -76
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/form_fields.scss +10 -11
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/forms.scss +28 -21
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/frame.scss +20 -18
- data/app/stylesheets/alchemy/admin/hints.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/icons.scss +1 -1
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/image_library.scss +21 -61
- 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 +55 -59
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/node-select.scss +1 -10
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/nodes.scss +21 -18
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/notices.scss +20 -19
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/page-select.scss +18 -2
- data/app/stylesheets/alchemy/admin/pagination.scss +137 -0
- data/app/stylesheets/alchemy/admin/preview_window.scss +46 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/print.scss +1 -1
- data/app/stylesheets/alchemy/admin/resource_info.scss +148 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/search.scss +10 -7
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/selects.scss +85 -46
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/shoelace.scss +37 -68
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/sitemap.scss +39 -34
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tables.scss +9 -7
- data/app/stylesheets/alchemy/admin/tags.scss +143 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/toolbar.scss +6 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/typography.scss +3 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/upload.scss +7 -5
- data/app/stylesheets/alchemy/admin.scss +44 -0
- data/app/stylesheets/alchemy/dark-theme.scss +5 -0
- data/app/stylesheets/alchemy/light-theme.scss +6 -0
- data/app/stylesheets/alchemy/theme.scss +13 -0
- data/app/stylesheets/alchemy/welcome.scss +75 -0
- data/app/stylesheets/tinymce/skins/content/alchemy/content.scss +69 -0
- data/app/{assets/stylesheets/tinymce/skins/content/alchemy → stylesheets/tinymce/skins/content/alchemy-dark}/content.scss +12 -12
- data/app/stylesheets/tinymce/skins/ui/alchemy/content.scss +1 -0
- data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/skin.scss +158 -176
- data/app/stylesheets/tinymce/skins/ui/alchemy-dark/content.scss +1 -0
- data/app/stylesheets/tinymce/skins/ui/alchemy-dark/skin.scss +3784 -0
- data/app/views/alchemy/admin/attachments/_files_list.html.erb +22 -12
- data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +1 -1
- data/app/views/alchemy/admin/attachments/assign.js.erb +4 -3
- data/app/views/alchemy/admin/attachments/show.html.erb +55 -43
- 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/crop.html.erb +1 -1
- 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 +9 -9
- data/app/views/alchemy/admin/elements/_header.html.erb +5 -1
- 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 +0 -9
- 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/partials/_remote_search_form.html.erb +9 -12
- data/app/views/alchemy/admin/partials/_search_form.html.erb +4 -9
- data/app/views/alchemy/admin/pictures/_archive.html.erb +16 -29
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +3 -7
- 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/_infos.html.erb +21 -52
- data/app/views/alchemy/admin/pictures/_library_sidebar.html.erb +7 -0
- data/app/views/alchemy/admin/pictures/_picture.html.erb +14 -20
- data/app/views/alchemy/admin/pictures/_picture_to_assign.html.erb +20 -16
- data/app/views/alchemy/admin/pictures/_sorting_select.html.erb +13 -0
- data/app/views/alchemy/admin/pictures/_tag_list.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/edit_multiple.html.erb +1 -6
- data/app/views/alchemy/admin/pictures/index.html.erb +5 -19
- data/app/views/alchemy/admin/pictures/show.html.erb +10 -5
- data/app/views/alchemy/admin/resources/_applied_filters.html.erb +8 -0
- data/app/views/alchemy/admin/resources/_filter_bar.html.erb +5 -25
- 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/_resource_usage_info.html.erb +36 -0
- 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 +118 -58
- 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/base/error_notice.html.erb +1 -1
- data/app/views/alchemy/ingredients/_number_editor.html.erb +24 -0
- 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 +25 -24
- 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 +57 -21
- data/config/routes.rb +0 -2
- data/db/migrate/20250905140323_add_created_at_index_to_pictures_and_attachments.rb +14 -0
- 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 +223 -0
- data/lib/alchemy/configurations/page_cache.rb +19 -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/engine.rb +81 -24
- data/lib/alchemy/hints.rb +3 -7
- data/lib/alchemy/install/tasks.rb +0 -12
- data/lib/alchemy/on_page_layout.rb +2 -2
- data/lib/alchemy/propshaft/tinymce_asset.rb +15 -0
- data/lib/alchemy/seeder.rb +2 -2
- data/lib/alchemy/tasks/tidy.rb +18 -0
- 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 +1 -1
- data/lib/alchemy/test_support/factories/page_factory.rb +2 -3
- data/lib/alchemy/test_support/factories/picture_factory.rb +31 -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/relatable_resource_examples.rb +58 -0
- data/lib/alchemy/test_support/shared_ingredient_examples.rb +5 -5
- data/lib/alchemy/tinymce.rb +0 -1
- 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 +185 -172
- 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 +25 -23
- data/lib/generators/alchemy/install/templates/alchemy.rb.tt +200 -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/tidy.rake +6 -0
- data/lib/tasks/alchemy/upgrade.rake +12 -47
- data/vendor/assets/stylesheets/tinymce/skins/content/dark/content.min.css +1 -0
- data/vendor/assets/stylesheets/tinymce/skins/content/default/content.min.css +1 -0
- data/vendor/assets/stylesheets/tinymce/skins/ui/oxide/skin.min.css +1 -0
- data/vendor/assets/stylesheets/tinymce/skins/ui/oxide-dark/content.min.css +1 -0
- data/vendor/assets/stylesheets/tinymce/skins/ui/oxide-dark/skin.min.css +1 -0
- data/vendor/javascript/clipboard.min.js +1 -1
- data/vendor/javascript/cropperjs.min.js +1 -1
- data/vendor/javascript/handlebars.min.js +3 -3
- data/vendor/javascript/jquery.min.js +1 -1
- data/vendor/javascript/select2.min.js +3 -3
- data/vendor/javascript/shoelace.min.js +92 -76
- data/vendor/javascript/sortable.min.js +2 -2
- data/vendor/javascript/tinymce.min.js +1 -1
- data/vendor/javascript/ungap-custom-elements.min.js +2 -2
- metadata +223 -208
- data/CHANGELOG.md +0 -2041
- data/CODE_OF_CONDUCT.md +0 -13
- data/CONTRIBUTING.md +0 -73
- data/Gemfile +0 -71
- data/Rakefile +0 -102
- data/SECURITY.md +0 -13
- data/alchemy_cms.gemspec +0 -88
- 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 +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/attachments.scss +0 -40
- 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/preview_window.scss +0 -33
- data/app/assets/stylesheets/alchemy/admin/resource_info.scss +0 -42
- data/app/assets/stylesheets/alchemy/admin/tags.scss +0 -158
- 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/helpers/alchemy/admin/elements_helper.rb +0 -25
- 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/models/alchemy/picture/url.rb +0 -54
- 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/bin/importmap +0 -4
- data/bin/rails +0 -9
- data/bin/rspec +0 -3
- data/bin/setup +0 -30
- data/bin/start +0 -17
- data/bun.lockb +0 -0
- data/bundles/shoelace.js +0 -12
- data/bundles/tinymce.js +0 -22
- data/config/initializers/assets.rb +0 -4
- data/eslint.config.js +0 -17
- 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/alchemy/upgrader/tasks/.keep +0 -0
- 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/rollup.config.mjs +0 -108
- 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/flash.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/list_filter.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
@@ -2,6 +2,9 @@ import "tinymce"
|
|
2
2
|
import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
|
3
3
|
import { currentLocale } from "alchemy_admin/i18n"
|
4
4
|
|
5
|
+
const DARK_THEME = "alchemy-dark"
|
6
|
+
const LIGHT_THEME = "alchemy"
|
7
|
+
|
5
8
|
class Tinymce extends AlchemyHTMLElement {
|
6
9
|
#min_height = null
|
7
10
|
|
@@ -32,6 +35,9 @@ class Tinymce extends AlchemyHTMLElement {
|
|
32
35
|
options
|
33
36
|
)
|
34
37
|
this.tinymceIntersectionObserver.observe(this)
|
38
|
+
|
39
|
+
// Set up theme change listener
|
40
|
+
this._setupThemeChangeListener()
|
35
41
|
}
|
36
42
|
|
37
43
|
/**
|
@@ -42,6 +48,9 @@ class Tinymce extends AlchemyHTMLElement {
|
|
42
48
|
this.tinymceIntersectionObserver.disconnect()
|
43
49
|
}
|
44
50
|
|
51
|
+
// Remove theme change listener
|
52
|
+
this._removeThemeChangeListener()
|
53
|
+
|
45
54
|
tinymce.get(this.editorId)?.remove(this.editorId)
|
46
55
|
}
|
47
56
|
|
@@ -66,22 +75,84 @@ class Tinymce extends AlchemyHTMLElement {
|
|
66
75
|
*/
|
67
76
|
_initTinymceEditor() {
|
68
77
|
tinymce.init(this.configuration).then((editors) => {
|
69
|
-
editors.forEach((editor) =>
|
70
|
-
// mark the editor container as visible
|
71
|
-
// without these correction the editor remains hidden
|
72
|
-
// after a drag and drop action
|
73
|
-
editor.show()
|
74
|
-
|
75
|
-
// remove the spinner after the Tinymce initialized
|
76
|
-
this.getElementsByTagName("alchemy-spinner")[0].remove()
|
77
|
-
|
78
|
-
// event listener to mark the editor as dirty
|
79
|
-
editor.on("dirty", () => this.elementEditor.setDirty())
|
80
|
-
editor.on("click", () => this.elementEditor.onClickElement(false))
|
81
|
-
})
|
78
|
+
editors.forEach((editor) => this._setupEditor(editor))
|
82
79
|
})
|
83
80
|
}
|
84
81
|
|
82
|
+
/**
|
83
|
+
* Setup editor after initialization
|
84
|
+
* @param {Object} editor - The TinyMCE editor instance
|
85
|
+
* @private
|
86
|
+
*/
|
87
|
+
_setupEditor(editor) {
|
88
|
+
// mark the editor container as visible
|
89
|
+
// without these correction the editor remains hidden
|
90
|
+
// after a drag and drop action
|
91
|
+
editor.show()
|
92
|
+
|
93
|
+
// remove the spinner after the Tinymce initialized (only on first init)
|
94
|
+
const spinner = this.getElementsByTagName("alchemy-spinner")[0]
|
95
|
+
if (spinner) {
|
96
|
+
spinner.remove()
|
97
|
+
}
|
98
|
+
|
99
|
+
// event listener to mark the editor as dirty
|
100
|
+
if (this.elementEditor) {
|
101
|
+
editor.on("dirty", (evt) => {
|
102
|
+
this.elementEditor.setDirty(evt.target.editorContainer)
|
103
|
+
})
|
104
|
+
editor.on("click", () => this.elementEditor.onClickElement(false))
|
105
|
+
}
|
106
|
+
}
|
107
|
+
|
108
|
+
/**
|
109
|
+
* Set up listener for OS theme changes
|
110
|
+
* @private
|
111
|
+
*/
|
112
|
+
_setupThemeChangeListener() {
|
113
|
+
this.darkModeMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
|
114
|
+
this.themeChangeHandler = (event) => this._handleThemeChange(event)
|
115
|
+
this.darkModeMediaQuery.addEventListener("change", this.themeChangeHandler)
|
116
|
+
}
|
117
|
+
|
118
|
+
/**
|
119
|
+
* Remove theme change listener
|
120
|
+
* @private
|
121
|
+
*/
|
122
|
+
_removeThemeChangeListener() {
|
123
|
+
if (this.darkModeMediaQuery && this.themeChangeHandler) {
|
124
|
+
this.darkModeMediaQuery.removeEventListener(
|
125
|
+
"change",
|
126
|
+
this.themeChangeHandler
|
127
|
+
)
|
128
|
+
}
|
129
|
+
}
|
130
|
+
|
131
|
+
/**
|
132
|
+
* Handle OS theme change and update TinyMCE skin
|
133
|
+
* @param {MediaQueryListEvent} event - The media query change event
|
134
|
+
* @private
|
135
|
+
*/
|
136
|
+
_handleThemeChange(event) {
|
137
|
+
const editor = tinymce.get(this.editorId)
|
138
|
+
if (editor) {
|
139
|
+
const skin = event.matches ? DARK_THEME : LIGHT_THEME
|
140
|
+
const content_css = event.matches ? DARK_THEME : LIGHT_THEME
|
141
|
+
|
142
|
+
// Update the skin by reinitializing the editor with new configuration
|
143
|
+
editor.remove()
|
144
|
+
tinymce
|
145
|
+
.init({
|
146
|
+
content_css,
|
147
|
+
...this.configuration,
|
148
|
+
skin
|
149
|
+
})
|
150
|
+
.then((editors) => {
|
151
|
+
editors.forEach((editor) => this._setupEditor(editor))
|
152
|
+
})
|
153
|
+
}
|
154
|
+
}
|
155
|
+
|
85
156
|
get configuration() {
|
86
157
|
const customConfig = {}
|
87
158
|
|
@@ -101,10 +172,12 @@ class Tinymce extends AlchemyHTMLElement {
|
|
101
172
|
})
|
102
173
|
|
103
174
|
const config = {
|
175
|
+
content_css: this.preferredTheme,
|
104
176
|
...Alchemy.TinymceDefaults,
|
105
177
|
...customConfig,
|
106
178
|
language: currentLocale(),
|
107
|
-
selector: `#${this.editorId}
|
179
|
+
selector: `#${this.editorId}`,
|
180
|
+
skin: this.preferredTheme
|
108
181
|
}
|
109
182
|
|
110
183
|
// Tinymce has a height of 400px by default
|
@@ -115,6 +188,12 @@ class Tinymce extends AlchemyHTMLElement {
|
|
115
188
|
return config
|
116
189
|
}
|
117
190
|
|
191
|
+
get preferredTheme() {
|
192
|
+
return window.matchMedia("(prefers-color-scheme: dark)").matches
|
193
|
+
? DARK_THEME
|
194
|
+
: LIGHT_THEME
|
195
|
+
}
|
196
|
+
|
118
197
|
get editorId() {
|
119
198
|
return this.editor.id
|
120
199
|
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import Spinner from "alchemy_admin/spinner"
|
2
|
+
|
3
|
+
class UpdateCheck extends HTMLElement {
|
4
|
+
async connectedCallback() {
|
5
|
+
const spinner = new Spinner("small")
|
6
|
+
spinner.spin(this)
|
7
|
+
|
8
|
+
try {
|
9
|
+
const response = await fetch(this.url)
|
10
|
+
const responseText = await response.text()
|
11
|
+
|
12
|
+
if (response.ok) {
|
13
|
+
this.showStatus(responseText)
|
14
|
+
} else {
|
15
|
+
this.showError(response)
|
16
|
+
}
|
17
|
+
} catch (error) {
|
18
|
+
this.showError(error)
|
19
|
+
} finally {
|
20
|
+
spinner.stop()
|
21
|
+
}
|
22
|
+
}
|
23
|
+
|
24
|
+
get url() {
|
25
|
+
return this.getAttribute("url")
|
26
|
+
}
|
27
|
+
|
28
|
+
showStatus(responseText) {
|
29
|
+
if (responseText == "true") {
|
30
|
+
this.querySelector(".update_available").classList.remove("hidden")
|
31
|
+
} else {
|
32
|
+
this.querySelector(".up_to_date").classList.remove("hidden")
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
showError(error) {
|
37
|
+
this.querySelector(".error").classList.remove("hidden")
|
38
|
+
console.error("[alchemy] Error fetching update status", error)
|
39
|
+
}
|
40
|
+
}
|
41
|
+
|
42
|
+
customElements.define("alchemy-update-check", UpdateCheck)
|
@@ -4,21 +4,28 @@ import { translate } from "alchemy_admin/i18n"
|
|
4
4
|
import { growl } from "alchemy_admin/growler"
|
5
5
|
|
6
6
|
export class FileUpload extends AlchemyHTMLElement {
|
7
|
+
constructor() {
|
8
|
+
super()
|
9
|
+
|
10
|
+
this.file = null
|
11
|
+
this.request = null
|
12
|
+
|
13
|
+
this.progressEventLoaded = 0
|
14
|
+
this.progressEventTotal = 0
|
15
|
+
this.className = "in-progress"
|
16
|
+
this.valid = true
|
17
|
+
this.value = 0
|
18
|
+
}
|
19
|
+
|
7
20
|
/**
|
21
|
+
* Initialize the component with file and request
|
8
22
|
* @param {File} file
|
9
23
|
* @param {XMLHttpRequest} request
|
10
24
|
*/
|
11
|
-
|
12
|
-
super({})
|
13
|
-
|
25
|
+
initialize(file, request) {
|
14
26
|
this.file = file
|
15
27
|
this.request = request
|
16
|
-
|
17
|
-
this.progressEventLoaded = 0
|
18
28
|
this.progressEventTotal = file ? file.size : 0
|
19
|
-
this.className = "in-progress"
|
20
|
-
this.valid = true
|
21
|
-
this.value = 0
|
22
29
|
|
23
30
|
this._validateFile()
|
24
31
|
this._addRequestEventListener()
|
@@ -6,19 +6,25 @@ import { translate } from "alchemy_admin/i18n"
|
|
6
6
|
export class Progress extends AlchemyHTMLElement {
|
7
7
|
#visible = false
|
8
8
|
|
9
|
-
|
10
|
-
* @param {FileUpload[]} fileUploads
|
11
|
-
*/
|
12
|
-
constructor(fileUploads = []) {
|
9
|
+
constructor() {
|
13
10
|
super()
|
14
11
|
this.buttonLabel = translate("Cancel all uploads")
|
15
|
-
this.fileUploads =
|
16
|
-
this.fileCount =
|
12
|
+
this.fileUploads = []
|
13
|
+
this.fileCount = 0
|
17
14
|
this.className = "in-progress"
|
18
15
|
this.visible = true
|
19
16
|
this.handleFileChange = () => this._updateView()
|
20
17
|
}
|
21
18
|
|
19
|
+
/**
|
20
|
+
* Initialize the component with file uploads
|
21
|
+
* @param {FileUpload[]} fileUploads
|
22
|
+
*/
|
23
|
+
initialize(fileUploads = []) {
|
24
|
+
this.fileUploads = fileUploads
|
25
|
+
this.fileCount = fileUploads.length
|
26
|
+
}
|
27
|
+
|
22
28
|
/**
|
23
29
|
* append file progress - components for each file
|
24
30
|
*/
|
@@ -68,7 +68,8 @@ export class Uploader extends AlchemyHTMLElement {
|
|
68
68
|
|
69
69
|
const fileUploads = files.map((file) => {
|
70
70
|
const request = new XMLHttpRequest()
|
71
|
-
const fileUpload = new FileUpload(
|
71
|
+
const fileUpload = new FileUpload()
|
72
|
+
fileUpload.initialize(file, request)
|
72
73
|
|
73
74
|
if (Alchemy.uploader_defaults.upload_limit - 1 < fileUploadCount) {
|
74
75
|
fileUpload.valid = false
|
@@ -110,7 +111,8 @@ export class Uploader extends AlchemyHTMLElement {
|
|
110
111
|
this.uploadProgress.cancel()
|
111
112
|
document.body.removeChild(this.uploadProgress)
|
112
113
|
}
|
113
|
-
this.uploadProgress = new Progress(
|
114
|
+
this.uploadProgress = new Progress()
|
115
|
+
this.uploadProgress.initialize(fileUploads)
|
114
116
|
this.uploadProgress.onComplete = (status) => {
|
115
117
|
this.dispatchCustomEvent(`upload.${status}`)
|
116
118
|
}
|
@@ -1,20 +1,19 @@
|
|
1
|
-
import { growl } from "alchemy_admin/growler"
|
2
|
-
import pleaseWaitOverlay from "alchemy_admin/please_wait_overlay"
|
3
1
|
import { createHtmlElement } from "alchemy_admin/utils/dom_helpers"
|
4
2
|
import { translate } from "alchemy_admin/i18n"
|
5
3
|
|
6
|
-
const
|
4
|
+
const getDefaults = () => ({
|
5
|
+
// The default size of the dialog
|
7
6
|
size: "300x100",
|
8
7
|
title: translate("Please confirm"),
|
9
8
|
ok_label: translate("Yes"),
|
10
9
|
cancel_label: translate("No"),
|
11
10
|
on_ok() {}
|
12
|
-
}
|
11
|
+
})
|
13
12
|
|
14
13
|
class ConfirmDialog {
|
15
14
|
constructor(message, options = {}) {
|
16
15
|
this.message = message
|
17
|
-
this.options = { ...
|
16
|
+
this.options = { ...getDefaults(), ...options }
|
18
17
|
this.#build()
|
19
18
|
this.#bindEvents()
|
20
19
|
}
|
@@ -44,6 +43,7 @@ class ConfirmDialog {
|
|
44
43
|
#bindEvents() {
|
45
44
|
this.cancelButton.addEventListener("click", (evt) => {
|
46
45
|
evt.preventDefault()
|
46
|
+
this.options.on_cancel()
|
47
47
|
this.dialog.hide()
|
48
48
|
})
|
49
49
|
this.okButton.addEventListener("click", (evt) => {
|
@@ -54,6 +54,7 @@ class ConfirmDialog {
|
|
54
54
|
// Prevent the dialog from closing when the user clicks on the overlay
|
55
55
|
this.dialog.addEventListener("sl-request-close", (event) => {
|
56
56
|
if (event.detail.source === "overlay") {
|
57
|
+
this.options.on_cancel()
|
57
58
|
event.preventDefault()
|
58
59
|
}
|
59
60
|
})
|
@@ -72,60 +73,29 @@ class ConfirmDialog {
|
|
72
73
|
}
|
73
74
|
}
|
74
75
|
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
//
|
76
|
+
/* Opens a confirm dialog
|
77
|
+
*
|
78
|
+
* @param {string} message - The message that will be displayed to the user
|
79
|
+
* @param {Object} [options={}] - Configuration options for the dialog
|
80
|
+
* @param {string} [options.title="Please confirm"] - The title of the overlay window
|
81
|
+
* @param {string} [options.cancel_label="No"] - The label of the cancel button
|
82
|
+
* @param {string} [options.ok_label="Yes"] - The label of the ok button
|
83
|
+
*
|
84
|
+
* @returns {Promise<void>} A promise that resolves to true when the OK button is clicked and
|
85
|
+
* resolves to false when the cancel button is clicked. Works as confirm dialog replacement
|
86
|
+
* for Turbo.confirm.
|
87
|
+
*/
|
88
88
|
export function openConfirmDialog(message, options = {}) {
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
}
|
93
|
-
|
94
|
-
// Opens a confirm to delete dialog
|
95
|
-
//
|
96
|
-
// Arguments:
|
97
|
-
//
|
98
|
-
// url - The url to the server delete action. Uses DELETE as HTTP method. (String)
|
99
|
-
// opts - An options object (Object)
|
100
|
-
//
|
101
|
-
// Options:
|
102
|
-
//
|
103
|
-
// title: '' - The title of the confirmation window (String)
|
104
|
-
// message: '' - The message that will be displayed to the user (String)
|
105
|
-
// ok_label: '' - The label for the ok button (String)
|
106
|
-
// cancel_label: '' - The label for the cancel button (String)
|
107
|
-
//
|
108
|
-
export function confirmToDeleteDialog(url, opts = {}) {
|
109
|
-
return new Promise((resolve, reject) => {
|
110
|
-
const options = {
|
89
|
+
return new Promise((resolve) => {
|
90
|
+
const dialog = new ConfirmDialog(message, {
|
91
|
+
...options,
|
111
92
|
on_ok() {
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
error(xhr, _status, error) {
|
117
|
-
const type = xhr.status === 403 ? "warning" : "error"
|
118
|
-
growl(xhr.responseText || error, type)
|
119
|
-
reject(error)
|
120
|
-
},
|
121
|
-
complete(response) {
|
122
|
-
pleaseWaitOverlay(false)
|
123
|
-
resolve(response)
|
124
|
-
}
|
125
|
-
})
|
93
|
+
resolve(true)
|
94
|
+
},
|
95
|
+
on_cancel() {
|
96
|
+
resolve(false)
|
126
97
|
}
|
127
|
-
}
|
128
|
-
|
129
|
-
openConfirmDialog(opts.message, { ...options, ...opts })
|
98
|
+
})
|
99
|
+
dialog.open()
|
130
100
|
})
|
131
101
|
}
|
@@ -233,7 +233,7 @@ export class Dialog {
|
|
233
233
|
this.dialog_header = $('<div class="alchemy-dialog-header" />')
|
234
234
|
this.dialog_title = $('<div class="alchemy-dialog-title" />')
|
235
235
|
this.close_button = $(
|
236
|
-
'<
|
236
|
+
'<button class="alchemy-dialog-close"><alchemy-icon name="close"></alchemy-icon></button>'
|
237
237
|
)
|
238
238
|
this.dialog_title.text(this.options.title)
|
239
239
|
this.dialog_header.append(this.dialog_title)
|
@@ -27,8 +27,9 @@ function checkPageDirtyness(element) {
|
|
27
27
|
openConfirmDialog(translate("page_dirty_notice"), {
|
28
28
|
title: translate("warning"),
|
29
29
|
ok_label: translate("ok"),
|
30
|
-
cancel_label: translate("cancel")
|
31
|
-
|
30
|
+
cancel_label: translate("cancel")
|
31
|
+
}).then((proceed) => {
|
32
|
+
if (proceed) {
|
32
33
|
window.onbeforeunload = void 0
|
33
34
|
callback()
|
34
35
|
}
|
@@ -16,7 +16,7 @@ class FileEditor {
|
|
16
16
|
this.fileIcon.innerHTML = ""
|
17
17
|
this.fileName.innerHTML = ""
|
18
18
|
this.deleteLink.classList.add("hidden")
|
19
|
-
this.container.closest("alchemy-element-editor").setDirty()
|
19
|
+
this.container.closest("alchemy-element-editor").setDirty(this.formField)
|
20
20
|
return false
|
21
21
|
}
|
22
22
|
}
|
@@ -1,20 +1,5 @@
|
|
1
|
-
import { en } from "alchemy_admin/locales/en"
|
2
|
-
|
3
|
-
Alchemy.translations = Object.assign(Alchemy.translations || {}, { en })
|
4
|
-
|
5
1
|
const KEY_SEPARATOR = /\./
|
6
2
|
|
7
|
-
function getTranslations() {
|
8
|
-
const locale = currentLocale()
|
9
|
-
const translations = Alchemy.translations && Alchemy.translations[locale]
|
10
|
-
|
11
|
-
if (translations) {
|
12
|
-
return translations
|
13
|
-
}
|
14
|
-
console.warn(`Translations for locale ${locale} not found!`)
|
15
|
-
return {}
|
16
|
-
}
|
17
|
-
|
18
3
|
function nestedTranslation(translations, key) {
|
19
4
|
const keys = key.split(KEY_SEPARATOR)
|
20
5
|
const group = translations[keys[0]]
|
@@ -25,7 +10,13 @@ function nestedTranslation(translations, key) {
|
|
25
10
|
}
|
26
11
|
|
27
12
|
function getTranslation(key) {
|
28
|
-
const
|
13
|
+
const locale = currentLocale()
|
14
|
+
const translations = Alchemy.translations
|
15
|
+
|
16
|
+
if (!translations) {
|
17
|
+
console.warn(`Translations for locale ${locale} not found!`)
|
18
|
+
return key
|
19
|
+
}
|
29
20
|
|
30
21
|
if (KEY_SEPARATOR.test(key)) {
|
31
22
|
return nestedTranslation(translations, key)
|
@@ -48,3 +39,11 @@ export function translate(key, replacement = undefined) {
|
|
48
39
|
}
|
49
40
|
return translation
|
50
41
|
}
|
42
|
+
|
43
|
+
export async function setupSelectLocale() {
|
44
|
+
const locale = currentLocale()
|
45
|
+
if (locale === "en") return
|
46
|
+
|
47
|
+
await import(`select2/${locale}.js`)
|
48
|
+
$.extend($.fn.select2.defaults, $.fn.select2.locales[locale])
|
49
|
+
}
|
@@ -1,6 +1,8 @@
|
|
1
1
|
// Shows spinner while loading images and
|
2
2
|
// fades the image after its been loaded
|
3
3
|
|
4
|
+
import Spinner from "alchemy_admin/spinner"
|
5
|
+
|
4
6
|
export default class ImageLoader {
|
5
7
|
static init(scope = document) {
|
6
8
|
if (typeof scope === "string") {
|
@@ -15,7 +17,7 @@ export default class ImageLoader {
|
|
15
17
|
constructor(image) {
|
16
18
|
this.image = image
|
17
19
|
this.parent = image.parentNode
|
18
|
-
this.spinner = new
|
20
|
+
this.spinner = new Spinner("small")
|
19
21
|
this.bind()
|
20
22
|
}
|
21
23
|
|
@@ -28,7 +30,7 @@ export default class ImageLoader {
|
|
28
30
|
if (!force && this.image.complete) return
|
29
31
|
|
30
32
|
this.image.classList.add("loading")
|
31
|
-
this.spinner.spin(this.
|
33
|
+
this.spinner.spin(this.parent)
|
32
34
|
}
|
33
35
|
|
34
36
|
onLoaded() {
|
@@ -1,8 +1,3 @@
|
|
1
|
-
import {
|
2
|
-
confirmToDeleteDialog,
|
3
|
-
openConfirmDialog
|
4
|
-
} from "alchemy_admin/confirm_dialog"
|
5
|
-
|
6
1
|
import Hotkeys from "alchemy_admin/hotkeys"
|
7
2
|
import pleaseWaitOverlay from "alchemy_admin/please_wait_overlay"
|
8
3
|
|
@@ -26,44 +21,6 @@ function selectHandler(selectId, parameterName, forcedReload = false) {
|
|
26
21
|
})
|
27
22
|
}
|
28
23
|
|
29
|
-
// Watches elements for Alchemy Dialogs
|
30
|
-
//
|
31
|
-
// Links having a data-alchemy-confirm-delete
|
32
|
-
// and input/buttons having a data-alchemy-confirm attribute get watched.
|
33
|
-
//
|
34
|
-
// You can pass a scope so that only elements inside this scope are queried.
|
35
|
-
//
|
36
|
-
// The href attribute of the link is the url for the overlay window.
|
37
|
-
//
|
38
|
-
// See Dialog for further options you can add to the data attribute.
|
39
|
-
//
|
40
|
-
function watchForConfirmDialogs(scope) {
|
41
|
-
if (scope == null) {
|
42
|
-
scope = "#alchemy"
|
43
|
-
}
|
44
|
-
$(scope).on("click", "[data-alchemy-confirm-delete]", function (event) {
|
45
|
-
const $this = $(this)
|
46
|
-
const options = $this.data("alchemy-confirm-delete")
|
47
|
-
confirmToDeleteDialog($this.attr("href"), options)
|
48
|
-
event.preventDefault()
|
49
|
-
})
|
50
|
-
$(scope).on("click", "[data-alchemy-confirm]", function (event) {
|
51
|
-
const options = $(this).data("alchemy-confirm")
|
52
|
-
openConfirmDialog(
|
53
|
-
options.message,
|
54
|
-
$.extend(options, {
|
55
|
-
ok_label: options.ok_label,
|
56
|
-
cancel_label: options.cancel_label,
|
57
|
-
on_ok: () => {
|
58
|
-
pleaseWaitOverlay()
|
59
|
-
this.form.submit()
|
60
|
-
}
|
61
|
-
})
|
62
|
-
)
|
63
|
-
event.preventDefault()
|
64
|
-
})
|
65
|
-
}
|
66
|
-
|
67
24
|
export default function Initializer() {
|
68
25
|
// We obviously have javascript enabled.
|
69
26
|
$("html").removeClass("no-js")
|
@@ -71,13 +28,8 @@ export default function Initializer() {
|
|
71
28
|
// Initialize hotkeys.
|
72
29
|
Hotkeys()
|
73
30
|
|
74
|
-
// Watch for click on confirm dialog links.
|
75
|
-
watchForConfirmDialogs()
|
76
|
-
|
77
31
|
// Add observer for please wait overlay.
|
78
|
-
$(".please_wait")
|
79
|
-
.not("*[data-alchemy-confirm]")
|
80
|
-
.on("click", Alchemy.pleaseWaitOverlay)
|
32
|
+
$(".please_wait").on("click", pleaseWaitOverlay)
|
81
33
|
|
82
34
|
// Hack for enabling tab focus for <a>'s styled as button.
|
83
35
|
$("a.button").attr({ tabindex: 0 })
|
@@ -5,7 +5,7 @@ import { growl } from "alchemy_admin/growler"
|
|
5
5
|
import ImageLoader from "alchemy_admin/image_loader"
|
6
6
|
|
7
7
|
const UPDATE_DELAY = 125
|
8
|
-
const IMAGE_PLACEHOLDER = '<alchemy-icon name="image"></alchemy-icon>'
|
8
|
+
const IMAGE_PLACEHOLDER = '<alchemy-icon name="image" size="xl"></alchemy-icon>'
|
9
9
|
const THUMBNAIL_SIZE = "160x120"
|
10
10
|
|
11
11
|
export class PictureEditor {
|
@@ -75,6 +75,7 @@ export class PictureEditor {
|
|
75
75
|
this.image.src = data.url
|
76
76
|
this.image.alt = data.alt
|
77
77
|
this.image.title = data.title
|
78
|
+
this.setElementDirty()
|
78
79
|
})
|
79
80
|
.catch((error) => {
|
80
81
|
console.error(error.message || error)
|
@@ -83,8 +84,6 @@ export class PictureEditor {
|
|
83
84
|
}
|
84
85
|
|
85
86
|
ensureImage() {
|
86
|
-
if (this.image) return
|
87
|
-
|
88
87
|
const img = new Image()
|
89
88
|
this.thumbnailBackground.replaceChildren(img)
|
90
89
|
this.image = img
|
@@ -96,7 +95,11 @@ export class PictureEditor {
|
|
96
95
|
this.pictureIdField.value = ""
|
97
96
|
this.image = null
|
98
97
|
this.cropLink.classList.add("disabled")
|
99
|
-
this.
|
98
|
+
this.setElementDirty()
|
99
|
+
}
|
100
|
+
|
101
|
+
setElementDirty() {
|
102
|
+
this.container.closest(".element-editor").setDirty(this.container)
|
100
103
|
}
|
101
104
|
|
102
105
|
updateCropLink() {
|
@@ -15,13 +15,13 @@ function checkedInputs() {
|
|
15
15
|
}
|
16
16
|
|
17
17
|
function editMultiplePicturesUrl(href) {
|
18
|
-
const
|
18
|
+
const url = new URL(href)
|
19
|
+
|
19
20
|
checkedInputs().forEach((entry) =>
|
20
|
-
|
21
|
+
url.searchParams.append(entry.name, entry.value)
|
21
22
|
)
|
22
|
-
const url = href + "?" + searchParameters.toString()
|
23
23
|
|
24
|
-
return url
|
24
|
+
return url.toString()
|
25
25
|
}
|
26
26
|
|
27
27
|
/**
|