alchemy_cms 7.4.16 → 8.0.0.a
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +87 -77
- data/Gemfile +13 -7
- data/README.md +13 -5
- data/alchemy_cms.gemspec +14 -5
- data/app/assets/builds/alchemy/admin/page-select.css +1 -1
- data/app/assets/builds/alchemy/admin/print.css +1 -1
- data/app/assets/builds/alchemy/admin.css +2 -2
- data/app/assets/builds/alchemy/custom-properties.css +1 -1
- data/app/assets/builds/alchemy/welcome.css +1 -1
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css +1 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy/content.min.css +1 -0
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css +1 -1
- data/app/assets/config/alchemy_manifest.js +0 -2
- data/app/assets/images/alchemy/icons-sprite.svg +1 -0
- data/app/components/alchemy/admin/resource/applied_filter.rb +29 -0
- data/app/components/alchemy/admin/resource/checkbox_filter.rb +36 -0
- data/app/components/alchemy/admin/resource/datepicker_filter.rb +42 -0
- data/app/components/alchemy/admin/resource/select_filter.rb +43 -0
- data/app/components/alchemy/admin/toolbar_button.rb +6 -3
- data/app/components/alchemy/ingredients/datetime_view.rb +2 -4
- data/app/components/alchemy/ingredients/number_view.rb +18 -0
- data/app/controllers/alchemy/admin/attachments_controller.rb +8 -15
- data/app/controllers/alchemy/admin/clipboard_controller.rb +2 -6
- data/app/controllers/alchemy/admin/elements_controller.rb +1 -1
- data/app/controllers/alchemy/admin/languages_controller.rb +1 -4
- data/app/controllers/alchemy/admin/pages_controller.rb +15 -17
- data/app/controllers/alchemy/admin/pictures_controller.rb +9 -5
- data/app/controllers/alchemy/admin/resources_controller.rb +16 -106
- data/app/controllers/alchemy/api/nodes_controller.rb +1 -1
- data/app/controllers/alchemy/api/pages_controller.rb +0 -2
- data/app/controllers/alchemy/attachments_controller.rb +43 -14
- data/app/controllers/alchemy/messages_controller.rb +1 -1
- data/app/controllers/alchemy/pages_controller.rb +7 -2
- data/app/controllers/concerns/alchemy/admin/resource_filter.rb +92 -0
- data/app/decorators/alchemy/element_editor.rb +5 -48
- data/app/decorators/alchemy/ingredient_editor.rb +4 -62
- data/app/helpers/alchemy/admin/base_helper.rb +14 -84
- data/app/helpers/alchemy/admin/elements_helper.rb +4 -4
- data/app/helpers/alchemy/admin/pages_helper.rb +1 -1
- data/app/helpers/alchemy/base_helper.rb +0 -30
- data/app/helpers/alchemy/elements_block_helper.rb +0 -14
- data/app/helpers/alchemy/pages_helper.rb +2 -2
- data/{lib → app/helpers}/alchemy/resources_helper.rb +6 -46
- data/app/javascript/alchemy_admin/components/action.js +2 -0
- data/app/javascript/alchemy_admin/components/alchemy_html_element.js +3 -3
- data/app/javascript/alchemy_admin/components/datepicker.js +10 -2
- data/app/javascript/alchemy_admin/components/element_editor/delete_element_button.js +7 -7
- data/app/javascript/alchemy_admin/components/element_editor.js +1 -1
- data/app/javascript/alchemy_admin/components/index.js +1 -3
- data/app/javascript/alchemy_admin/components/remote_select.js +4 -1
- data/app/javascript/alchemy_admin/components/tags_autocomplete.js +5 -1
- data/app/javascript/alchemy_admin/components/tinymce.js +4 -2
- data/app/javascript/alchemy_admin/components/update_check.js +42 -0
- data/app/javascript/alchemy_admin/components/uploader/file_upload.js +15 -8
- data/app/javascript/alchemy_admin/components/uploader/progress.js +12 -6
- data/app/javascript/alchemy_admin/components/uploader.js +4 -2
- data/app/javascript/alchemy_admin/confirm_dialog.js +27 -57
- data/app/javascript/alchemy_admin/dirty.js +3 -2
- data/app/javascript/alchemy_admin/i18n.js +15 -16
- data/app/javascript/alchemy_admin/initializer.js +1 -49
- data/app/javascript/alchemy_admin/link_dialog.js +1 -6
- data/app/javascript/alchemy_admin/utils/ajax.js +51 -44
- data/app/javascript/alchemy_admin.js +3 -8
- data/app/models/alchemy/admin/filters/base.rb +38 -0
- data/app/models/alchemy/admin/filters/checkbox.rb +24 -0
- data/app/models/alchemy/admin/filters/datepicker.rb +53 -0
- data/app/models/alchemy/admin/filters/select.rb +70 -0
- data/app/models/alchemy/admin/resource_name.rb +27 -0
- data/app/models/alchemy/attachment.rb +49 -37
- data/app/models/alchemy/base_record.rb +2 -0
- data/app/models/alchemy/element/definitions.rb +1 -1
- data/app/models/alchemy/element/element_ingredients.rb +6 -6
- data/app/models/alchemy/element/presenters.rb +3 -12
- data/app/models/alchemy/element.rb +9 -27
- data/app/models/alchemy/element_definition.rb +160 -0
- data/app/models/alchemy/ingredient.rb +11 -44
- data/app/models/alchemy/ingredient_definition.rb +134 -0
- data/app/models/alchemy/ingredient_validator.rb +7 -3
- data/app/models/alchemy/ingredients/number.rb +19 -0
- data/app/models/alchemy/language/code.rb +0 -1
- data/app/models/alchemy/language.rb +0 -14
- data/app/models/alchemy/message.rb +3 -7
- data/app/models/alchemy/node.rb +1 -22
- data/app/models/alchemy/page/{page_layouts.rb → definitions.rb} +12 -19
- data/app/models/alchemy/page/fixed_attributes.rb +1 -1
- data/app/models/alchemy/page/page_elements.rb +13 -14
- data/app/models/alchemy/page/page_naming.rb +0 -1
- data/app/models/alchemy/page/page_natures.rb +7 -7
- data/app/models/alchemy/page/page_scopes.rb +1 -1
- data/app/models/alchemy/page.rb +11 -33
- data/app/models/alchemy/page_definition.rb +115 -0
- data/app/models/alchemy/picture.rb +71 -88
- data/app/models/alchemy/picture_variant.rb +115 -5
- data/{lib → app/models}/alchemy/resource.rb +4 -18
- data/{lib → app/models}/alchemy/searchable_resource.rb +15 -0
- data/app/models/alchemy/site/layout.rb +5 -6
- data/app/models/alchemy/site.rb +0 -15
- data/app/models/alchemy/storage_adapter/active_storage/attachment_url.rb +41 -0
- data/app/models/alchemy/storage_adapter/active_storage/picture_url.rb +55 -0
- data/app/models/alchemy/storage_adapter/active_storage/preprocessor.rb +40 -0
- data/app/models/alchemy/storage_adapter/active_storage.rb +173 -0
- data/app/models/alchemy/{attachment/url.rb → storage_adapter/dragonfly/attachment_url.rb} +12 -12
- data/app/models/alchemy/{picture/url.rb → storage_adapter/dragonfly/picture_url.rb} +28 -12
- data/app/models/alchemy/{picture → storage_adapter/dragonfly}/preprocessor.rb +4 -4
- data/app/models/alchemy/storage_adapter/dragonfly.rb +183 -0
- data/app/models/alchemy/storage_adapter.rb +74 -0
- data/app/models/concerns/alchemy/picture_thumbnails.rb +19 -6
- data/app/serializers/alchemy/element_serializer.rb +0 -1
- data/app/serializers/alchemy/page_tree_serializer.rb +1 -17
- data/app/services/alchemy/dragonfly_to_image_processing.rb +100 -0
- data/app/stylesheets/alchemy/_defaults.scss +3 -0
- data/app/stylesheets/alchemy/_extends.scss +69 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/_mixins.scss +33 -49
- data/app/stylesheets/alchemy/_variables.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/archive.scss +20 -37
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/base.scss +16 -14
- data/app/stylesheets/alchemy/admin/buttons.scss +160 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/clipboard.scss +2 -2
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dashboard.scss +13 -16
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/dialogs.scss +23 -16
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/elements.scss +150 -105
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/errors.scss +5 -5
- data/app/stylesheets/alchemy/admin/filters.scss +58 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flatpickr.scss +53 -60
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/form_fields.scss +21 -7
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/forms.scss +31 -19
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/frame.scss +20 -16
- data/app/stylesheets/alchemy/admin/hints.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/icons.scss +10 -1
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/image_library.scss +10 -8
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/images.scss +1 -1
- data/app/stylesheets/alchemy/admin/labels.scss +5 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/lists.scss +3 -3
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/navigation.scss +61 -55
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/nodes.scss +21 -18
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/notices.scss +18 -18
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/page-select.scss +2 -2
- data/app/stylesheets/alchemy/admin/pagination.scss +144 -0
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/preview_window.scss +8 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/print.scss +1 -1
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/resource_info.scss +8 -5
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/search.scss +9 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/selects.scss +49 -37
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/shoelace.scss +5 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/sitemap.scss +38 -33
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tables.scss +6 -4
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/tags.scss +6 -4
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/toolbar.scss +12 -6
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/typography.scss +2 -2
- data/app/{assets/stylesheets → stylesheets}/alchemy/admin/upload.scss +7 -5
- data/app/stylesheets/alchemy/admin.scss +44 -0
- data/app/stylesheets/alchemy/custom-properties.css +244 -0
- data/app/stylesheets/alchemy/welcome.scss +75 -0
- data/app/{assets/stylesheets → stylesheets}/tinymce/skins/content/alchemy/content.scss +8 -9
- data/app/stylesheets/tinymce/skins/ui/alchemy/content.scss +1 -0
- data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/skin.scss +133 -136
- data/app/views/alchemy/admin/attachments/_files_list.html.erb +2 -2
- data/app/views/alchemy/admin/attachments/_overlay_file_list.html.erb +1 -1
- data/app/views/alchemy/admin/{elements/_clipboard_button.html.erb → clipboard/_button.html.erb} +3 -5
- data/app/views/alchemy/admin/clipboard/_update_nested_element_button.turbo_stream.erb +11 -0
- data/app/views/alchemy/admin/clipboard/clear.turbo_stream.erb +4 -0
- data/app/views/alchemy/admin/clipboard/index.html.erb +15 -13
- data/app/views/alchemy/admin/clipboard/insert.turbo_stream.erb +18 -0
- data/app/views/alchemy/admin/clipboard/remove.turbo_stream.erb +9 -0
- data/app/views/alchemy/admin/dashboard/info.html.erb +17 -31
- data/app/views/alchemy/admin/elements/_element.html.erb +4 -8
- data/app/views/alchemy/admin/elements/_form.html.erb +1 -1
- data/app/views/alchemy/admin/elements/_header.html.erb +1 -0
- data/app/views/alchemy/admin/elements/_toolbar.html.erb +4 -6
- data/app/views/alchemy/admin/elements/create.turbo_stream.erb +2 -1
- data/app/views/alchemy/admin/elements/index.html.erb +2 -2
- data/app/views/alchemy/admin/ingredients/_file_fields.html.erb +3 -16
- data/app/views/alchemy/admin/ingredients/_picture_fields.html.erb +1 -10
- data/app/views/alchemy/admin/languages/_form.html.erb +1 -1
- data/app/views/alchemy/admin/languages/_table.html.erb +1 -1
- data/app/views/alchemy/admin/languages/index.html.erb +5 -2
- data/app/views/alchemy/admin/layoutpages/index.html.erb +1 -12
- data/app/views/alchemy/admin/pages/_form.html.erb +2 -2
- data/app/views/alchemy/admin/pages/_page.html.erb +2 -3
- data/app/views/alchemy/admin/pages/_toolbar.html.erb +1 -15
- data/app/views/alchemy/admin/pages/index.html.erb +1 -1
- data/app/views/alchemy/admin/pages/info.html.erb +1 -1
- data/app/views/alchemy/admin/partials/_remote_search_form.html.erb +9 -12
- data/app/views/alchemy/admin/partials/_search_form.html.erb +4 -10
- data/app/views/alchemy/admin/pictures/_archive.html.erb +4 -7
- data/app/views/alchemy/admin/pictures/_archive_overlay.html.erb +2 -1
- data/app/views/alchemy/admin/pictures/_filter_and_size_bar.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/_form.html.erb +1 -1
- data/app/views/alchemy/admin/pictures/index.html.erb +2 -7
- data/app/views/alchemy/admin/resources/_applied_filters.html.erb +8 -0
- data/app/views/alchemy/admin/resources/_filter_bar.html.erb +11 -21
- data/app/views/alchemy/admin/resources/_pagination.html.erb +6 -0
- data/app/views/alchemy/admin/resources/_per_page_select.html.erb +4 -2
- data/app/views/alchemy/admin/resources/_resource_table.html.erb +1 -1
- data/app/views/alchemy/admin/resources/_table_header.html.erb +1 -15
- data/app/views/alchemy/admin/sites/index.html.erb +5 -1
- data/app/views/alchemy/admin/styleguide/index.html.erb +8 -0
- data/app/views/alchemy/admin/tags/index.html.erb +1 -1
- data/app/views/alchemy/admin/tinymce/_setup.html.erb +7 -7
- data/app/{javascript/alchemy_admin/locales/en.js → views/alchemy/admin/translations/_en.js} +5 -2
- data/app/views/alchemy/admin/uploader/_button.html.erb +1 -1
- data/app/views/alchemy/admin/uploader/_setup.html.erb +4 -4
- data/app/views/alchemy/ingredients/_number_editor.html.erb +24 -0
- data/app/views/alchemy/ingredients/_page_editor.html.erb +1 -0
- data/app/views/alchemy/ingredients/_richtext_editor.html.erb +1 -0
- data/app/views/alchemy/ingredients/_select_editor.html.erb +2 -1
- data/app/views/alchemy/no_index.html.erb +31 -0
- data/app/views/alchemy/welcome.html.erb +12 -10
- data/app/views/kaminari/alchemy/_first_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_last_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_next_page.html.erb +5 -3
- data/app/views/kaminari/alchemy/_paginator.html.erb +18 -13
- data/app/views/kaminari/alchemy/_prev_page.html.erb +5 -3
- data/app/views/layouts/alchemy/admin.html.erb +5 -9
- data/bun.lockb +0 -0
- data/bundles/remixicon.mjs +153 -0
- data/config/alchemy/config.yml +3 -2
- data/config/initializers/dragonfly.rb +0 -1
- data/config/initializers/mime_types.rb +1 -0
- data/config/locales/alchemy.en.yml +32 -14
- data/config/routes.rb +0 -2
- data/eslint.config.js +2 -1
- data/lib/alchemy/admin/preview_url.rb +4 -5
- data/lib/alchemy/cache_digests/template_tracker.rb +6 -9
- data/lib/alchemy/config_missing.rb +14 -0
- data/lib/alchemy/configuration/base_option.rb +24 -0
- data/lib/alchemy/configuration/boolean_option.rb +16 -0
- data/lib/alchemy/configuration/class_option.rb +15 -0
- data/lib/alchemy/configuration/class_set_option.rb +46 -0
- data/lib/alchemy/configuration/integer_list_option.rb +13 -0
- data/lib/alchemy/configuration/integer_option.rb +12 -0
- data/lib/alchemy/configuration/list_option.rb +22 -0
- data/lib/alchemy/configuration/regexp_option.rb +11 -0
- data/lib/alchemy/configuration/string_list_option.rb +13 -0
- data/lib/alchemy/configuration/string_option.rb +11 -0
- data/lib/alchemy/configuration.rb +115 -0
- data/lib/alchemy/configuration_methods.rb +3 -1
- data/lib/alchemy/configurations/default_language.rb +12 -0
- data/lib/alchemy/configurations/default_site.rb +10 -0
- data/lib/alchemy/configurations/format_matchers.rb +11 -0
- data/lib/alchemy/configurations/mailer.rb +16 -0
- data/lib/alchemy/configurations/main.rb +216 -0
- data/lib/alchemy/configurations/preview.rb +32 -0
- data/lib/alchemy/configurations/sitemap.rb +10 -0
- data/lib/alchemy/configurations/uploader.rb +34 -0
- data/lib/alchemy/controller_actions.rb +1 -2
- data/lib/alchemy/engine.rb +65 -17
- data/lib/alchemy/hints.rb +3 -7
- data/lib/alchemy/name_conversions.rb +0 -6
- data/lib/alchemy/on_page_layout.rb +2 -2
- data/lib/alchemy/permissions.rb +0 -10
- data/lib/alchemy/propshaft/tinymce_asset.rb +15 -0
- data/lib/alchemy/seeder.rb +6 -12
- data/lib/alchemy/tasks/usage.rb +4 -4
- data/lib/alchemy/test_support/config_stubbing.rb +1 -7
- data/lib/alchemy/test_support/factories/attachment_factory.rb +13 -2
- data/lib/alchemy/test_support/factories/language_factory.rb +5 -9
- data/lib/alchemy/test_support/factories/page_factory.rb +2 -4
- data/lib/alchemy/test_support/factories/picture_factory.rb +30 -2
- data/lib/alchemy/test_support/factories/site_factory.rb +2 -2
- data/lib/alchemy/test_support/having_crop_action_examples.rb +2 -2
- data/lib/alchemy/test_support/having_picture_thumbnails_examples.rb +80 -26
- data/lib/alchemy/test_support/shared_ingredient_examples.rb +5 -5
- data/lib/alchemy/upgrader/.keep +0 -0
- data/lib/alchemy/upgrader/eight_zero.rb +14 -0
- data/lib/alchemy/upgrader.rb +33 -20
- data/lib/alchemy/version.rb +1 -1
- data/lib/alchemy.rb +192 -170
- data/lib/alchemy_cms.rb +1 -7
- data/lib/generators/alchemy/ingredient/ingredient_generator.rb +0 -3
- data/lib/generators/alchemy/install/files/_article.html.erb +6 -4
- data/lib/generators/alchemy/install/files/alchemy.en.yml +22 -3
- data/lib/generators/alchemy/install/files/application.html.erb +5 -0
- data/lib/generators/alchemy/install/install_generator.rb +5 -14
- data/lib/generators/alchemy/install/templates/alchemy.rb.tt +196 -0
- data/lib/generators/alchemy/install/templates/dragonfly.rb.tt +0 -1
- data/lib/generators/alchemy/install/templates/elements.yml.tt +3 -1
- data/lib/generators/alchemy/install/templates/menus.yml.tt +1 -1
- data/lib/generators/alchemy/install/templates/page_layouts.yml.tt +2 -2
- data/lib/generators/alchemy/page_layouts/page_layouts_generator.rb +2 -2
- data/lib/tasks/alchemy/assets.rake +14 -0
- data/lib/tasks/alchemy/upgrade.rake +12 -47
- data/lib/tasks/alchemy/usage.rake +0 -2
- data/vendor/javascript/tinymce.min.js +1 -1
- data/vitest.config.js +21 -0
- metadata +184 -183
- data/app/assets/builds/alchemy/admin/page-select.css.map +0 -1
- data/app/assets/builds/alchemy/admin/print.css.map +0 -1
- data/app/assets/builds/alchemy/admin.css.map +0 -1
- data/app/assets/builds/alchemy/custom-properties.css.map +0 -1
- data/app/assets/builds/alchemy/welcome.css.map +0 -1
- data/app/assets/builds/tinymce/skins/content/alchemy/content.min.css.map +0 -1
- data/app/assets/builds/tinymce/skins/ui/alchemy/skin.min.css.map +0 -1
- data/app/assets/javascripts/alchemy/admin.js +0 -10
- data/app/assets/stylesheets/alchemy/_defaults.scss +0 -3
- data/app/assets/stylesheets/alchemy/_deprecated_variables.scss +0 -45
- data/app/assets/stylesheets/alchemy/_deprecation.scss +0 -17
- data/app/assets/stylesheets/alchemy/_extends.scss +0 -62
- data/app/assets/stylesheets/alchemy/_variables.scss +0 -201
- data/app/assets/stylesheets/alchemy/admin/buttons.scss +0 -123
- data/app/assets/stylesheets/alchemy/admin/hints.scss +0 -5
- data/app/assets/stylesheets/alchemy/admin/labels.scss +0 -3
- data/app/assets/stylesheets/alchemy/admin/pagination.scss +0 -92
- data/app/assets/stylesheets/alchemy/admin.scss +0 -42
- data/app/assets/stylesheets/alchemy/custom-properties.css +0 -98
- data/app/assets/stylesheets/alchemy/welcome.scss +0 -57
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.css +0 -711
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.css +0 -705
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.inline.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.css +0 -29
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/content.mobile.min.css +0 -7
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.css +0 -677
- data/app/assets/stylesheets/tinymce/skins/ui/alchemy/skin.mobile.min.css +0 -7
- data/app/controllers/alchemy/elements_controller.rb +0 -32
- data/app/models/alchemy/element/dom_id.rb +0 -31
- data/app/models/alchemy/picture/calculations.rb +0 -49
- data/app/models/alchemy/picture/transformations.rb +0 -115
- data/app/views/alchemy/admin/attachments/destroy.js.erb +0 -1
- data/app/views/alchemy/admin/clipboard/clear.js.erb +0 -3
- data/app/views/alchemy/admin/clipboard/insert.js.erb +0 -29
- data/app/views/alchemy/admin/clipboard/remove.js.erb +0 -10
- data/app/views/alchemy/admin/resources/_filter.html.erb +0 -12
- data/app/views/alchemy/admin/resources/_resource.html.erb +0 -34
- data/app/views/alchemy/admin/resources/_table.html.erb +0 -29
- data/app/views/alchemy/elements/show.html.erb +0 -1
- data/app/views/alchemy/elements/show.js.erb +0 -1
- data/app/views/alchemy/ingredients/_audio_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_boolean_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_datetime_view.html.erb +0 -3
- data/app/views/alchemy/ingredients/_file_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_headline_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_html_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_link_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_node_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_page_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_picture_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_richtext_view.html.erb +0 -3
- data/app/views/alchemy/ingredients/_select_view.html.erb +0 -1
- data/app/views/alchemy/ingredients/_text_view.html.erb +0 -4
- data/app/views/alchemy/ingredients/_video_view.html.erb +0 -3
- data/babel.config.js +0 -12
- data/config/initializers/assets.rb +0 -4
- data/lib/alchemy/config.rb +0 -114
- data/lib/alchemy/element_definition.rb +0 -73
- data/lib/alchemy/page_layout.rb +0 -73
- data/lib/alchemy/resource_filter.rb +0 -40
- data/lib/alchemy/upgrader/seven_point_four.rb +0 -26
- data/lib/alchemy/upgrader/seven_point_three.rb +0 -52
- data/lib/generators/alchemy/ingredient/templates/view.html.erb +0 -1
- data/lib/generators/alchemy/install/files/alchemy_admin.js +0 -1
- data/lib/generators/alchemy/install/files/all.js +0 -11
- data/lib/generators/alchemy/install/files/article.css +0 -25
- data/vendor/assets/images/remixicon.symbol.svg +0 -11
- /data/app/{assets/stylesheets → stylesheets}/alchemy/_fonts.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachment-select.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/attachments.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/flash.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/list_filter.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/node-select.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/alchemy/admin/spinner.scss +0 -0
- /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/skintool.json +0 -0
- /data/app/{assets/stylesheets → stylesheets}/tinymce/skins/ui/alchemy/fonts/tinymce-mobile.woff +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { removeTab } from "alchemy_admin/fixed_elements"
|
|
2
2
|
import { growl } from "alchemy_admin/growler"
|
|
3
3
|
import { reloadPreview } from "alchemy_admin/components/preview_window"
|
|
4
|
-
import {
|
|
4
|
+
import { openConfirmDialog } from "alchemy_admin/confirm_dialog"
|
|
5
5
|
|
|
6
6
|
export class DeleteElementButton extends HTMLElement {
|
|
7
7
|
constructor() {
|
|
@@ -9,12 +9,12 @@ export class DeleteElementButton extends HTMLElement {
|
|
|
9
9
|
this.addEventListener("click", this)
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
handleEvent() {
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
12
|
+
async handleEvent() {
|
|
13
|
+
const confirmed = await openConfirmDialog(this.message)
|
|
14
|
+
if (confirmed) {
|
|
15
|
+
const response = await fetch(this.url, { method: "DELETE" })
|
|
16
|
+
this.#removeElement(await response.json())
|
|
17
|
+
}
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
#removeElement(response) {
|
|
@@ -231,7 +231,7 @@ export class ElementEditor extends HTMLElement {
|
|
|
231
231
|
setDirty() {
|
|
232
232
|
if (this.hasEditors) {
|
|
233
233
|
this.dirty = true
|
|
234
|
-
window.onbeforeunload = () =>
|
|
234
|
+
window.onbeforeunload = (event) => event.preventDefault()
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
237
|
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
"use strict"
|
|
2
|
-
// ^ add support for top-level await in Terser
|
|
3
|
-
|
|
4
1
|
import "alchemy_admin/components/action"
|
|
5
2
|
import "alchemy_admin/components/attachment_select"
|
|
6
3
|
import "alchemy_admin/components/button"
|
|
@@ -27,6 +24,7 @@ import "alchemy_admin/components/select"
|
|
|
27
24
|
import "alchemy_admin/components/spinner"
|
|
28
25
|
import "alchemy_admin/components/tags_autocomplete"
|
|
29
26
|
import "alchemy_admin/components/tinymce"
|
|
27
|
+
import "alchemy_admin/components/update_check"
|
|
30
28
|
|
|
31
29
|
await Promise.race([
|
|
32
30
|
// Load all global custom elements
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { AlchemyHTMLElement } from "alchemy_admin/components/alchemy_html_element"
|
|
2
|
+
import { setupSelectLocale } from "alchemy_admin/i18n"
|
|
2
3
|
|
|
3
4
|
export class RemoteSelect extends AlchemyHTMLElement {
|
|
4
5
|
static properties = {
|
|
@@ -9,7 +10,9 @@ export class RemoteSelect extends AlchemyHTMLElement {
|
|
|
9
10
|
url: { default: "" }
|
|
10
11
|
}
|
|
11
12
|
|
|
12
|
-
connected() {
|
|
13
|
+
async connected() {
|
|
14
|
+
await setupSelectLocale()
|
|
15
|
+
|
|
13
16
|
this.input.classList.add("alchemy_selectbox")
|
|
14
17
|
|
|
15
18
|
$(this.input)
|
|
@@ -1,5 +1,9 @@
|
|
|
1
|
+
import { setupSelectLocale } from "alchemy_admin/i18n"
|
|
2
|
+
|
|
1
3
|
class TagsAutocomplete extends HTMLElement {
|
|
2
|
-
connectedCallback() {
|
|
4
|
+
async connectedCallback() {
|
|
5
|
+
await setupSelectLocale()
|
|
6
|
+
|
|
3
7
|
this.classList.add("autocomplete_tag_list")
|
|
4
8
|
$(this.input).select2(this.select2Config)
|
|
5
9
|
}
|
|
@@ -76,8 +76,10 @@ class Tinymce extends AlchemyHTMLElement {
|
|
|
76
76
|
this.getElementsByTagName("alchemy-spinner")[0].remove()
|
|
77
77
|
|
|
78
78
|
// event listener to mark the editor as dirty
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
if (this.elementEditor) {
|
|
80
|
+
editor.on("dirty", () => this.elementEditor.setDirty())
|
|
81
|
+
editor.on("click", () => this.elementEditor.onClickElement(false))
|
|
82
|
+
}
|
|
81
83
|
})
|
|
82
84
|
})
|
|
83
85
|
}
|
|
@@ -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
|
}
|
|
@@ -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
|
}
|
|
@@ -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,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 })
|
|
@@ -1,11 +1,6 @@
|
|
|
1
1
|
import { translate } from "alchemy_admin/i18n"
|
|
2
2
|
import { Dialog } from "alchemy_admin/dialog"
|
|
3
3
|
|
|
4
|
-
// Matches a URL fragment (#anchor) at the end of a string.
|
|
5
|
-
// Covers RFC 3986 unreserved characters (ALPHA, DIGIT, "-", ".", "_", "~")
|
|
6
|
-
// which are the characters valid in URL fragments and common in DOM element IDs.
|
|
7
|
-
const ANCHOR_REGEX = /#[\w.~-]+$/
|
|
8
|
-
|
|
9
4
|
// Represents the link Dialog that appears, if a user clicks the link buttons
|
|
10
5
|
// in TinyMCE or on an Ingredient that has links enabled (e.g. Picture)
|
|
11
6
|
//
|
|
@@ -108,7 +103,7 @@ export class LinkDialog extends Dialog {
|
|
|
108
103
|
|
|
109
104
|
if (linkType === "internal" && elementAnchor.value !== "") {
|
|
110
105
|
// remove possible fragments on the url and attach the fragment (which contains the #)
|
|
111
|
-
url = url.replace(
|
|
106
|
+
url = url.replace(/#\w+$/, "") + elementAnchor.value
|
|
112
107
|
} else if (linkType === "external" && !url.match(Alchemy.link_url_regexp)) {
|
|
113
108
|
// show validation error and prevent link creation
|
|
114
109
|
this.#showValidationError()
|
|
@@ -1,27 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
1
|
+
const JSON_CONTENT_TYPE = "application/json"
|
|
2
|
+
|
|
3
|
+
function isGetRequest(method) {
|
|
4
|
+
return method.toLowerCase() === "get"
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
function prepareURL(path, data, method) {
|
|
8
|
+
const url = new URL(window.location.origin + path)
|
|
9
|
+
|
|
10
|
+
if (data && isGetRequest(method)) {
|
|
11
|
+
url.search = new URLSearchParams(data).toString()
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
return url.toString()
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function prepareHeaders(accept) {
|
|
18
|
+
return {
|
|
19
|
+
"Content-Type": "application/json; charset=utf-8",
|
|
20
|
+
Accept: accept,
|
|
21
|
+
"X-Requested-With": "XMLHttpRequest",
|
|
22
|
+
"X-CSRF-Token": getToken()
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function prepareOptions(method, data, accept) {
|
|
27
|
+
const headers = prepareHeaders(accept)
|
|
28
|
+
const options = { method, headers }
|
|
29
|
+
|
|
30
|
+
if (data && !isGetRequest(method)) {
|
|
31
|
+
options.body = JSON.stringify(data)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return options
|
|
25
35
|
}
|
|
26
36
|
|
|
27
37
|
export function getToken() {
|
|
@@ -37,30 +47,27 @@ export function patch(url, data) {
|
|
|
37
47
|
return ajax("PATCH", url, data)
|
|
38
48
|
}
|
|
39
49
|
|
|
40
|
-
export function post(url, data, accept =
|
|
50
|
+
export function post(url, data, accept = JSON_CONTENT_TYPE) {
|
|
41
51
|
return ajax("POST", url, data, accept)
|
|
42
52
|
}
|
|
43
53
|
|
|
44
|
-
export default function ajax(
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
54
|
+
export default async function ajax(
|
|
55
|
+
method,
|
|
56
|
+
path,
|
|
57
|
+
data,
|
|
58
|
+
accept = JSON_CONTENT_TYPE
|
|
59
|
+
) {
|
|
60
|
+
const response = await fetch(
|
|
61
|
+
prepareURL(path, data, method),
|
|
62
|
+
prepareOptions(method, data, accept)
|
|
63
|
+
)
|
|
64
|
+
const contentType = response.headers.get("content-type")
|
|
65
|
+
const isJson = contentType?.includes(JSON_CONTENT_TYPE)
|
|
66
|
+
const responseData = isJson ? await response.json() : null
|
|
48
67
|
|
|
49
|
-
if (
|
|
50
|
-
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
xhr.open(method, url.toString())
|
|
54
|
-
xhr.setRequestHeader("Content-type", "application/json; charset=utf-8")
|
|
55
|
-
xhr.setRequestHeader("Accept", accept)
|
|
56
|
-
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
|
|
57
|
-
xhr.setRequestHeader("X-CSRF-Token", getToken())
|
|
58
|
-
|
|
59
|
-
if (data && method.toLowerCase() !== "get") {
|
|
60
|
-
xhr.send(JSON.stringify(data))
|
|
68
|
+
if (response.ok) {
|
|
69
|
+
return { data: responseData, status: response.status }
|
|
61
70
|
} else {
|
|
62
|
-
|
|
71
|
+
throw responseData || new Error("An error occurred during the transaction")
|
|
63
72
|
}
|
|
64
|
-
|
|
65
|
-
return promise
|
|
66
73
|
}
|
|
@@ -20,10 +20,7 @@ import Sitemap from "alchemy_admin/sitemap"
|
|
|
20
20
|
import Spinner from "alchemy_admin/spinner"
|
|
21
21
|
import PagePublicationFields from "alchemy_admin/page_publication_fields"
|
|
22
22
|
import { reloadPreview } from "alchemy_admin/components/preview_window"
|
|
23
|
-
import {
|
|
24
|
-
openConfirmDialog,
|
|
25
|
-
confirmToDeleteDialog
|
|
26
|
-
} from "alchemy_admin/confirm_dialog"
|
|
23
|
+
import { openConfirmDialog } from "alchemy_admin/confirm_dialog"
|
|
27
24
|
|
|
28
25
|
// Web Components
|
|
29
26
|
import "alchemy_admin/components"
|
|
@@ -53,11 +50,9 @@ Object.assign(Alchemy, {
|
|
|
53
50
|
Sitemap,
|
|
54
51
|
Spinner,
|
|
55
52
|
PagePublicationFields,
|
|
56
|
-
reloadPreview
|
|
57
|
-
openConfirmDialog,
|
|
58
|
-
confirmToDeleteDialog
|
|
53
|
+
reloadPreview
|
|
59
54
|
})
|
|
60
55
|
|
|
61
56
|
Rails.start()
|
|
62
|
-
|
|
57
|
+
Turbo.config.forms.confirm = openConfirmDialog
|
|
63
58
|
$(document).on("turbo:load", Initializer)
|