wagtail 6.2.2__py3-none-any.whl → 6.3rc1__py3-none-any.whl
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.
- wagtail/__init__.py +1 -1
- wagtail/actions/copy_for_translation.py +6 -0
- wagtail/actions/publish_revision.py +3 -3
- wagtail/admin/action_menu.py +5 -3
- wagtail/admin/forms/account.py +1 -1
- wagtail/admin/icons.py +2 -6
- wagtail/admin/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/cy/LC_MESSAGES/django.po +32 -0
- wagtail/admin/locale/dv/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/dv/LC_MESSAGES/django.po +28 -0
- wagtail/admin/locale/en/LC_MESSAGES/django.po +451 -485
- wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +7 -7
- wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/sl/LC_MESSAGES/django.po +150 -0
- wagtail/admin/locale/sl/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/sl/LC_MESSAGES/djangojs.po +9 -2
- wagtail/admin/locale/ug/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ug/LC_MESSAGES/django.po +3250 -196
- wagtail/admin/localization.py +8 -2
- wagtail/admin/panels/model_utils.py +1 -1
- wagtail/admin/site_summary.py +0 -2
- wagtail/admin/static/wagtailadmin/css/core.css +1 -1
- wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
- wagtail/admin/static/wagtailadmin/images/email-header.jpg +0 -0
- wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +12 -0
- wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
- wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
- wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
- wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
- wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
- wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
- wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
- wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
- wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
- wagtail/admin/staticfiles.py +6 -4
- wagtail/admin/templates/wagtailadmin/account/account.html +42 -59
- wagtail/admin/templates/wagtailadmin/admin_base.html +0 -28
- wagtail/admin/templates/wagtailadmin/bulk_actions/footer.html +1 -1
- wagtail/admin/templates/wagtailadmin/chooser/_search_results.html +7 -5
- wagtail/admin/templates/wagtailadmin/chooser/tables/page_navigate_to_children_cell.html +1 -1
- wagtail/admin/templates/wagtailadmin/generic/chooser/results.html +7 -5
- wagtail/admin/templates/wagtailadmin/generic/edit.html +0 -8
- wagtail/admin/templates/wagtailadmin/generic/form.html +60 -17
- wagtail/admin/templates/wagtailadmin/generic/index.html +17 -0
- wagtail/admin/templates/wagtailadmin/generic/index_results.html +9 -35
- wagtail/admin/templates/wagtailadmin/generic/inspect.html +2 -21
- wagtail/admin/templates/wagtailadmin/generic/listing.html +11 -17
- wagtail/admin/templates/wagtailadmin/generic/listing_results.html +19 -1
- wagtail/admin/templates/wagtailadmin/home/account_summary.html +26 -0
- wagtail/admin/templates/wagtailadmin/home/locked_pages.html +28 -18
- wagtail/admin/templates/wagtailadmin/home/recent_edits.html +28 -17
- wagtail/admin/templates/wagtailadmin/home/site_summary_pages.html +2 -2
- wagtail/admin/templates/wagtailadmin/home/upgrade_notification.html +35 -13
- wagtail/admin/templates/wagtailadmin/home/user_objects_in_workflow_moderation.html +28 -18
- wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +43 -32
- wagtail/admin/templates/wagtailadmin/home.html +22 -5
- wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -1
- wagtail/admin/templates/wagtailadmin/pages/action_menu/menu.html +1 -11
- wagtail/admin/templates/wagtailadmin/pages/action_menu/menu_item.html +1 -6
- wagtail/admin/templates/wagtailadmin/pages/action_menu/page_locked.html +1 -1
- wagtail/admin/templates/wagtailadmin/pages/action_menu/publish.html +1 -1
- wagtail/admin/templates/wagtailadmin/pages/action_menu/save_draft.html +1 -1
- wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_delete.html +12 -6
- wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_move.html +12 -7
- wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_publish.html +17 -11
- wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_unpublish.html +15 -9
- wagtail/admin/templates/wagtailadmin/pages/confirm_delete.html +9 -9
- wagtail/admin/templates/wagtailadmin/pages/confirm_move.html +2 -2
- wagtail/admin/templates/wagtailadmin/pages/confirm_unpublish.html +4 -4
- wagtail/admin/templates/wagtailadmin/pages/create.html +15 -34
- wagtail/admin/templates/wagtailadmin/pages/edit.html +15 -30
- wagtail/admin/templates/wagtailadmin/pages/explorable_index.html +6 -0
- wagtail/admin/templates/wagtailadmin/pages/explorable_index_results.html +60 -0
- wagtail/admin/templates/wagtailadmin/pages/index.html +1 -36
- wagtail/admin/templates/wagtailadmin/pages/index_results.html +2 -50
- wagtail/admin/templates/wagtailadmin/pages/listing/_locked_indicator.html +1 -1
- wagtail/admin/templates/wagtailadmin/pages/listing/_ordering_cell.html +1 -1
- wagtail/admin/templates/wagtailadmin/pages/listing/_page_header_buttons.html +1 -1
- wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_column_header.html +3 -3
- wagtail/admin/templates/wagtailadmin/pages/listing/_pagination.html +1 -1
- wagtail/admin/templates/wagtailadmin/pages/listing.html +24 -0
- wagtail/admin/templates/wagtailadmin/pages/search.html +1 -20
- wagtail/admin/templates/wagtailadmin/pages/search_results.html +27 -25
- wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_formset.html +0 -1
- wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_report.html +2 -2
- wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_types_usage.html +6 -6
- wagtail/admin/templates/wagtailadmin/reports/workflow_tasks_results.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/action_menu/menu.html +14 -0
- wagtail/admin/templates/wagtailadmin/shared/action_menu/menu_item.html +6 -0
- wagtail/admin/templates/wagtailadmin/shared/avatar.html +13 -3
- wagtail/admin/templates/wagtailadmin/shared/header.html +0 -5
- wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +4 -1
- wagtail/admin/templates/wagtailadmin/shared/pagination_nav.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/locale.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/usage.html +2 -2
- wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +94 -52
- wagtail/admin/templates/wagtailadmin/{pages/_unsaved_changes_warning.html → shared/unsaved_changes_warning.html} +4 -4
- wagtail/admin/templates/wagtailadmin/shared/usage_summary.html +2 -2
- wagtail/admin/templates/wagtailadmin/shared/workflow_history/detail.html +1 -7
- wagtail/admin/templates/wagtailadmin/shared/workflow_history/listing.html +1 -0
- wagtail/admin/templates/wagtailadmin/shared/workflow_history/{list.html → listing_results.html} +33 -35
- wagtail/admin/templates/wagtailadmin/tables/references_cell.html +1 -1
- wagtail/admin/templates/wagtailadmin/workflows/create.html +11 -36
- wagtail/admin/templates/wagtailadmin/workflows/create_task.html +0 -38
- wagtail/admin/templates/wagtailadmin/workflows/edit.html +44 -74
- wagtail/admin/templates/wagtailadmin/workflows/edit_task.html +27 -66
- wagtail/admin/templates/wagtailadmin/workflows/includes/task_usage_cell.html +4 -2
- wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_tasks_cell.html +4 -4
- wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_used_by_cell.html +15 -11
- wagtail/admin/templates/wagtailadmin/workflows/task_chooser/includes/results.html +7 -5
- wagtail/admin/templatetags/wagtailadmin_tags.py +51 -22
- wagtail/admin/tests/pages/test_content_type_use_view.py +82 -2
- wagtail/admin/tests/pages/test_edit_page.py +70 -0
- wagtail/admin/tests/pages/test_explorer_view.py +65 -4
- wagtail/admin/tests/pages/test_page_search.py +8 -7
- wagtail/admin/tests/pages/test_page_usage.py +3 -1
- wagtail/admin/tests/pages/test_preview.py +208 -63
- wagtail/admin/tests/pages/test_reorder_page.py +91 -1
- wagtail/admin/tests/pages/test_view_draft.py +32 -1
- wagtail/admin/tests/pages/test_workflow_history.py +288 -7
- wagtail/admin/tests/test_account_management.py +23 -3
- wagtail/admin/tests/test_audit_log.py +24 -2
- wagtail/admin/tests/test_collections_views.py +17 -5
- wagtail/admin/tests/test_dashboard.py +1 -1
- wagtail/admin/tests/test_edit_handlers.py +3 -2
- wagtail/admin/tests/test_icon_sprite.py +4 -0
- wagtail/admin/tests/test_privacy.py +5 -19
- wagtail/admin/tests/test_reports_views.py +1 -1
- wagtail/admin/tests/test_site_summary.py +3 -3
- wagtail/admin/tests/test_templatetags.py +27 -3
- wagtail/admin/tests/test_upgrade_notification.py +71 -18
- wagtail/admin/tests/test_views.py +2 -2
- wagtail/admin/tests/test_views_generic.py +13 -0
- wagtail/admin/tests/test_widgets.py +3 -3
- wagtail/admin/tests/test_workflows.py +172 -27
- wagtail/admin/tests/tests.py +1 -1
- wagtail/admin/tests/viewsets/test_model_viewset.py +55 -3
- wagtail/admin/ui/side_panels.py +19 -0
- wagtail/admin/ui/sidebar.py +4 -3
- wagtail/admin/ui/tables/__init__.py +14 -1
- wagtail/admin/ui/tables/pages.py +6 -1
- wagtail/admin/urls/pages.py +10 -1
- wagtail/admin/urls/workflows.py +6 -1
- wagtail/admin/views/account.py +5 -1
- wagtail/admin/views/collections.py +0 -2
- wagtail/admin/views/generic/base.py +118 -1
- wagtail/admin/views/generic/history.py +158 -26
- wagtail/admin/views/generic/models.py +113 -99
- wagtail/admin/views/home.py +24 -9
- wagtail/admin/views/page_privacy.py +54 -30
- wagtail/admin/views/pages/history.py +11 -4
- wagtail/admin/views/pages/listing.py +76 -89
- wagtail/admin/views/pages/preview.py +1 -1
- wagtail/admin/views/pages/search.py +27 -71
- wagtail/admin/views/pages/usage.py +63 -24
- wagtail/admin/views/pages/utils.py +4 -3
- wagtail/admin/views/reports/base.py +0 -6
- wagtail/admin/views/workflows.py +67 -25
- wagtail/admin/viewsets/model.py +4 -3
- wagtail/admin/widgets/chooser.py +2 -1
- wagtail/api/v2/tests/test_pages.py +15 -2
- wagtail/blocks/field_block.py +15 -3
- wagtail/blocks/static_block.py +3 -0
- wagtail/contrib/forms/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/cy/LC_MESSAGES/django.po +29 -1
- wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +15 -11
- wagtail/contrib/forms/templates/wagtailforms/confirm_delete.html +1 -0
- wagtail/contrib/forms/templates/wagtailforms/index.html +1 -1
- wagtail/contrib/forms/templates/wagtailforms/index_results.html +1 -1
- wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +2 -1
- wagtail/contrib/forms/templates/wagtailforms/panels/form_responses_panel.html +2 -2
- wagtail/contrib/forms/tests/test_views.py +234 -35
- wagtail/contrib/forms/utils.py +8 -14
- wagtail/contrib/forms/views.py +72 -27
- wagtail/contrib/frontend_cache/backends/azure.py +1 -1
- wagtail/contrib/frontend_cache/backends/cloudfront.py +2 -2
- wagtail/contrib/frontend_cache/backends/dummy.py +11 -0
- wagtail/contrib/frontend_cache/tests.py +31 -37
- wagtail/contrib/frontend_cache/utils.py +12 -5
- wagtail/contrib/redirects/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/cy/LC_MESSAGES/django.po +16 -1
- wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +20 -24
- wagtail/contrib/redirects/models.py +16 -1
- wagtail/contrib/redirects/signal_handlers.py +18 -3
- wagtail/contrib/redirects/templates/wagtailredirects/add.html +5 -16
- wagtail/contrib/redirects/templates/wagtailredirects/import_summary.html +1 -1
- wagtail/contrib/redirects/tests/test_redirects.py +49 -6
- wagtail/contrib/redirects/tests/test_signal_handlers.py +42 -1
- wagtail/contrib/redirects/views.py +27 -3
- wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.po +60 -1
- wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +12 -18
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/add.html +5 -8
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/edit.html +15 -10
- wagtail/contrib/search_promotions/tests.py +13 -2
- wagtail/contrib/search_promotions/views.py +15 -3
- wagtail/contrib/settings/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/settings/locale/cy/LC_MESSAGES/django.po +6 -1
- wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +2 -10
- wagtail/contrib/settings/templates/wagtailsettings/edit.html +12 -45
- wagtail/contrib/simple_translation/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/simple_translation/locale/cy/LC_MESSAGES/django.po +13 -1
- wagtail/contrib/simple_translation/locale/dv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/simple_translation/locale/dv/LC_MESSAGES/django.po +3 -0
- wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +4 -4
- wagtail/contrib/simple_translation/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/simple_translation/locale/sl/LC_MESSAGES/django.po +5 -2
- wagtail/contrib/simple_translation/tests/test_views.py +51 -0
- wagtail/contrib/simple_translation/wagtail_hooks.py +16 -11
- wagtail/contrib/styleguide/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/styleguide/locale/cy/LC_MESSAGES/django.po +5 -1
- wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/table_block/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/table_block/locale/cy/LC_MESSAGES/django.po +27 -1
- wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/blocks.py +25 -0
- wagtail/contrib/typed_table_block/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/typed_table_block/locale/cy/LC_MESSAGES/django.po +12 -1
- wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
- wagtail/contrib/typed_table_block/tests.py +24 -1
- wagtail/coreutils.py +5 -11
- wagtail/documents/admin_urls.py +2 -2
- wagtail/documents/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/cy/LC_MESSAGES/django.po +10 -0
- wagtail/documents/locale/en/LC_MESSAGES/django.po +61 -76
- wagtail/documents/migrations/0014_alter_document_file_size.py +18 -0
- wagtail/documents/models.py +1 -1
- wagtail/documents/rich_text/__init__.py +1 -3
- wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
- wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_add_tags.html +5 -1
- wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_add_to_collection.html +5 -1
- wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_delete.html +11 -5
- wagtail/documents/templates/wagtaildocs/documents/add.html +13 -41
- wagtail/documents/templates/wagtaildocs/documents/edit.html +28 -56
- wagtail/documents/templates/wagtaildocs/documents/index.html +1 -4
- wagtail/documents/templates/wagtaildocs/homepage/site_summary_documents.html +2 -2
- wagtail/documents/templates/wagtaildocs/multiple/add.html +36 -41
- wagtail/documents/tests/test_admin_views.py +64 -6
- wagtail/documents/tests/test_site_summary.py +3 -3
- wagtail/documents/views/documents.py +103 -113
- wagtail/documents/views/multiple.py +19 -1
- wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/embeds/oembed_providers.py +9 -19
- wagtail/fields.py +43 -27
- wagtail/images/admin_urls.py +2 -2
- wagtail/images/blocks.py +230 -2
- wagtail/images/fields.py +17 -29
- wagtail/images/image_operations.py +1 -1
- wagtail/images/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/cy/LC_MESSAGES/django.po +128 -1
- wagtail/images/locale/en/LC_MESSAGES/django.po +119 -129
- wagtail/images/migrations/0025_alter_image_file_alter_rendition_file.py +36 -43
- wagtail/images/migrations/0027_image_description.py +20 -0
- wagtail/images/models.py +120 -45
- wagtail/images/rich_text/__init__.py +1 -3
- wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
- wagtail/images/static/wagtailimages/js/image-block.js +1 -0
- wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_add_tags.html +5 -1
- wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_add_to_collection.html +5 -1
- wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_delete.html +11 -5
- wagtail/images/templates/wagtailimages/chooser/results.html +2 -2
- wagtail/images/templates/wagtailimages/homepage/site_summary_images.html +2 -2
- wagtail/images/templates/wagtailimages/images/_file_field.html +2 -2
- wagtail/images/templates/wagtailimages/images/add.html +13 -31
- wagtail/images/templates/wagtailimages/images/edit.html +53 -82
- wagtail/images/templates/wagtailimages/images/index.html +1 -4
- wagtail/images/templates/wagtailimages/images/url_generator.html +1 -1
- wagtail/images/templates/wagtailimages/multiple/add.html +40 -47
- wagtail/images/templates/wagtailimages/widgets/image.html +5 -0
- wagtail/images/templates/wagtailimages/widgets/image_chooser.html +1 -1
- wagtail/images/tests/test_admin_views.py +70 -10
- wagtail/images/tests/test_blocks.py +367 -1
- wagtail/images/tests/test_image_operations.py +23 -0
- wagtail/images/tests/test_models.py +20 -0
- wagtail/images/tests/test_signal_handlers.py +99 -95
- wagtail/images/tests/test_site_summary.py +3 -3
- wagtail/images/tests/test_templatetags.py +11 -7
- wagtail/images/tests/tests.py +4 -0
- wagtail/images/views/images.py +103 -104
- wagtail/images/views/multiple.py +17 -1
- wagtail/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/cy/LC_MESSAGES/django.po +3 -0
- wagtail/locale/en/LC_MESSAGES/django.po +137 -125
- wagtail/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/sl/LC_MESSAGES/django.po +3 -0
- wagtail/locales/locale/en/LC_MESSAGES/django.po +8 -8
- wagtail/locales/views.py +4 -1
- wagtail/migrations/0089_log_entry_data_json_null_to_object.py +1 -7
- wagtail/models/__init__.py +122 -14
- wagtail/models/audit_log.py +1 -3
- wagtail/models/i18n.py +1 -2
- wagtail/project_template/Dockerfile +3 -3
- wagtail/project_template/requirements.txt +2 -2
- wagtail/query.py +17 -4
- wagtail/rich_text/__init__.py +2 -3
- wagtail/rich_text/pages.py +2 -4
- wagtail/rich_text/rewriters.py +2 -2
- wagtail/search/backends/database/mysql/query.py +3 -3
- wagtail/search/backends/database/sqlite/query.py +6 -6
- wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/sites/locale/en/LC_MESSAGES/django.po +6 -6
- wagtail/sites/views.py +0 -1
- wagtail/snippets/action_menu.py +14 -4
- wagtail/snippets/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/cy/LC_MESSAGES/django.po +73 -1
- wagtail/snippets/locale/en/LC_MESSAGES/django.po +27 -33
- wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
- wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
- wagtail/snippets/templates/wagtailsnippets/bulk_actions/confirm_bulk_delete.html +5 -3
- wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/locked.html +1 -1
- wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu.html +1 -11
- wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu_item.html +1 -6
- wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/publish.html +1 -1
- wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/save.html +1 -1
- wagtail/snippets/templates/wagtailsnippets/snippets/create.html +1 -18
- wagtail/snippets/templates/wagtailsnippets/snippets/edit.html +1 -14
- wagtail/snippets/templates/wagtailsnippets/snippets/index.html +2 -5
- wagtail/snippets/tests/test_preview.py +193 -61
- wagtail/snippets/tests/test_snippets.py +247 -38
- wagtail/snippets/tests/test_usage.py +5 -0
- wagtail/snippets/tests/test_viewset.py +25 -9
- wagtail/snippets/tests/test_workflows.py +232 -19
- wagtail/snippets/views/snippets.py +1 -10
- wagtail/test/numberformat.py +104 -0
- wagtail/test/settings.py +10 -0
- wagtail/test/settings_ui.py +2 -0
- wagtail/test/testapp/migrations/0010_alter_customimage_file_and_more.py +71 -78
- wagtail/test/testapp/migrations/0040_nocreatablesubpagetypespage_nosubpagetypespage.py +54 -0
- wagtail/test/testapp/migrations/0041_alter_jsonstreammodel_options.py +17 -0
- wagtail/test/testapp/migrations/0042_alter_customdocument_file_size_and_more.py +28 -0
- wagtail/test/testapp/migrations/0043_customimage_description.py +41 -0
- wagtail/test/testapp/migrations/0044_custompreviewsizesmodel_custompreviewsizespage.py +52 -0
- wagtail/test/testapp/migrations/0045_alter_streampage_body.py +52 -0
- wagtail/test/testapp/models.py +62 -4
- wagtail/test/testapp/rich_text.py +2 -2
- wagtail/test/testapp/templates/tests/form_page_landing.html +2 -1
- wagtail/test/testapp/urls.py +5 -0
- wagtail/test/testapp/views.py +5 -0
- wagtail/test/utils/page_tests.py +5 -5
- wagtail/test/utils/template_tests.py +2 -2
- wagtail/tests/test_blocks.py +15 -0
- wagtail/tests/test_page_permissions.py +109 -0
- wagtail/tests/test_revision_model.py +27 -0
- wagtail/tests/test_signals.py +21 -2
- wagtail/tests/test_tests.py +30 -0
- wagtail/users/locale/cy/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/cy/LC_MESSAGES/django.po +118 -1
- wagtail/users/locale/dv/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/dv/LC_MESSAGES/django.po +3 -0
- wagtail/users/locale/en/LC_MESSAGES/django.po +89 -113
- wagtail/users/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/sl/LC_MESSAGES/django.po +3 -0
- wagtail/users/migrations/0014_userprofile_contrast.py +23 -0
- wagtail/users/models.py +11 -0
- wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_assign_role.html +5 -1
- wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_delete.html +5 -1
- wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_set_active_state.html +5 -1
- wagtail/users/templates/wagtailusers/groups/create.html +19 -17
- wagtail/users/templates/wagtailusers/groups/edit.html +2 -40
- wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +1 -62
- wagtail/users/templates/wagtailusers/groups/includes/page_permissions_formset.html +0 -1
- wagtail/users/templates/wagtailusers/users/create.html +46 -50
- wagtail/users/templates/wagtailusers/users/edit.html +45 -62
- wagtail/users/templates/wagtailusers/users/index.html +1 -4
- wagtail/users/templatetags/wagtailusers_tags.py +1 -5
- wagtail/users/tests/test_admin_views.py +85 -66
- wagtail/users/views/groups.py +4 -1
- wagtail/users/views/users.py +2 -0
- {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/METADATA +6 -6
- {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/RECORD +378 -367
- wagtail/admin/static/wagtailadmin/js/preview-panel.js +0 -2
- wagtail/admin/static/wagtailadmin/js/preview-panel.js.LICENSE.txt +0 -11
- wagtail/admin/templates/wagtailadmin/generic/history_results.html +0 -1
- wagtail/admin/templates/wagtailadmin/page_privacy/ancestor_privacy.html +0 -3
- wagtail/admin/templates/wagtailadmin/pages/usage_results.html +0 -6
- wagtail/admin/templates/wagtailadmin/shared/workflow_history/index.html +0 -17
- wagtail/admin/templates/wagtailadmin/shared/workflow_history/results.html +0 -17
- wagtail/admin/templates/wagtailadmin/workflows/usage.html +0 -44
- {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/LICENSE +0 -0
- {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/WHEEL +0 -0
- {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/entry_points.txt +0 -0
- {wagtail-6.2.2.dist-info → wagtail-6.3rc1.dist-info}/top_level.txt +0 -0
wagtail/images/models.py
CHANGED
|
@@ -7,11 +7,12 @@ import logging
|
|
|
7
7
|
import os.path
|
|
8
8
|
import re
|
|
9
9
|
import time
|
|
10
|
-
from collections import OrderedDict, defaultdict
|
|
10
|
+
from collections import OrderedDict, defaultdict, namedtuple
|
|
11
|
+
from collections.abc import Iterable
|
|
11
12
|
from contextlib import contextmanager
|
|
12
13
|
from io import BytesIO
|
|
13
14
|
from tempfile import SpooledTemporaryFile
|
|
14
|
-
from typing import Any
|
|
15
|
+
from typing import Any
|
|
15
16
|
|
|
16
17
|
import willow
|
|
17
18
|
from django.apps import apps
|
|
@@ -38,7 +39,6 @@ from wagtail.images.exceptions import (
|
|
|
38
39
|
InvalidFilterSpecError,
|
|
39
40
|
UnknownOutputImageFormatError,
|
|
40
41
|
)
|
|
41
|
-
from wagtail.images.fields import image_format_name_to_content_type
|
|
42
42
|
from wagtail.images.image_operations import (
|
|
43
43
|
FilterOperation,
|
|
44
44
|
FormatOperation,
|
|
@@ -62,6 +62,7 @@ IMAGE_FORMAT_EXTENSIONS = {
|
|
|
62
62
|
"webp": ".webp",
|
|
63
63
|
"svg": ".svg",
|
|
64
64
|
"ico": ".ico",
|
|
65
|
+
"heic": ".heic",
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
|
|
@@ -262,6 +263,12 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
262
263
|
width_field="width",
|
|
263
264
|
height_field="height",
|
|
264
265
|
)
|
|
266
|
+
description = models.CharField(
|
|
267
|
+
blank=True,
|
|
268
|
+
max_length=255,
|
|
269
|
+
verbose_name=_("description"),
|
|
270
|
+
default="",
|
|
271
|
+
)
|
|
265
272
|
width = models.IntegerField(verbose_name=_("width"), editable=False)
|
|
266
273
|
height = models.IntegerField(verbose_name=_("height"), editable=False)
|
|
267
274
|
created_at = models.DateTimeField(
|
|
@@ -292,6 +299,11 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
292
299
|
|
|
293
300
|
objects = ImageQuerySet.as_manager()
|
|
294
301
|
|
|
302
|
+
def __init__(self, *args, **kwargs):
|
|
303
|
+
super().__init__(*args, **kwargs)
|
|
304
|
+
self.decorative = False
|
|
305
|
+
self.contextual_alt_text = None
|
|
306
|
+
|
|
295
307
|
def _set_file_hash(self):
|
|
296
308
|
with self.open_file() as f:
|
|
297
309
|
self.file_hash = hash_filelike(f)
|
|
@@ -365,6 +377,36 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
365
377
|
def __str__(self):
|
|
366
378
|
return self.title
|
|
367
379
|
|
|
380
|
+
def __eq__(self, other):
|
|
381
|
+
"""
|
|
382
|
+
Customise the definition of equality so that two Image instances referring to the same
|
|
383
|
+
image but different contextual alt text or decorative status are considered different.
|
|
384
|
+
All other aspects are copied from Django's base `Model` implementation.
|
|
385
|
+
"""
|
|
386
|
+
if not isinstance(other, models.Model):
|
|
387
|
+
return NotImplemented
|
|
388
|
+
|
|
389
|
+
if self._meta.concrete_model != other._meta.concrete_model:
|
|
390
|
+
return False
|
|
391
|
+
|
|
392
|
+
my_pk = self.pk
|
|
393
|
+
if my_pk is None:
|
|
394
|
+
return self is other
|
|
395
|
+
|
|
396
|
+
return (
|
|
397
|
+
my_pk == other.pk
|
|
398
|
+
and other.contextual_alt_text == self.contextual_alt_text
|
|
399
|
+
and other.decorative == self.decorative
|
|
400
|
+
)
|
|
401
|
+
|
|
402
|
+
def __hash__(self):
|
|
403
|
+
"""
|
|
404
|
+
Match the semantics of the custom equality definition.
|
|
405
|
+
"""
|
|
406
|
+
if self.pk is None:
|
|
407
|
+
raise TypeError("Model instances without primary key value are unhashable")
|
|
408
|
+
return hash((self.pk, self.contextual_alt_text, self.decorative))
|
|
409
|
+
|
|
368
410
|
def get_rect(self):
|
|
369
411
|
return Rect(0, 0, self.width, self.height)
|
|
370
412
|
|
|
@@ -442,12 +484,12 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
442
484
|
"""Get the Rendition model for this Image model"""
|
|
443
485
|
return cls.renditions.rel.related_model
|
|
444
486
|
|
|
445
|
-
def _get_prefetched_renditions(self) ->
|
|
487
|
+
def _get_prefetched_renditions(self) -> Iterable[AbstractRendition] | None:
|
|
446
488
|
if "renditions" in getattr(self, "_prefetched_objects_cache", {}):
|
|
447
489
|
return self.renditions.all()
|
|
448
490
|
return getattr(self, "prefetched_renditions", None)
|
|
449
491
|
|
|
450
|
-
def _add_to_prefetched_renditions(self, rendition:
|
|
492
|
+
def _add_to_prefetched_renditions(self, rendition: AbstractRendition) -> None:
|
|
451
493
|
# Reuse this rendition if requested again from this object
|
|
452
494
|
try:
|
|
453
495
|
self._prefetched_objects_cache["renditions"]._result_cache.append(rendition)
|
|
@@ -458,7 +500,7 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
458
500
|
except AttributeError:
|
|
459
501
|
pass
|
|
460
502
|
|
|
461
|
-
def get_rendition(self, filter:
|
|
503
|
+
def get_rendition(self, filter: Filter | str) -> AbstractRendition:
|
|
462
504
|
"""
|
|
463
505
|
Returns a ``Rendition`` instance with a ``file`` field value (an
|
|
464
506
|
image) reflecting the supplied ``filter`` value and focal point values
|
|
@@ -486,7 +528,7 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
486
528
|
|
|
487
529
|
return rendition
|
|
488
530
|
|
|
489
|
-
def find_existing_rendition(self, filter:
|
|
531
|
+
def find_existing_rendition(self, filter: Filter) -> AbstractRendition:
|
|
490
532
|
"""
|
|
491
533
|
Returns an existing ``Rendition`` instance with a ``file`` field value
|
|
492
534
|
(an image) reflecting the supplied ``filter`` value and focal point
|
|
@@ -505,7 +547,7 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
505
547
|
except KeyError:
|
|
506
548
|
raise Rendition.DoesNotExist
|
|
507
549
|
|
|
508
|
-
def create_rendition(self, filter:
|
|
550
|
+
def create_rendition(self, filter: Filter) -> AbstractRendition:
|
|
509
551
|
"""
|
|
510
552
|
Creates and returns a ``Rendition`` instance with a ``file`` field
|
|
511
553
|
value (an image) reflecting the supplied ``filter`` value and focal
|
|
@@ -526,9 +568,7 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
526
568
|
)
|
|
527
569
|
return rendition
|
|
528
570
|
|
|
529
|
-
def get_renditions(
|
|
530
|
-
self, *filters: Union["Filter", str]
|
|
531
|
-
) -> Dict[str, "AbstractRendition"]:
|
|
571
|
+
def get_renditions(self, *filters: Filter | str) -> dict[str, AbstractRendition]:
|
|
532
572
|
"""
|
|
533
573
|
Returns a ``dict`` of ``Rendition`` instances with image files reflecting
|
|
534
574
|
the supplied ``filters``, keyed by filter spec patterns.
|
|
@@ -566,8 +606,8 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
566
606
|
return {filter.spec: renditions[filter] for filter in filters}
|
|
567
607
|
|
|
568
608
|
def find_existing_renditions(
|
|
569
|
-
self, *filters:
|
|
570
|
-
) ->
|
|
609
|
+
self, *filters: Filter
|
|
610
|
+
) -> dict[Filter, AbstractRendition]:
|
|
571
611
|
"""
|
|
572
612
|
Returns a dictionary of existing ``Rendition`` instances with ``file``
|
|
573
613
|
values (images) reflecting the supplied ``filters`` and the focal point
|
|
@@ -578,8 +618,8 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
578
618
|
created before, the return value will be an empty dict.
|
|
579
619
|
"""
|
|
580
620
|
Rendition = self.get_rendition_model()
|
|
581
|
-
filters_by_spec:
|
|
582
|
-
found:
|
|
621
|
+
filters_by_spec: dict[str, Filter] = {f.spec: f for f in filters}
|
|
622
|
+
found: dict[Filter, AbstractRendition] = {}
|
|
583
623
|
|
|
584
624
|
# Interrogate prefetched values first (where available)
|
|
585
625
|
prefetched_renditions = self._get_prefetched_renditions()
|
|
@@ -589,7 +629,7 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
589
629
|
# prefetched value, and further cache/database lookups are avoided.
|
|
590
630
|
|
|
591
631
|
# group renditions by the filters of interest
|
|
592
|
-
potential_matches:
|
|
632
|
+
potential_matches: dict[Filter, list[AbstractRendition]] = defaultdict(list)
|
|
593
633
|
for rendition in prefetched_renditions:
|
|
594
634
|
try:
|
|
595
635
|
filter = filters_by_spec[rendition.filter_spec]
|
|
@@ -621,6 +661,9 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
621
661
|
]
|
|
622
662
|
for rendition in Rendition.cache_backend.get_many(cache_keys).values():
|
|
623
663
|
filter = filters_by_spec[rendition.filter_spec]
|
|
664
|
+
# The retrieved rendition needs to be associated with the current image instance, so that any
|
|
665
|
+
# locally-set properties such as contextual_alt_text are respected
|
|
666
|
+
rendition.image = self
|
|
624
667
|
found[filter] = rendition
|
|
625
668
|
|
|
626
669
|
# For items not found in the cache, look in the database
|
|
@@ -637,9 +680,7 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
637
680
|
found[filter] = rendition
|
|
638
681
|
return found
|
|
639
682
|
|
|
640
|
-
def create_renditions(
|
|
641
|
-
self, *filters: "Filter"
|
|
642
|
-
) -> Dict["Filter", "AbstractRendition"]:
|
|
683
|
+
def create_renditions(self, *filters: Filter) -> dict[Filter, AbstractRendition]:
|
|
643
684
|
"""
|
|
644
685
|
Creates multiple ``Rendition`` instances with image files reflecting the supplied
|
|
645
686
|
``filters``, and returns them as a ``dict`` keyed by the relevant ``Filter`` instance.
|
|
@@ -664,8 +705,8 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
664
705
|
filter = filters[0]
|
|
665
706
|
return {filter: self.create_rendition(filter)}
|
|
666
707
|
|
|
667
|
-
return_value:
|
|
668
|
-
filter_map:
|
|
708
|
+
return_value: dict[Filter, AbstractRendition] = {}
|
|
709
|
+
filter_map: dict[str, Filter] = {f.spec: f for f in filters}
|
|
669
710
|
|
|
670
711
|
# Read file contents into memory
|
|
671
712
|
with self.open_file() as file:
|
|
@@ -688,7 +729,7 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
688
729
|
# identical renditions in the meantime, we should find them to avoid clashes.
|
|
689
730
|
# NB: Clashes can still occur, because there is no get_or_create() equivalent
|
|
690
731
|
# for multiple objects. However, this will reduce that risk considerably.
|
|
691
|
-
files_for_deletion:
|
|
732
|
+
files_for_deletion: list[File] = []
|
|
692
733
|
|
|
693
734
|
# Assemble Q() to identify potential clashes
|
|
694
735
|
lookup_q = Q()
|
|
@@ -724,8 +765,8 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
724
765
|
return return_value
|
|
725
766
|
|
|
726
767
|
def generate_rendition_instance(
|
|
727
|
-
self, filter:
|
|
728
|
-
) ->
|
|
768
|
+
self, filter: Filter, source: BytesIO
|
|
769
|
+
) -> AbstractRendition:
|
|
729
770
|
"""
|
|
730
771
|
Use the supplied ``source`` image to create and return an
|
|
731
772
|
**unsaved** ``Rendition`` instance, with a ``file`` value reflecting
|
|
@@ -740,7 +781,7 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
740
781
|
),
|
|
741
782
|
)
|
|
742
783
|
|
|
743
|
-
def generate_rendition_file(self, filter:
|
|
784
|
+
def generate_rendition_file(self, filter: Filter, *, source: File = None) -> File:
|
|
744
785
|
"""
|
|
745
786
|
Generates an in-memory image matching the supplied ``filter`` value
|
|
746
787
|
and focal point value from this object, wraps it in a ``File`` object
|
|
@@ -827,9 +868,9 @@ class AbstractImage(ImageFileMixin, CollectionMember, index.Indexed, models.Mode
|
|
|
827
868
|
@property
|
|
828
869
|
def default_alt_text(self):
|
|
829
870
|
# by default the alt text field (used in rich text insertion) is populated
|
|
830
|
-
# from the
|
|
831
|
-
# override this
|
|
832
|
-
return self.title
|
|
871
|
+
# from the description. In the absence of that, it is populated from the title.
|
|
872
|
+
# Subclasses might provide a separate alt field, and override this
|
|
873
|
+
return getattr(self, "description", None) or self.title
|
|
833
874
|
|
|
834
875
|
def is_editable_by_user(self, user):
|
|
835
876
|
from wagtail.images.permissions import permission_policy
|
|
@@ -844,6 +885,7 @@ class Image(AbstractImage):
|
|
|
844
885
|
admin_form_fields = (
|
|
845
886
|
"title",
|
|
846
887
|
"file",
|
|
888
|
+
"description",
|
|
847
889
|
"collection",
|
|
848
890
|
"tags",
|
|
849
891
|
"focal_point_x",
|
|
@@ -877,7 +919,7 @@ class Filter:
|
|
|
877
919
|
self.spec = spec
|
|
878
920
|
|
|
879
921
|
@classmethod
|
|
880
|
-
def expand_spec(self, spec:
|
|
922
|
+
def expand_spec(self, spec: str | Iterable[str]) -> list[str]:
|
|
881
923
|
"""
|
|
882
924
|
Converts a spec pattern with brace-expansions, into a list of spec patterns.
|
|
883
925
|
For example, "width-{100,200}" becomes ["width-100", "width-200"].
|
|
@@ -995,11 +1037,12 @@ class Filter:
|
|
|
995
1037
|
# Developer specified an output format
|
|
996
1038
|
output_format = env["output-format"]
|
|
997
1039
|
else:
|
|
998
|
-
# Convert bmp and webp to png by default
|
|
1040
|
+
# Convert avif, bmp and webp to png, and heic to jpg, by default
|
|
999
1041
|
default_conversions = {
|
|
1000
1042
|
"avif": "png",
|
|
1001
1043
|
"bmp": "png",
|
|
1002
1044
|
"webp": "png",
|
|
1045
|
+
"heic": "jpeg",
|
|
1003
1046
|
}
|
|
1004
1047
|
|
|
1005
1048
|
# Convert unanimated GIFs to PNG as well
|
|
@@ -1058,6 +1101,20 @@ class Filter:
|
|
|
1058
1101
|
else:
|
|
1059
1102
|
quality = getattr(settings, "WAGTAILIMAGES_AVIF_QUALITY", 80)
|
|
1060
1103
|
return willow.save_as_avif(output, quality=quality)
|
|
1104
|
+
elif output_format == "heic":
|
|
1105
|
+
# Allow changing of HEIC compression quality. Safari is the only browser that supports HEIC,
|
|
1106
|
+
# so there is little value in outputting it - for that reason, we make it work if someone
|
|
1107
|
+
# explicitly requests it, but these settings are not documented.
|
|
1108
|
+
if (
|
|
1109
|
+
"output-format-options" in env
|
|
1110
|
+
and "lossless" in env["output-format-options"]
|
|
1111
|
+
):
|
|
1112
|
+
return willow.save_as_heic(output, lossless=True)
|
|
1113
|
+
elif "heic-quality" in env:
|
|
1114
|
+
quality = env["heic-quality"]
|
|
1115
|
+
else:
|
|
1116
|
+
quality = getattr(settings, "WAGTAILIMAGES_HEIC_QUALITY", 80)
|
|
1117
|
+
return willow.save_as_heic(output, quality=quality)
|
|
1061
1118
|
elif output_format == "svg":
|
|
1062
1119
|
return willow.save_as_svg(output)
|
|
1063
1120
|
elif output_format == "ico":
|
|
@@ -1092,14 +1149,14 @@ class ResponsiveImage:
|
|
|
1092
1149
|
|
|
1093
1150
|
def __init__(
|
|
1094
1151
|
self,
|
|
1095
|
-
renditions:
|
|
1096
|
-
attrs:
|
|
1152
|
+
renditions: dict[str, AbstractRendition],
|
|
1153
|
+
attrs: dict[str, Any] | None = None,
|
|
1097
1154
|
):
|
|
1098
1155
|
self.renditions = list(renditions.values())
|
|
1099
1156
|
self.attrs = attrs
|
|
1100
1157
|
|
|
1101
1158
|
@classmethod
|
|
1102
|
-
def get_width_srcset(cls, renditions_list:
|
|
1159
|
+
def get_width_srcset(cls, renditions_list: list[AbstractRendition]):
|
|
1103
1160
|
if len(renditions_list) == 1:
|
|
1104
1161
|
# No point in using width descriptors if there is a single image.
|
|
1105
1162
|
return renditions_list[0].url
|
|
@@ -1122,32 +1179,41 @@ class ResponsiveImage:
|
|
|
1122
1179
|
def __bool__(self):
|
|
1123
1180
|
return bool(self.renditions)
|
|
1124
1181
|
|
|
1125
|
-
def __eq__(self, other:
|
|
1182
|
+
def __eq__(self, other: ResponsiveImage):
|
|
1126
1183
|
if isinstance(other, ResponsiveImage):
|
|
1127
1184
|
return self.renditions == other.renditions and self.attrs == other.attrs
|
|
1128
1185
|
return False
|
|
1129
1186
|
|
|
1130
1187
|
|
|
1188
|
+
FileFormat = namedtuple("FileFormat", ["name", "mime_type"])
|
|
1189
|
+
|
|
1190
|
+
|
|
1131
1191
|
class Picture(ResponsiveImage):
|
|
1132
1192
|
# Keep this separate from FormatOperation.supported_formats,
|
|
1133
1193
|
# as the order our formats are defined in is essential for the picture tag.
|
|
1134
1194
|
# Defines the order of <source> elements in the tag when format operations
|
|
1135
1195
|
# are in use, and the priority order to identify the "fallback" format.
|
|
1136
1196
|
# The browser will pick the first supported format in this list.
|
|
1137
|
-
source_format_order = [
|
|
1197
|
+
source_format_order = [
|
|
1198
|
+
FileFormat("avif", "image/avif"),
|
|
1199
|
+
FileFormat("webp", "image/webp"),
|
|
1200
|
+
FileFormat("jpeg", "image/jpeg"),
|
|
1201
|
+
FileFormat("png", "image/png"),
|
|
1202
|
+
FileFormat("gif", "image/gif"),
|
|
1203
|
+
]
|
|
1138
1204
|
|
|
1139
1205
|
def __init__(
|
|
1140
1206
|
self,
|
|
1141
|
-
renditions:
|
|
1142
|
-
attrs:
|
|
1207
|
+
renditions: dict[str, AbstractRendition],
|
|
1208
|
+
attrs: dict[str, Any] | None = None,
|
|
1143
1209
|
):
|
|
1144
1210
|
super().__init__(renditions, attrs)
|
|
1145
1211
|
# Store renditions grouped by format separately for access from templates.
|
|
1146
1212
|
self.formats = self.get_formats(renditions)
|
|
1147
1213
|
|
|
1148
1214
|
def get_formats(
|
|
1149
|
-
self, renditions:
|
|
1150
|
-
) ->
|
|
1215
|
+
self, renditions: dict[str, AbstractRendition]
|
|
1216
|
+
) -> dict[str, list[AbstractRendition]]:
|
|
1151
1217
|
"""
|
|
1152
1218
|
Group renditions by the format they are for, if any.
|
|
1153
1219
|
If there is only one format, no grouping is required.
|
|
@@ -1167,8 +1233,8 @@ class Picture(ResponsiveImage):
|
|
|
1167
1233
|
|
|
1168
1234
|
def get_fallback_format(self):
|
|
1169
1235
|
for fmt in reversed(self.source_format_order):
|
|
1170
|
-
if fmt in self.formats:
|
|
1171
|
-
return fmt
|
|
1236
|
+
if fmt.name in self.formats:
|
|
1237
|
+
return fmt.name
|
|
1172
1238
|
|
|
1173
1239
|
def __html__(self):
|
|
1174
1240
|
# If there aren’t multiple formats, render a vanilla img tag with srcset.
|
|
@@ -1184,9 +1250,9 @@ class Picture(ResponsiveImage):
|
|
|
1184
1250
|
sources = []
|
|
1185
1251
|
|
|
1186
1252
|
for fmt in self.source_format_order:
|
|
1187
|
-
if fmt != fallback_format and fmt in self.formats:
|
|
1188
|
-
srcset = self.get_width_srcset(self.formats[fmt])
|
|
1189
|
-
mime =
|
|
1253
|
+
if fmt.name != fallback_format and fmt.name in self.formats:
|
|
1254
|
+
srcset = self.get_width_srcset(self.formats[fmt.name])
|
|
1255
|
+
mime = fmt.mime_type
|
|
1190
1256
|
sources.append(f'<source srcset="{srcset}" {sizes}type="{mime}">')
|
|
1191
1257
|
|
|
1192
1258
|
if len(fallback_renditions) > 1:
|
|
@@ -1221,7 +1287,16 @@ class AbstractRendition(ImageFileMixin, models.Model):
|
|
|
1221
1287
|
|
|
1222
1288
|
@property
|
|
1223
1289
|
def alt(self):
|
|
1224
|
-
|
|
1290
|
+
# 'decorative' and 'contextual_alt_text' exist only for ImageBlock
|
|
1291
|
+
if hasattr(self.image, "decorative") and self.image.decorative:
|
|
1292
|
+
return ""
|
|
1293
|
+
elif (
|
|
1294
|
+
hasattr(self.image, "contextual_alt_text")
|
|
1295
|
+
and self.image.contextual_alt_text
|
|
1296
|
+
):
|
|
1297
|
+
return self.image.contextual_alt_text
|
|
1298
|
+
else:
|
|
1299
|
+
return self.image.default_alt_text
|
|
1225
1300
|
|
|
1226
1301
|
@property
|
|
1227
1302
|
def attrs(self):
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
from typing import List
|
|
2
|
-
|
|
3
1
|
from wagtail.images import get_image_model
|
|
4
2
|
from wagtail.images.formats import get_image_format
|
|
5
3
|
from wagtail.rich_text import EmbedHandler
|
|
@@ -19,7 +17,7 @@ class ImageEmbedHandler(EmbedHandler):
|
|
|
19
17
|
return cls.expand_db_attributes_many([attrs])[0]
|
|
20
18
|
|
|
21
19
|
@classmethod
|
|
22
|
-
def expand_db_attributes_many(cls, attrs_list:
|
|
20
|
+
def expand_db_attributes_many(cls, attrs_list: list[dict]) -> list[str]:
|
|
23
21
|
"""
|
|
24
22
|
Given a dict of attributes from the <embed> tag, return the real HTML
|
|
25
23
|
representation for use on the front-end.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
$((function(){$(document).on("drop dragover",(function(e){e.preventDefault()})),$("#fileupload").fileupload({dataType:"html",sequentialUploads:!0,dropZone:$(".drop-zone"),
|
|
1
|
+
$((function(){$(document).on("drop dragover",(function(e){e.preventDefault()})),$("#fileupload").fileupload({dataType:"html",sequentialUploads:!0,dropZone:$(".drop-zone"),previewMinWidth:150,previewMaxWidth:150,previewMinHeight:150,previewMaxHeight:150,add:function(e,t){var a=$(this),o=a.data("blueimp-fileupload")||a.data("fileupload"),s=$($("#upload-list-item").html()).addClass("upload-uploading"),i=o.options;$("#upload-list").append(s),t.context=s,t.process((function(){return a.fileupload("process",t)})).always((function(){t.context.removeClass("processing"),t.context.find(".left").each((function(e,a){$(a).append(escapeHtml(t.files[e].name))})),t.context.find(".preview .thumb").each((function(e,a){$(a).find(".icon").remove(),$(a).append(t.files[e].preview)}))})).done((function(){t.context.find(".start").prop("disabled",!1),!1!==o._trigger("added",e,t)&&(i.autoUpload||t.autoUpload)&&!1!==t.autoUpload&&t.submit()})).fail((function(){t.files.error&&t.context.each((function(e){var a=t.files[e].error;a&&$(this).find(".error_messages").html(a)}))}))},processfail:function(e,t){$(t.context).removeClass("upload-uploading").addClass("upload-failure")},progress:function(e,t){if(e.isDefaultPrevented())return!1;var a=Math.floor(t.loaded/t.total*100);t.context.each((function(){$(this).find(".progress").addClass("active").attr("aria-valuenow",a).find(".bar").css("width",a+"%").html(a+"%")}))},progressall:function(e,t){var a=parseInt(t.loaded/t.total*100,10);$("#overall-progress").addClass("active").attr("aria-valuenow",a).find(".bar").css("width",a+"%").html(a+"%"),a>=100&&$("#overall-progress").removeClass("active").find(".bar").css("width","0%")},formData:function(e){var t=this.files[0].name,a={title:t.replace(/\.[^.]+$/,"")};return e.get(0).dispatchEvent(new CustomEvent("wagtail:images-upload",{bubbles:!0,cancelable:!0,detail:{data:a,filename:t,maxTitleLength:this.maxTitleLength}}))?e.serializeArray().concat({name:"title",value:a.title}):e.serializeArray()},done:function(e,t){var a=$(t.context),o=JSON.parse(t.result);o.success?o.duplicate?(a.addClass("upload-duplicate"),$(".right",a).append(o.confirm_duplicate_upload),$(".confirm-duplicate-upload",a).on("click",".confirm-upload",(function(e){e.preventDefault(),$(this).closest(".confirm-duplicate-upload").remove(),$(".right",a).append(o.form)}))):(a.addClass("upload-success"),$(".right",a).append(o.form)):(a.addClass("upload-failure"),$(".right .error_messages",a).append(o.error_message))},fail:function(e,t){var a=$(t.context),o=$(".server-error",a);$(".error-text",o).text(t.errorThrown),$(".error-code",o).text(t.jqXHR.status),a.addClass("upload-server-error")},always:function(e,t){$(t.context).removeClass("upload-uploading").addClass("upload-complete")}}),$("#upload-list").on("submit","form",(function(e){var t=$(this),a=new FormData(this),o=t.closest("#upload-list > li");e.preventDefault(),$.ajax({contentType:!1,data:a,processData:!1,type:"POST",url:this.action}).done((function(e){if(e.success){var a=$(".status-msg.update-success").first().text();document.dispatchEvent(new CustomEvent("w-messages:add",{detail:{clear:!0,text:a,type:"success"}})),o.slideUp((function(){$(this).remove()}))}else t.replaceWith(e.form)}))})),$("#upload-list").on("click",".delete",(function(e){var t=$(this).closest("form"),a=t.closest("#upload-list > li");e.preventDefault();var o=$('input[name="csrfmiddlewaretoken"]',t).val();$.post(this.href,{csrfmiddlewaretoken:o},(function(e){e.success&&a.slideUp((function(){$(this).remove()}))}))}))}));
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
(()=>{class e extends window.wagtailStreamField.blocks.StructBlockDefinition{render(e,t,d,i){const n=super.render(e,t,d,i),r=document.getElementById(`${t}-alt_text`),a=document.getElementById(`${t}-decorative`),l=()=>{a.checked?r.setAttribute("disabled",!0):r.removeAttribute("disabled")};return l(),a.addEventListener("change",l),n}}window.telepath.register("wagtail.images.blocks.ImageBlock",e)})();
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{% extends 'wagtailadmin/bulk_actions/confirmation/base.html' %}
|
|
2
2
|
{% load i18n %}
|
|
3
3
|
{% load wagtailimages_tags wagtailadmin_tags %}
|
|
4
|
-
{% block titletag %}
|
|
4
|
+
{% block titletag %}
|
|
5
|
+
{% with counter_val=items|length %}
|
|
6
|
+
{% blocktrans trimmed with counter=counter_val|intcomma count counter_val=counter_val %}Add tags to 1 image {% plural %}Add tags to {{ counter }} images{% endblocktrans %}
|
|
7
|
+
{% endwith %}
|
|
8
|
+
{% endblock %}
|
|
5
9
|
|
|
6
10
|
{% block header %}
|
|
7
11
|
{% trans "Add tags to images" as add_str %}
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
{% extends 'wagtailadmin/bulk_actions/confirmation/base.html' %}
|
|
2
2
|
{% load i18n %}
|
|
3
3
|
{% load wagtailimages_tags wagtailadmin_tags %}
|
|
4
|
-
{% block titletag %}
|
|
4
|
+
{% block titletag %}
|
|
5
|
+
{% with counter_val=items|length %}
|
|
6
|
+
{% blocktrans trimmed with counter=counter_val|intcomma count counter_val=counter_val %}Add 1 image to new collection {% plural %}Add {{ counter }} images to new collection{% endblocktrans %}
|
|
7
|
+
{% endwith %}
|
|
8
|
+
{% endblock %}
|
|
5
9
|
|
|
6
10
|
{% block header %}
|
|
7
11
|
{% trans "Add images to collection" as add_str %}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
{% extends 'wagtailadmin/bulk_actions/confirmation/base.html' %}
|
|
2
2
|
{% load wagtailadmin_tags i18n %}
|
|
3
|
-
{% block titletag %}
|
|
3
|
+
{% block titletag %}
|
|
4
|
+
{% with counter_val=items|length %}
|
|
5
|
+
{% blocktrans trimmed with counter=counter_val|intcomma count counter_val=counter_val %}Delete 1 image{% plural %}Delete {{ counter }} images{% endblocktrans %}
|
|
6
|
+
{% endwith %}
|
|
7
|
+
{% endblock %}
|
|
4
8
|
|
|
5
9
|
{% block header %}
|
|
6
10
|
{% trans "Delete images" as del_str %}
|
|
@@ -18,10 +22,12 @@
|
|
|
18
22
|
</p>
|
|
19
23
|
<ul>
|
|
20
24
|
{% for image in items %}
|
|
21
|
-
|
|
22
|
-
<
|
|
23
|
-
|
|
24
|
-
|
|
25
|
+
{% with usage_count_val=image.item.get_usage.count %}
|
|
26
|
+
<li>
|
|
27
|
+
<a href="{% url 'wagtailimages:edit' image.item.id %}" target="_blank" rel="noreferrer">{{image.item.title}}</a>
|
|
28
|
+
(<a href="{{ image.item.usage_url }}">{% blocktrans trimmed with usage_count=usage_count_val|intcomma count usage_count_val=usage_count_val %}Used {{ usage_count }} time{% plural %}Used {{ usage_count }} times{% endblocktrans %}</a>)
|
|
29
|
+
</li>
|
|
30
|
+
{% endwith %}
|
|
25
31
|
{% endfor %}
|
|
26
32
|
</ul>
|
|
27
33
|
{% endif %}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{% extends "wagtailadmin/generic/chooser/results.html" %}
|
|
2
|
-
{% load i18n wagtailimages_tags wagtailadmin_tags
|
|
2
|
+
{% load i18n wagtailimages_tags wagtailadmin_tags %}
|
|
3
3
|
|
|
4
4
|
{% block listing_title %}<h2>{% trans "Latest images" %}</h2>{% endblock %}
|
|
5
5
|
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
<label title="Select {% if collections %}{{ image.collection.name }} » {% endif %}{{ image.title }}">
|
|
12
12
|
<div class="image">{% image image max-165x165 class="show-transparency" %}</div>
|
|
13
13
|
<h3>
|
|
14
|
-
<input type="checkbox" data-multiple-choice-select name="id" value="{{ image.id
|
|
14
|
+
<input type="checkbox" data-multiple-choice-select name="id" value="{{ image.id }}" title="{% blocktrans trimmed with title=image.title %}Select {{ title }}{% endblocktrans %}">
|
|
15
15
|
{{ image.title|ellipsistrim:60 }}
|
|
16
16
|
</h3>
|
|
17
17
|
</label>
|
|
@@ -4,9 +4,9 @@
|
|
|
4
4
|
{% icon name="image" %}
|
|
5
5
|
<a href="{% url 'wagtailimages:index' %}">
|
|
6
6
|
{% blocktrans trimmed count counter=total_images with total_images|intcomma as total %}
|
|
7
|
-
|
|
7
|
+
{{ total }} Image <span class="w-sr-only">created in {{ site_name }}</span>
|
|
8
8
|
{% plural %}
|
|
9
|
-
|
|
9
|
+
{{ total }} Images <span class="w-sr-only">created in {{ site_name }}</span>
|
|
10
10
|
{% endblocktrans %}
|
|
11
11
|
</a>
|
|
12
12
|
</li>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
{% load i18n wagtailadmin_tags wagtailimages_tags %}
|
|
1
|
+
{% load i18n wagtailadmin_tags wagtailimages_tags l10n %}
|
|
2
2
|
{% rawformattedfield field=field %}
|
|
3
3
|
{% image image original as original_image %}
|
|
4
4
|
|
|
5
|
-
<a href="{{ original_image.url }}">{% icon name="image" classname="default" %}{{ image.filename }}</a> ({{ original_image.width }}x{{ original_image.height}})<br /><br />
|
|
5
|
+
<a href="{{ original_image.url }}">{% icon name="image" classname="default" %}{{ image.filename }}</a> ({{ original_image.width|unlocalize }}x{{ original_image.height|unlocalize }})<br /><br />
|
|
6
6
|
|
|
7
7
|
{% trans "Change image file:" %}
|
|
8
8
|
{{ field }}
|
|
@@ -1,13 +1,10 @@
|
|
|
1
|
-
{% extends "wagtailadmin/
|
|
2
|
-
{% load wagtailimages_tags wagtailadmin_tags %}
|
|
3
|
-
{% load i18n %}
|
|
1
|
+
{% extends "wagtailadmin/generic/form.html" %}
|
|
2
|
+
{% load wagtailimages_tags wagtailadmin_tags i18n %}
|
|
4
3
|
{% block titletag %}{% trans "Add an image" %}{% endblock %}
|
|
5
4
|
|
|
6
5
|
{% block extra_js %}
|
|
7
6
|
{{ block.super }}
|
|
8
7
|
|
|
9
|
-
{{ form.media.js }}
|
|
10
|
-
|
|
11
8
|
<script>
|
|
12
9
|
$(function() {
|
|
13
10
|
$('#id_file').on(
|
|
@@ -39,30 +36,15 @@
|
|
|
39
36
|
</script>
|
|
40
37
|
{% endblock %}
|
|
41
38
|
|
|
42
|
-
{% block
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
<form action="{% url 'wagtailimages:add' %}" method="POST" enctype="multipart/form-data" novalidate>
|
|
55
|
-
{% csrf_token %}
|
|
56
|
-
<ul class="fields">
|
|
57
|
-
{% for field in form %}
|
|
58
|
-
{% if field.is_hidden %}
|
|
59
|
-
{{ field }}
|
|
60
|
-
{% else %}
|
|
61
|
-
<li>{% formattedfield field %}</li>
|
|
62
|
-
{% endif %}
|
|
63
|
-
{% endfor %}
|
|
64
|
-
<li><input type="submit" value="{% trans 'Save' %}" class="button" /></li>
|
|
65
|
-
</ul>
|
|
66
|
-
</form>
|
|
67
|
-
</div>
|
|
39
|
+
{% block actions %}
|
|
40
|
+
<button
|
|
41
|
+
type="submit"
|
|
42
|
+
class="button button-longrunning"
|
|
43
|
+
data-controller="w-progress"
|
|
44
|
+
data-action="w-progress#activate"
|
|
45
|
+
data-w-progress-active-value="{% trans 'Uploading…' %}"
|
|
46
|
+
>
|
|
47
|
+
{% icon name="spinner" %}
|
|
48
|
+
<em data-w-progress-target="label">{% trans 'Upload' %}</em>
|
|
49
|
+
</button>
|
|
68
50
|
{% endblock %}
|