alchemy_cms 7.4.6 → 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.
Potentially problematic release.
This version of alchemy_cms might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +86 -0
- data/Gemfile +13 -6
- 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 +5 -2
- 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 -1
- data/app/controllers/alchemy/admin/pages_controller.rb +15 -15
- data/app/controllers/alchemy/admin/pictures_controller.rb +9 -5
- 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 +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 +3 -53
- 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 +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/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 -0
- 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/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 +51 -34
- 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 +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 +0 -14
- 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_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 +69 -79
- 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 +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/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 +36 -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 +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 +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/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/base/redirect.js.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 +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/engine.rb +65 -17
- data/lib/alchemy/hints.rb +3 -7
- 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/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 +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/vendor/javascript/tinymce.min.js +1 -1
- data/vitest.config.js +21 -0
- metadata +181 -180
- 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 -124
- 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
@@ -33,7 +33,7 @@ module Alchemy
|
|
33
33
|
#
|
34
34
|
class ToolbarButton < ViewComponent::Base
|
35
35
|
erb_template <<-ERB
|
36
|
-
<div class="toolbar_button">
|
36
|
+
<div class="toolbar_button" id="<%= id %>">
|
37
37
|
<sl-tooltip content="<%= label %>" placement="<%= tooltip_placement %>">
|
38
38
|
<%= link_to(render_icon(icon, style: icon_style), url, {
|
39
39
|
class: css_classes,
|
@@ -57,6 +57,7 @@ module Alchemy
|
|
57
57
|
:if_permitted_to,
|
58
58
|
:active,
|
59
59
|
:link_options,
|
60
|
+
:id,
|
60
61
|
:icon_style,
|
61
62
|
:tooltip_placement
|
62
63
|
|
@@ -72,6 +73,7 @@ module Alchemy
|
|
72
73
|
if_permitted_to: [],
|
73
74
|
active: false,
|
74
75
|
link_options: {},
|
76
|
+
id: nil,
|
75
77
|
icon_style: "line",
|
76
78
|
tooltip_placement: "top-start"
|
77
79
|
)
|
@@ -85,6 +87,7 @@ module Alchemy
|
|
85
87
|
@if_permitted_to = if_permitted_to
|
86
88
|
@active = active
|
87
89
|
@link_options = link_options
|
90
|
+
@id = id
|
88
91
|
@icon_style = icon_style
|
89
92
|
@tooltip_placement = tooltip_placement
|
90
93
|
end
|
@@ -100,7 +103,7 @@ module Alchemy
|
|
100
103
|
def permission_options = if_permitted_to.presence || permissions_from_url
|
101
104
|
|
102
105
|
def permissions_from_url
|
103
|
-
action_controller = url.
|
106
|
+
action_controller = url.delete_prefix("/").split("/")
|
104
107
|
[
|
105
108
|
action_controller.last.to_sym,
|
106
109
|
action_controller[0..action_controller.length - 2].join("_").to_sym
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module Alchemy
|
2
|
+
module Ingredients
|
3
|
+
class NumberView < BaseView
|
4
|
+
def initialize(ingredient, options = {})
|
5
|
+
super(ingredient)
|
6
|
+
@options = {
|
7
|
+
units: {
|
8
|
+
unit: settings_value(:unit, value: options[:unit])
|
9
|
+
}.merge(options[:units] || {})
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def call
|
14
|
+
number_to_human(value, @options)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -6,6 +6,12 @@ module Alchemy
|
|
6
6
|
include UploaderResponses
|
7
7
|
include ArchiveOverlay
|
8
8
|
|
9
|
+
add_alchemy_filter :by_file_type, type: :select,
|
10
|
+
options: -> { Alchemy::Attachment.file_types(_1.result) }
|
11
|
+
add_alchemy_filter :recent, type: :checkbox
|
12
|
+
add_alchemy_filter :last_upload, type: :checkbox
|
13
|
+
add_alchemy_filter :without_tag, type: :checkbox
|
14
|
+
|
9
15
|
helper "alchemy/admin/tags"
|
10
16
|
|
11
17
|
before_action(only: :assign) do
|
@@ -21,10 +27,6 @@ module Alchemy
|
|
21
27
|
@attachments = @attachments.tagged_with(search_filter_params[:tagged_with])
|
22
28
|
end
|
23
29
|
|
24
|
-
if search_filter_params[:filter].present?
|
25
|
-
@attachments = apply_filters(@attachments)
|
26
|
-
end
|
27
|
-
|
28
30
|
@attachments = @attachments
|
29
31
|
.page(params[:page] || 1)
|
30
32
|
.per(items_per_page)
|
@@ -58,18 +60,9 @@ module Alchemy
|
|
58
60
|
end
|
59
61
|
|
60
62
|
def destroy
|
61
|
-
name = @attachment.name
|
62
63
|
@attachment.destroy
|
63
|
-
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
def download
|
68
|
-
@attachment = Attachment.find(params[:id])
|
69
|
-
send_file @attachment.file.path, {
|
70
|
-
filename: @attachment.file_name,
|
71
|
-
type: @attachment.file_mime_type
|
72
|
-
}
|
64
|
+
flash[:notice] = Alchemy.t("File deleted successfully", name: @attachment.name)
|
65
|
+
redirect_to alchemy.admin_attachments_path(**search_filter_params)
|
73
66
|
end
|
74
67
|
|
75
68
|
private
|
@@ -8,6 +8,8 @@ module Alchemy
|
|
8
8
|
authorize_resource class: :alchemy_admin_clipboard
|
9
9
|
before_action :set_clipboard
|
10
10
|
|
11
|
+
helper_method :remarkable_type
|
12
|
+
|
11
13
|
def index
|
12
14
|
@clipboard_items = model_class.all_from_clipboard(@clipboard)
|
13
15
|
respond_to do |format|
|
@@ -23,17 +25,11 @@ module Alchemy
|
|
23
25
|
"action" => params[:remove] ? "cut" : "copy"
|
24
26
|
}
|
25
27
|
end
|
26
|
-
respond_to do |format|
|
27
|
-
format.js
|
28
|
-
end
|
29
28
|
end
|
30
29
|
|
31
30
|
def remove
|
32
31
|
@item = model_class.find(remarkable_params[:remarkable_id])
|
33
32
|
@clipboard.delete_if { |item| item["id"] == remarkable_params[:remarkable_id] }
|
34
|
-
respond_to do |format|
|
35
|
-
format.js
|
36
|
-
end
|
37
33
|
end
|
38
34
|
|
39
35
|
def clear
|
@@ -37,6 +37,16 @@ module Alchemy
|
|
37
37
|
if: :run_on_page_layout_callbacks?,
|
38
38
|
only: [:show]
|
39
39
|
|
40
|
+
add_alchemy_filter :by_page_layout, type: :select, options: ->(_q) do
|
41
|
+
PageDefinition.all.map { |p| [Alchemy.t(p[:name], scope: "page_layout_names"), p[:name]] }
|
42
|
+
end
|
43
|
+
|
44
|
+
add_alchemy_filter :updated_at_gteq, type: :datepicker
|
45
|
+
add_alchemy_filter :updated_at_lteq, type: :datepicker
|
46
|
+
add_alchemy_filter :published, type: :checkbox
|
47
|
+
add_alchemy_filter :not_public, type: :checkbox
|
48
|
+
add_alchemy_filter :restricted, type: :checkbox
|
49
|
+
|
40
50
|
def index
|
41
51
|
@query = @current_language.pages.contentpages.ransack(search_filter_params[:q])
|
42
52
|
|
@@ -48,10 +58,6 @@ module Alchemy
|
|
48
58
|
items = items.tagged_with(search_filter_params[:tagged_with])
|
49
59
|
end
|
50
60
|
|
51
|
-
if search_filter_params[:filter].present?
|
52
|
-
items = apply_filters(items)
|
53
|
-
end
|
54
|
-
|
55
61
|
items = items.page(params[:page] || 1).per(items_per_page)
|
56
62
|
@pages = items
|
57
63
|
end
|
@@ -69,7 +75,7 @@ module Alchemy
|
|
69
75
|
Current.preview_page = @page
|
70
76
|
# Setting the locale to pages language, so the page content has it's correct translations.
|
71
77
|
::I18n.locale = @page.language.locale
|
72
|
-
render(layout: Alchemy
|
78
|
+
render(layout: Alchemy.config.admin_page_preview_layout || "application")
|
73
79
|
end
|
74
80
|
|
75
81
|
def info
|
@@ -155,16 +161,10 @@ module Alchemy
|
|
155
161
|
flash[:warning] = @page.errors.full_messages.to_sentence
|
156
162
|
end
|
157
163
|
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
else
|
163
|
-
alchemy.admin_pages_path
|
164
|
-
end
|
165
|
-
|
166
|
-
render :redirect
|
167
|
-
end
|
164
|
+
if @page.layoutpage?
|
165
|
+
redirect_to alchemy.admin_layoutpages_path
|
166
|
+
else
|
167
|
+
redirect_to alchemy.admin_pages_path
|
168
168
|
end
|
169
169
|
end
|
170
170
|
|
@@ -21,9 +21,17 @@ module Alchemy
|
|
21
21
|
@picture = Picture.find(params[:id])
|
22
22
|
end
|
23
23
|
|
24
|
+
add_alchemy_filter :by_file_format, type: :select, options: ->(query) do
|
25
|
+
Alchemy::Picture.file_formats(query.result)
|
26
|
+
end
|
27
|
+
add_alchemy_filter :recent, type: :checkbox
|
28
|
+
add_alchemy_filter :last_upload, type: :checkbox
|
29
|
+
add_alchemy_filter :without_tag, type: :checkbox
|
30
|
+
add_alchemy_filter :deletable, type: :checkbox
|
31
|
+
|
24
32
|
def index
|
25
33
|
@query = Picture.ransack(search_filter_params[:q])
|
26
|
-
@pictures =
|
34
|
+
@pictures = Alchemy.storage_adapter.preloaded_pictures(filtered_pictures)
|
27
35
|
|
28
36
|
if in_overlay?
|
29
37
|
archive_overlay
|
@@ -139,10 +147,6 @@ module Alchemy
|
|
139
147
|
pictures = pictures.tagged_with(params[:tagged_with])
|
140
148
|
end
|
141
149
|
|
142
|
-
if search_filter_params[:filter].present?
|
143
|
-
pictures = apply_filters(pictures)
|
144
|
-
end
|
145
|
-
|
146
150
|
pictures = pictures.page(params[:page] || 1).per(items_per_page)
|
147
151
|
|
148
152
|
pictures.order(:name)
|
@@ -1,21 +1,16 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "csv"
|
4
|
-
require "alchemy/resource"
|
5
|
-
require "alchemy/resources_helper"
|
6
|
-
require "alchemy/resource_filter"
|
7
4
|
|
8
5
|
module Alchemy
|
9
6
|
module Admin
|
10
7
|
class ResourcesController < Alchemy::Admin::BaseController
|
11
|
-
COMMON_SEARCH_FILTER_EXCLUDES = %i[id utf8 _method _ format].freeze
|
12
|
-
|
13
8
|
include Alchemy::ResourcesHelper
|
9
|
+
extend Alchemy::Admin::ResourceName
|
10
|
+
include Alchemy::Admin::ResourceFilter
|
14
11
|
|
15
12
|
helper Alchemy::ResourcesHelper, TagsHelper
|
16
|
-
helper_method :resource_handler, :
|
17
|
-
:items_per_page, :items_per_page_options, :resource_has_filters,
|
18
|
-
:resource_filters_for_select
|
13
|
+
helper_method :resource_handler, :items_per_page, :items_per_page_options
|
19
14
|
|
20
15
|
before_action :load_resource,
|
21
16
|
only: %i[show edit update destroy]
|
@@ -29,21 +24,20 @@ module Alchemy
|
|
29
24
|
|
30
25
|
items = items.includes(*resource_relations_names) if contains_relations?
|
31
26
|
items = items.tagged_with(search_filter_params[:tagged_with]) if search_filter_params[:tagged_with].present?
|
32
|
-
items = apply_filters(items) if search_filter_params[:filter].present?
|
33
27
|
|
34
28
|
respond_to do |format|
|
35
29
|
format.html do
|
36
30
|
items = items.page(params[:page] || 1).per(items_per_page)
|
37
|
-
instance_variable_set(:"@#{
|
31
|
+
instance_variable_set(:"@#{resources_name}", items)
|
38
32
|
end
|
39
33
|
format.csv do
|
40
|
-
instance_variable_set(:"@#{
|
34
|
+
instance_variable_set(:"@#{resources_name}", items)
|
41
35
|
end
|
42
36
|
end
|
43
37
|
end
|
44
38
|
|
45
39
|
def new
|
46
|
-
instance_variable_set(:"@#{
|
40
|
+
instance_variable_set(:"@#{resource_name}", resource_handler.model.new)
|
47
41
|
end
|
48
42
|
|
49
43
|
def show
|
@@ -54,7 +48,7 @@ module Alchemy
|
|
54
48
|
end
|
55
49
|
|
56
50
|
def create
|
57
|
-
instance_variable_set(:"@#{
|
51
|
+
instance_variable_set(:"@#{resource_name}", resource_handler.model.new(resource_params))
|
58
52
|
resource_instance_variable.save
|
59
53
|
render_errors_or_redirect(
|
60
54
|
resource_instance_variable,
|
@@ -85,62 +79,8 @@ module Alchemy
|
|
85
79
|
@_resource_handler ||= Alchemy::Resource.new(controller_path, alchemy_module)
|
86
80
|
end
|
87
81
|
|
88
|
-
def resource_has_filters
|
89
|
-
resource_model.respond_to?(:alchemy_resource_filters)
|
90
|
-
end
|
91
|
-
|
92
|
-
def resource_has_deprecated_filters
|
93
|
-
resource_model.alchemy_resource_filters.any? { |f| !f.is_a?(Hash) }
|
94
|
-
end
|
95
|
-
|
96
|
-
def resource_filters
|
97
|
-
return unless resource_has_filters
|
98
|
-
|
99
|
-
@_resource_filters ||= resource_model.alchemy_resource_filters
|
100
|
-
end
|
101
|
-
|
102
|
-
def resource_filters_for_select
|
103
|
-
resource_filters.map do |filter|
|
104
|
-
ResourceFilter.new(filter, resource_handler.resource_name)
|
105
|
-
end
|
106
|
-
end
|
107
|
-
|
108
82
|
protected
|
109
83
|
|
110
|
-
def apply_filters(items)
|
111
|
-
sanitize_filter_params!
|
112
|
-
|
113
|
-
search_filter_params[:filter].each do |filter|
|
114
|
-
if argument_scope_filter?(filter)
|
115
|
-
items = items.public_send(filter[0], filter[1])
|
116
|
-
elsif simple_scope_filter?(filter)
|
117
|
-
items = items.public_send(filter[1])
|
118
|
-
else
|
119
|
-
raise "Can't apply filter #{filter[0]}. Either the name or the values must be defined as class methods / scopes on the model."
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
items
|
124
|
-
end
|
125
|
-
|
126
|
-
def simple_scope_filter?(filter)
|
127
|
-
resource_model.respond_to?(filter[1])
|
128
|
-
end
|
129
|
-
|
130
|
-
def argument_scope_filter?(filter)
|
131
|
-
resource_model.respond_to?(filter[0])
|
132
|
-
end
|
133
|
-
|
134
|
-
def sanitize_filter_params!
|
135
|
-
search_filter_params[:filter].reject! do |_, v|
|
136
|
-
eligible_resource_filter_values.exclude?(v)
|
137
|
-
end
|
138
|
-
end
|
139
|
-
|
140
|
-
def eligible_resource_filter_values
|
141
|
-
resource_filters.map(&:values).flatten!.map!(&:to_s)
|
142
|
-
end
|
143
|
-
|
144
84
|
# Returns a translated +flash[:notice]+ for current controller action.
|
145
85
|
def flash_notice_for_resource_action(action = action_name)
|
146
86
|
return if resource_instance_variable.errors.any?
|
@@ -174,61 +114,31 @@ module Alchemy
|
|
174
114
|
end
|
175
115
|
|
176
116
|
def load_resource
|
177
|
-
instance_variable_set(:"@#{
|
117
|
+
instance_variable_set(:"@#{resource_name}", resource_handler.model.find(params[:id]))
|
178
118
|
end
|
179
119
|
|
180
120
|
def authorize_resource
|
181
121
|
authorize!(action_name.to_sym, resource_instance_variable || resource_handler.model)
|
182
122
|
end
|
183
123
|
|
184
|
-
# Permits all
|
185
|
-
#
|
186
|
-
# THIS IS INSECURE! Although only signed in admin users can send requests anyway, but we should change this.
|
124
|
+
# Permits all editable resource attributes as default.
|
187
125
|
#
|
188
|
-
#
|
189
|
-
#
|
190
|
-
# TODO: Hook this into authorization provider.
|
126
|
+
# Define this method in your inheriting controller if you want to permit additional attributes.
|
191
127
|
#
|
128
|
+
# @see Alchemy::Resource#editable_attributes
|
192
129
|
def resource_params
|
193
|
-
params.require(resource_handler.namespaced_resource_name).permit
|
194
|
-
|
195
|
-
|
196
|
-
def search_filter_params
|
197
|
-
@_search_filter_params ||= params.except(*COMMON_SEARCH_FILTER_EXCLUDES).permit(*common_search_filter_includes).to_h
|
198
|
-
end
|
199
|
-
|
200
|
-
def common_search_filter_includes
|
201
|
-
search_filters = [
|
202
|
-
{
|
203
|
-
q: [:s] + permitted_ransack_search_fields
|
204
|
-
},
|
205
|
-
:tagged_with,
|
206
|
-
:page,
|
207
|
-
:per_page
|
208
|
-
]
|
209
|
-
|
210
|
-
if resource_has_filters
|
211
|
-
search_filters << {
|
212
|
-
filter: resource_filters.map { |f| f[:name] }
|
213
|
-
}
|
214
|
-
end
|
215
|
-
|
216
|
-
search_filters
|
217
|
-
end
|
218
|
-
|
219
|
-
def permitted_ransack_search_fields
|
220
|
-
[
|
221
|
-
resource_handler.search_field_name
|
222
|
-
]
|
130
|
+
params.require(resource_handler.namespaced_resource_name).permit(
|
131
|
+
resource_handler.editable_attributes.map { _1[:name] }
|
132
|
+
)
|
223
133
|
end
|
224
134
|
|
225
135
|
def items_per_page
|
226
136
|
cookies[:alchemy_items_per_page] =
|
227
|
-
params[:per_page] || cookies[:alchemy_items_per_page] || Alchemy
|
137
|
+
params[:per_page] || cookies[:alchemy_items_per_page] || Alchemy.config.items_per_page
|
228
138
|
end
|
229
139
|
|
230
140
|
def items_per_page_options
|
231
|
-
per_page = Alchemy
|
141
|
+
per_page = Alchemy.config.items_per_page
|
232
142
|
[per_page, per_page * 2, per_page * 4]
|
233
143
|
end
|
234
144
|
|
@@ -2,37 +2,66 @@
|
|
2
2
|
|
3
3
|
module Alchemy
|
4
4
|
class AttachmentsController < BaseController
|
5
|
+
if Alchemy.storage_adapter.active_storage?
|
6
|
+
include ActiveStorage::Streaming
|
7
|
+
end
|
8
|
+
|
5
9
|
before_action :load_attachment
|
6
10
|
authorize_resource class: Alchemy::Attachment
|
7
11
|
|
12
|
+
self.etag_with_template_digest = false
|
13
|
+
|
8
14
|
# sends file inline. i.e. for viewing pdfs/movies in browser
|
9
15
|
def show
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
type: @attachment.file_mime_type,
|
16
|
-
disposition: "inline"
|
17
|
-
}
|
18
|
-
)
|
16
|
+
if Alchemy.storage_adapter.dragonfly?
|
17
|
+
send_attachment_file(disposition: :inline)
|
18
|
+
else
|
19
|
+
send_attachment_blob(disposition: :inline)
|
20
|
+
end
|
19
21
|
end
|
20
22
|
|
21
23
|
# sends file as attachment. aka download
|
22
24
|
def download
|
25
|
+
if Alchemy.storage_adapter.dragonfly?
|
26
|
+
send_attachment_file(disposition: :attachment)
|
27
|
+
else
|
28
|
+
send_attachment_blob(disposition: :attachment)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def load_attachment
|
35
|
+
@attachment = Attachment.find(params[:id])
|
36
|
+
end
|
37
|
+
|
38
|
+
def send_attachment_file(disposition: :inline)
|
23
39
|
response.headers["Content-Length"] = @attachment.file.size.to_s
|
40
|
+
|
24
41
|
send_file(
|
25
|
-
@attachment.file.path,
|
42
|
+
@attachment.file.path,
|
43
|
+
{
|
26
44
|
filename: @attachment.file_name,
|
27
|
-
type: @attachment.file_mime_type
|
45
|
+
type: @attachment.file_mime_type,
|
46
|
+
disposition: disposition
|
28
47
|
}
|
29
48
|
)
|
30
49
|
end
|
31
50
|
|
32
|
-
|
51
|
+
def send_attachment_blob(disposition: :inline)
|
52
|
+
@blob = @attachment.file.blob
|
33
53
|
|
34
|
-
|
35
|
-
|
54
|
+
if request.headers["Range"].present?
|
55
|
+
send_blob_byte_range_data @blob, request.headers["Range"], disposition: disposition
|
56
|
+
else
|
57
|
+
http_cache_forever public: true do
|
58
|
+
response.headers["Accept-Ranges"] = "bytes"
|
59
|
+
send_blob_stream @blob, disposition: disposition
|
60
|
+
# Rails ActionController::Live removes the Content-Length header,
|
61
|
+
# but browsers need that to be able to show a progress bar during download.
|
62
|
+
response.headers["Content-Length"] = @blob.byte_size.to_s
|
63
|
+
end
|
64
|
+
end
|
36
65
|
end
|
37
66
|
end
|
38
67
|
end
|
@@ -106,7 +106,12 @@ module Alchemy
|
|
106
106
|
def load_index_page
|
107
107
|
@page ||= Language.current_root_page
|
108
108
|
Current.page = @page
|
109
|
-
|
109
|
+
|
110
|
+
if signup_required? && @page.nil?
|
111
|
+
render template: "alchemy/welcome", layout: false
|
112
|
+
elsif !@page&.public?
|
113
|
+
render template: "alchemy/no_index", layout: false
|
114
|
+
end
|
110
115
|
end
|
111
116
|
|
112
117
|
# == Loads page by urlname
|
@@ -192,7 +197,7 @@ module Alchemy
|
|
192
197
|
|
193
198
|
def signup_required?
|
194
199
|
if Alchemy.user_class.respond_to?(:admins)
|
195
|
-
Alchemy.user_class.admins.empty?
|
200
|
+
Alchemy.user_class.admins.empty?
|
196
201
|
end
|
197
202
|
end
|
198
203
|
|
@@ -0,0 +1,92 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Alchemy
|
4
|
+
module Admin
|
5
|
+
module ResourceFilter
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
COMMON_SEARCH_FILTER_EXCLUDES = %i[id utf8 _method _ format].freeze
|
8
|
+
|
9
|
+
included do
|
10
|
+
prepend_before_action :initialize_alchemy_filters
|
11
|
+
helper_method :alchemy_filters, :search_form_name, :applied_filters, :search_filter_params, :resource_has_filters
|
12
|
+
end
|
13
|
+
|
14
|
+
class_methods do
|
15
|
+
# Adds a filter to the resource.
|
16
|
+
# @param name [String, Symbol] The name of the filter.
|
17
|
+
# @param type [Symbol] The type of the filter. Can currently be `:select` or `:checkbox`.
|
18
|
+
# @param args [Hash] Additional arguments for the filter.
|
19
|
+
# @example
|
20
|
+
# add_alchemy_filter :by_location, type: :select, options: ->(query) { Location.pluck(:name, :id) }
|
21
|
+
# add_alchemy_filter :future, type: :checkbox
|
22
|
+
# add_alchemy_filter :by_timeframe, type: :select, options: ["today", "tomorrow"]
|
23
|
+
def add_alchemy_filter(name, type:, **args)
|
24
|
+
alchemy_filters << "Alchemy::Admin::Filters::#{type.to_s.camelize}".constantize.new(
|
25
|
+
name:,
|
26
|
+
resource_name:,
|
27
|
+
**args
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
def alchemy_filters
|
32
|
+
@alchemy_filters ||= []
|
33
|
+
end
|
34
|
+
end
|
35
|
+
delegate :alchemy_filters, to: :class
|
36
|
+
|
37
|
+
private
|
38
|
+
|
39
|
+
def initialize_alchemy_filters
|
40
|
+
return if alchemy_filters.any?
|
41
|
+
return unless resource_model.respond_to?(:alchemy_resource_filters)
|
42
|
+
Alchemy::Deprecation.warn(
|
43
|
+
"The `alchemy_resource_filters` method is deprecated and will be removed in Alchemy 8.1. " \
|
44
|
+
"Please use `add_alchemy_filter` in your controller instead. Make sure to safelist the scopes " \
|
45
|
+
"you want to use in the `ransackable_scopes` method of your model."
|
46
|
+
)
|
47
|
+
resource_model.alchemy_resource_filters.each do |filter_config|
|
48
|
+
if resource_model.respond_to?(filter_config[:name])
|
49
|
+
self.class.add_alchemy_filter filter_config[:name], type: :select, options: filter_config[:values]
|
50
|
+
else
|
51
|
+
filter_config[:values].each do |scope|
|
52
|
+
self.class.add_alchemy_filter scope, type: :checkbox
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def search_filter_params
|
59
|
+
@_search_filter_params ||= params.except(*COMMON_SEARCH_FILTER_EXCLUDES).permit(*common_search_filter_includes).to_h
|
60
|
+
end
|
61
|
+
|
62
|
+
def common_search_filter_includes
|
63
|
+
[
|
64
|
+
{
|
65
|
+
q: [:s] + permitted_ransack_search_fields
|
66
|
+
},
|
67
|
+
:tagged_with,
|
68
|
+
:page,
|
69
|
+
:per_page
|
70
|
+
]
|
71
|
+
end
|
72
|
+
|
73
|
+
def permitted_ransack_search_fields
|
74
|
+
[
|
75
|
+
resource_handler.search_field_name
|
76
|
+
] + alchemy_filters.map(&:name)
|
77
|
+
end
|
78
|
+
|
79
|
+
def resource_has_filters
|
80
|
+
alchemy_filters.any?
|
81
|
+
end
|
82
|
+
|
83
|
+
def applied_filters
|
84
|
+
return [] unless search_filter_params[:q]
|
85
|
+
|
86
|
+
alchemy_filters.select do |alchemy_filter|
|
87
|
+
search_filter_params[:q][alchemy_filter.name].present?
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|