wagtail 6.0.2__py3-none-any.whl → 6.1rc1__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/admin/checks.py +51 -0
- wagtail/admin/compare.py +1 -1
- wagtail/admin/filters.py +70 -1
- wagtail/admin/forms/account.py +1 -1
- wagtail/admin/forms/collections.py +15 -0
- wagtail/admin/forms/pages.py +49 -0
- wagtail/admin/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/de/LC_MESSAGES/django.po +5 -5
- wagtail/admin/locale/en/LC_MESSAGES/django.po +474 -385
- wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +3 -3
- wagtail/admin/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/pt_PT/LC_MESSAGES/django.po +73 -2
- wagtail/admin/locale/ro/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ro/LC_MESSAGES/django.po +3 -3
- wagtail/admin/panels/comment_panel.py +1 -1
- wagtail/admin/panels/field_panel.py +1 -1
- wagtail/admin/rich_text/converters/editor_html.py +3 -1
- wagtail/admin/rich_text/editors/draftail/__init__.py +28 -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/favicon.ico +0 -0
- wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
- wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
- wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
- wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
- wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js +1 -1
- wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -1
- wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
- wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
- wagtail/admin/static/wagtailadmin/js/expanding-formset.js +1 -1
- wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
- wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
- wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
- wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
- wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
- wagtail/admin/static/wagtailadmin/js/preview-panel.js +1 -1
- wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
- wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
- wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
- wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
- wagtail/admin/static/wagtailadmin/js/telepath/blocks.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/vendor.js +1 -1
- wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +4 -4
- wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
- wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
- wagtail/admin/staticfiles.py +1 -0
- wagtail/admin/templates/wagtailadmin/base.html +1 -0
- wagtail/admin/templates/wagtailadmin/collection_privacy/set_privacy.html +3 -1
- wagtail/admin/templates/wagtailadmin/collections/index_results.html +10 -0
- wagtail/admin/templates/wagtailadmin/generic/base.html +1 -9
- wagtail/admin/templates/wagtailadmin/generic/form.html +3 -2
- wagtail/admin/templates/wagtailadmin/generic/history/action_cell.html +27 -0
- wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +3 -3
- wagtail/admin/templates/wagtailadmin/icons/keyboard.svg +1 -0
- wagtail/admin/templates/wagtailadmin/page_privacy/set_privacy.html +3 -1
- wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -14
- wagtail/admin/templates/wagtailadmin/pages/action_menu/save_draft.html +3 -1
- wagtail/admin/templates/wagtailadmin/pages/choose_parent.html +17 -0
- wagtail/admin/templates/wagtailadmin/pages/explorable_index.html +8 -0
- wagtail/admin/templates/wagtailadmin/pages/history.html +1 -61
- wagtail/admin/templates/wagtailadmin/pages/index.html +1 -3
- wagtail/admin/templates/wagtailadmin/pages/listing/_locked_indicator.html +2 -2
- wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_column_header.html +25 -27
- wagtail/admin/templates/wagtailadmin/pages/page_listing_header.html +2 -1
- wagtail/admin/templates/wagtailadmin/panels/multi_field_panel_child.html +1 -1
- wagtail/admin/templates/wagtailadmin/panels/publishing/schedule_publishing_panel.html +3 -1
- wagtail/admin/templates/wagtailadmin/panels/tabbed_interface.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/active_filters.html +2 -1
- wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +8 -0
- wagtail/admin/templates/wagtailadmin/shared/forms/single_checkbox.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/headers/page_edit_header.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +21 -9
- wagtail/admin/templates/wagtailadmin/shared/human_readable_date.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/keyboard_shortcuts_dialog.html +29 -0
- wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +2 -1
- wagtail/admin/templates/wagtailadmin/skeleton.html +2 -1
- wagtail/admin/templates/wagtailadmin/tables/related_objects_cell.html +9 -0
- wagtail/admin/templates/wagtailadmin/tables/title_cell.html +9 -7
- wagtail/admin/templates/wagtailadmin/widgets/draftail_rich_text_area.html +1 -1
- wagtail/admin/templates/wagtailadmin/workflows/create.html +6 -23
- wagtail/admin/templates/wagtailadmin/workflows/create_task.html +6 -15
- wagtail/admin/templates/wagtailadmin/workflows/edit.html +6 -23
- wagtail/admin/templates/wagtailadmin/workflows/edit_task.html +6 -13
- wagtail/admin/templates/wagtailadmin/workflows/includes/task_usage_cell.html +4 -4
- wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_tasks_cell.html +18 -0
- wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_title_cell.html +7 -0
- wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_used_by_cell.html +25 -0
- wagtail/admin/templates/wagtailadmin/workflows/index.html +0 -99
- wagtail/admin/templates/wagtailadmin/workflows/index_results.html +10 -0
- wagtail/admin/templates/wagtailadmin/workflows/task_index.html +0 -30
- wagtail/admin/templates/wagtailadmin/workflows/task_index_results.html +10 -0
- wagtail/admin/templates/wagtailadmin/workflows/usage.html +1 -1
- wagtail/admin/templatetags/wagtailadmin_tags.py +116 -39
- wagtail/admin/tests/pages/test_create_page.py +10 -4
- wagtail/admin/tests/pages/test_custom_listing.py +37 -0
- wagtail/admin/tests/pages/test_edit_page.py +6 -6
- wagtail/admin/tests/pages/test_explorer_view.py +19 -18
- wagtail/admin/tests/pages/test_move_page.py +1 -1
- wagtail/admin/tests/pages/test_page_usage.py +50 -2
- wagtail/admin/tests/pages/test_parent_page_chooser_view.py +119 -0
- wagtail/admin/tests/pages/test_preview.py +18 -4
- wagtail/admin/tests/test_account_management.py +20 -1
- wagtail/admin/tests/test_audit_log.py +172 -5
- wagtail/admin/tests/test_checks.py +92 -0
- wagtail/admin/tests/test_collections_views.py +19 -5
- wagtail/admin/tests/test_compare.py +6 -6
- wagtail/admin/tests/test_dashboard.py +404 -0
- wagtail/admin/tests/test_dbwhitelister.py +4 -5
- wagtail/admin/tests/test_edit_handlers.py +2 -2
- wagtail/admin/tests/test_keyboard_shortcuts.py +84 -0
- wagtail/admin/tests/test_page_chooser.py +31 -18
- wagtail/admin/tests/test_privacy.py +36 -2
- wagtail/admin/tests/test_rich_text.py +168 -23
- wagtail/admin/tests/test_templatetags.py +411 -43
- wagtail/admin/tests/test_views.py +4 -2
- wagtail/admin/tests/test_workflows.py +531 -9
- wagtail/admin/tests/tests.py +3 -1
- wagtail/admin/tests/ui/test_tables.py +48 -1
- wagtail/admin/tests/viewsets/test_model_viewset.py +126 -29
- wagtail/admin/ui/side_panels.py +3 -1
- wagtail/admin/ui/tables/__init__.py +13 -1
- wagtail/admin/ui/tables/pages.py +17 -6
- wagtail/admin/urls/__init__.py +8 -3
- wagtail/admin/urls/pages.py +5 -0
- wagtail/admin/urls/workflows.py +10 -0
- wagtail/admin/views/chooser.py +20 -24
- wagtail/admin/views/collections.py +17 -1
- wagtail/admin/views/generic/base.py +31 -4
- wagtail/admin/views/generic/history.py +220 -51
- wagtail/admin/views/generic/mixins.py +7 -4
- wagtail/admin/views/generic/models.py +54 -38
- wagtail/admin/views/generic/multiple_upload.py +17 -8
- wagtail/admin/views/generic/usage.py +17 -11
- wagtail/admin/views/home.py +15 -12
- wagtail/admin/views/mixins.py +30 -0
- wagtail/admin/views/pages/choose_parent.py +73 -0
- wagtail/admin/views/pages/history.py +54 -66
- wagtail/admin/views/pages/listing.py +187 -106
- wagtail/admin/views/pages/usage.py +6 -1
- wagtail/admin/views/pages/utils.py +70 -1
- wagtail/admin/views/workflows.py +150 -21
- wagtail/admin/viewsets/model.py +2 -2
- wagtail/admin/viewsets/pages.py +77 -0
- wagtail/admin/wagtail_hooks.py +40 -2
- wagtail/admin/widgets/button.py +10 -9
- wagtail/api/v2/filters.py +1 -1
- wagtail/api/v2/tests/test_pages.py +1 -1
- wagtail/blocks/base.py +18 -9
- wagtail/blocks/field_block.py +9 -7
- wagtail/blocks/list_block.py +16 -6
- wagtail/blocks/static_block.py +3 -0
- wagtail/blocks/stream_block.py +58 -23
- wagtail/blocks/struct_block.py +15 -9
- wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +39 -47
- wagtail/contrib/forms/models.py +5 -5
- wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +44 -33
- wagtail/contrib/forms/templates/wagtailforms/submissions_index.html +2 -63
- wagtail/contrib/forms/tests/test_models.py +26 -0
- wagtail/contrib/forms/urls.py +6 -0
- wagtail/contrib/forms/views.py +52 -49
- wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.po +3 -3
- wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +34 -42
- wagtail/contrib/redirects/signal_handlers.py +1 -1
- wagtail/contrib/redirects/templates/wagtailredirects/index.html +1 -36
- wagtail/contrib/redirects/templates/wagtailredirects/index_results.html +18 -0
- wagtail/contrib/redirects/templates/wagtailredirects/redirect_target_cell.html +8 -0
- wagtail/contrib/redirects/tests/test_import_command.py +1 -1
- wagtail/contrib/redirects/tests/test_redirects.py +79 -8
- wagtail/contrib/redirects/urls.py +2 -1
- wagtail/contrib/redirects/views.py +85 -55
- wagtail/contrib/search_promotions/admin_urls.py +2 -1
- wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +41 -64
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index.html +1 -16
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index_results.html +11 -0
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/list.html +0 -51
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/results.html +3 -16
- wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/search_promotion_column.html +15 -0
- wagtail/contrib/search_promotions/tests.py +122 -9
- wagtail/contrib/search_promotions/views.py +66 -65
- wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +3 -3
- wagtail/contrib/settings/registry.py +10 -5
- wagtail/contrib/settings/tests/generic/test_admin.py +9 -0
- wagtail/contrib/settings/tests/site_specific/test_admin.py +10 -1
- wagtail/contrib/settings/tests/site_specific/test_model.py +3 -3
- wagtail/contrib/settings/tests/site_specific/test_templates.py +1 -1
- wagtail/contrib/settings/views.py +3 -1
- wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/simple_translation/tests/test_wagtail_hooks.py +2 -2
- wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
- wagtail/contrib/table_block/blocks.py +1 -1
- wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
- wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
- wagtail/coreutils.py +3 -2
- wagtail/documents/admin_urls.py +2 -2
- wagtail/documents/locale/en/LC_MESSAGES/django.po +22 -22
- wagtail/documents/migrations/0013_delete_uploadeddocument.py +16 -0
- wagtail/documents/models.py +1 -20
- wagtail/documents/rich_text/__init__.py +11 -7
- wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
- wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
- wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
- wagtail/documents/templates/wagtaildocs/documents/index.html +0 -16
- wagtail/documents/tests/test_admin_views.py +155 -23
- wagtail/documents/tests/test_collection_privacy.py +55 -1
- wagtail/documents/tests/test_rich_text.py +14 -0
- wagtail/documents/views/documents.py +25 -22
- wagtail/documents/views/multiple.py +6 -7
- wagtail/documents/views/serve.py +16 -1
- wagtail/documents/wagtail_hooks.py +20 -15
- wagtail/embeds/blocks.py +5 -0
- wagtail/embeds/locale/en/LC_MESSAGES/django.po +2 -2
- wagtail/embeds/rich_text/__init__.py +1 -1
- wagtail/embeds/tests/test_rich_text.py +14 -0
- wagtail/embeds/wagtail_hooks.py +4 -14
- wagtail/fields.py +3 -48
- wagtail/images/admin_urls.py +2 -2
- wagtail/images/check_files/wagtail.jpg +0 -0
- wagtail/images/check_files/wagtail.png +0 -0
- wagtail/images/fields.py +2 -0
- wagtail/images/image_operations.py +1 -1
- wagtail/images/locale/en/LC_MESSAGES/django.po +33 -45
- wagtail/images/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/pt_PT/LC_MESSAGES/django.po +4 -0
- wagtail/images/migrations/0026_delete_uploadedimage.py +16 -0
- wagtail/images/models.py +49 -43
- wagtail/images/rich_text/__init__.py +18 -8
- wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
- wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
- wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
- wagtail/images/templates/wagtailimages/images/image_listing_header.html +6 -0
- wagtail/images/templates/wagtailimages/images/index.html +11 -51
- wagtail/images/tests/test_admin_views.py +119 -62
- wagtail/images/tests/test_image_operations.py +10 -0
- wagtail/images/tests/test_models.py +35 -33
- wagtail/images/tests/test_rich_text.py +14 -0
- wagtail/images/tests/utils.py +1 -1
- wagtail/images/views/images.py +35 -64
- wagtail/images/views/multiple.py +6 -7
- wagtail/images/wagtail_hooks.py +4 -14
- wagtail/locale/en/LC_MESSAGES/django.po +150 -136
- wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/locales/tests.py +18 -3
- wagtail/locales/views.py +0 -1
- wagtail/management/commands/rebuild_references_index.py +3 -1
- wagtail/migrations/0092_alter_collectionviewrestriction_password_and_more.py +33 -0
- wagtail/migrations/0093_uploadedfile.py +53 -0
- wagtail/models/__init__.py +147 -32
- wagtail/models/i18n.py +1 -1
- wagtail/models/{collections.py → media.py} +33 -2
- wagtail/models/reference_index.py +1 -1
- wagtail/models/view_restrictions.py +10 -3
- wagtail/project_template/project_name/settings/base.py +6 -0
- wagtail/project_template/requirements.txt +1 -1
- wagtail/rich_text/__init__.py +25 -8
- wagtail/rich_text/pages.py +19 -8
- wagtail/rich_text/rewriters.py +140 -68
- wagtail/search/backends/database/mysql/mysql.py +3 -3
- wagtail/search/backends/database/postgres/postgres.py +3 -3
- wagtail/search/backends/database/sqlite/sqlite.py +2 -2
- wagtail/search/backends/elasticsearch7.py +4 -0
- wagtail/search/locale/en/LC_MESSAGES/django.po +3 -3
- wagtail/search/tests/test_postgres_backend.py +50 -0
- wagtail/sites/locale/en/LC_MESSAGES/django.po +8 -8
- wagtail/sites/tests.py +35 -9
- wagtail/sites/views.py +3 -1
- wagtail/snippets/locale/de/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/de/LC_MESSAGES/django.po +5 -6
- wagtail/snippets/locale/en/LC_MESSAGES/django.po +16 -56
- 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/snippets/action_menu/publish.html +3 -1
- wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/save.html +3 -1
- wagtail/snippets/templates/wagtailsnippets/snippets/create.html +1 -1
- wagtail/snippets/templates/wagtailsnippets/snippets/edit.html +1 -1
- wagtail/snippets/tests/test_preview.py +13 -2
- wagtail/snippets/tests/test_snippets.py +41 -16
- wagtail/snippets/tests/test_viewset.py +63 -18
- wagtail/snippets/tests/test_workflows.py +12 -0
- wagtail/snippets/views/snippets.py +1 -40
- wagtail/templatetags/wagtailcore_tags.py +1 -1
- wagtail/test/demosite/models.py +1 -1
- wagtail/test/middleware.py +14 -1
- wagtail/test/testapp/fixtures/test.json +20 -0
- wagtail/test/testapp/migrations/0001_squashed_0073_revisablechildmodel_secret_text.py +8 -8
- wagtail/test/testapp/migrations/0023_snippetchoosermodel_full_featured.py +1 -0
- wagtail/test/testapp/migrations/0034_custompermissionmodel.py +44 -0
- wagtail/test/testapp/migrations/0035_modelwithcustommanager.py +30 -0
- wagtail/test/testapp/migrations/0036_complexdefaultstreampage.py +28 -0
- wagtail/test/testapp/models.py +79 -2
- wagtail/test/testapp/templates/tests/custom_docs_password_required.html +10 -0
- wagtail/test/testapp/templates/tests/custom_page_password_required.html +10 -0
- wagtail/test/testapp/views.py +24 -2
- wagtail/test/testapp/wagtail_hooks.py +19 -0
- wagtail/test/utils/wagtail_tests.py +2 -2
- wagtail/tests/test_blocks.py +262 -1
- wagtail/tests/test_migrations.py +1 -1
- wagtail/tests/test_page_model.py +77 -0
- wagtail/tests/test_page_privacy.py +18 -1
- wagtail/tests/test_rich_text.py +95 -5
- wagtail/tests/test_streamfield.py +43 -0
- wagtail/tests/test_utils.py +8 -2
- wagtail/tests/test_views.py +52 -1
- wagtail/tests/test_whitelist.py +7 -7
- wagtail/users/forms.py +3 -1
- wagtail/users/locale/en/LC_MESSAGES/django.po +124 -96
- wagtail/users/migrations/0013_userprofile_density.py +23 -0
- wagtail/users/models.py +14 -3
- wagtail/users/templates/wagtailusers/groups/create.html +1 -7
- wagtail/users/templates/wagtailusers/groups/edit.html +1 -13
- wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +46 -2
- wagtail/users/templates/wagtailusers/groups/includes/group_form_js.html +0 -2
- wagtail/users/templates/wagtailusers/users/create.html +1 -9
- wagtail/users/templates/wagtailusers/users/edit.html +1 -9
- wagtail/users/templates/wagtailusers/users/index.html +2 -5
- wagtail/users/templates/wagtailusers/users/index_results.html +3 -13
- wagtail/users/templates/wagtailusers/users/user_cell.html +9 -0
- wagtail/users/templatetags/wagtailusers_tags.py +107 -20
- wagtail/users/tests/test_admin_views.py +669 -90
- wagtail/users/views/groups.py +58 -61
- wagtail/users/views/users.py +211 -92
- wagtail/users/wagtail_hooks.py +6 -38
- wagtail/users/widgets.py +3 -5
- wagtail/utils/text.py +1 -1
- wagtail/views.py +5 -9
- wagtail/whitelist.py +1 -1
- {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/METADATA +4 -5
- {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/RECORD +339 -320
- wagtail/admin/static/wagtailadmin/js/page-editor.js +0 -1
- wagtail/admin/static/wagtailadmin/js/vendor/mousetrap.min.js +0 -1
- wagtail/admin/static/wagtailadmin/js/vendor/urlify.js +0 -1
- wagtail/admin/static/wagtailadmin/js/vendor/xregexp.min.js +0 -1
- wagtail/admin/templates/wagtailadmin/collections/index.html +0 -34
- wagtail/admin/templates/wagtailadmin/pages/revisions/_actions.html +0 -22
- wagtail/admin/templates/wagtailadmin/shared/page_breadcrumbs.html +0 -55
- wagtail/admin/tests/pages/test_dashboard.py +0 -172
- wagtail/contrib/redirects/templates/wagtailredirects/results.html +0 -23
- wagtail/documents/templates/wagtaildocs/documents/list.html +0 -2
- wagtail/search/tests/test_postgres_stemming.py +0 -40
- wagtail/sites/templates/wagtailsites/create.html +0 -6
- wagtail/sites/templates/wagtailsites/edit.html +0 -6
- wagtail/snippets/templates/wagtailsnippets/snippets/revisions/_actions.html +0 -36
- wagtail/users/templates/wagtailusers/users/list.html +0 -62
- wagtail/users/urls/users.py +0 -12
- {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/LICENSE +0 -0
- {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/WHEEL +0 -0
- {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/entry_points.txt +0 -0
- {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
from django.contrib.auth import get_user_model
|
|
2
|
+
from django.contrib.auth.models import Group, Permission
|
|
3
|
+
from django.contrib.contenttypes.models import ContentType
|
|
4
|
+
from django.test import TestCase
|
|
5
|
+
from django.urls import reverse
|
|
6
|
+
from django.utils import timezone
|
|
7
|
+
from freezegun import freeze_time
|
|
8
|
+
|
|
9
|
+
from wagtail.admin.views.home import (
|
|
10
|
+
LockedPagesPanel,
|
|
11
|
+
RecentEditsPanel,
|
|
12
|
+
UserObjectsInWorkflowModerationPanel,
|
|
13
|
+
WorkflowObjectsToModeratePanel,
|
|
14
|
+
)
|
|
15
|
+
from wagtail.coreutils import get_dummy_request
|
|
16
|
+
from wagtail.models import GroupPagePermission, Page, Workflow, WorkflowContentType
|
|
17
|
+
from wagtail.test.testapp.models import FullFeaturedSnippet, SimplePage
|
|
18
|
+
from wagtail.test.utils import WagtailTestUtils
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestRecentEditsPanel(WagtailTestUtils, TestCase):
|
|
22
|
+
def setUp(self):
|
|
23
|
+
# Find root page
|
|
24
|
+
self.root_page = Page.objects.get(id=2)
|
|
25
|
+
|
|
26
|
+
# Add child page
|
|
27
|
+
child_page = SimplePage(
|
|
28
|
+
title="Hello world!",
|
|
29
|
+
slug="hello-world",
|
|
30
|
+
content="Some content here",
|
|
31
|
+
)
|
|
32
|
+
self.root_page.add_child(instance=child_page)
|
|
33
|
+
self.revision = child_page.save_revision()
|
|
34
|
+
self.revision.publish()
|
|
35
|
+
self.child_page = SimplePage.objects.get(id=child_page.id)
|
|
36
|
+
|
|
37
|
+
self.user_alice = self.create_superuser(username="alice", password="password")
|
|
38
|
+
self.create_superuser(username="bob", password="password")
|
|
39
|
+
|
|
40
|
+
def change_something(self, title):
|
|
41
|
+
post_data = {"title": title, "content": "Some content", "slug": "hello-world"}
|
|
42
|
+
response = self.client.post(
|
|
43
|
+
reverse("wagtailadmin_pages:edit", args=(self.child_page.id,)), post_data
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
# Should be redirected to edit page
|
|
47
|
+
self.assertRedirects(
|
|
48
|
+
response, reverse("wagtailadmin_pages:edit", args=(self.child_page.id,))
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# The page should have "has_unpublished_changes" flag set
|
|
52
|
+
child_page_new = SimplePage.objects.get(id=self.child_page.id)
|
|
53
|
+
self.assertTrue(child_page_new.has_unpublished_changes)
|
|
54
|
+
|
|
55
|
+
def go_to_dashboard_response(self):
|
|
56
|
+
response = self.client.get(reverse("wagtailadmin_home"))
|
|
57
|
+
self.assertEqual(response.status_code, 200)
|
|
58
|
+
return response
|
|
59
|
+
|
|
60
|
+
def test_your_recent_edits(self):
|
|
61
|
+
# Login as Bob
|
|
62
|
+
self.login(username="bob", password="password")
|
|
63
|
+
|
|
64
|
+
# Bob hasn't edited anything yet
|
|
65
|
+
response = self.client.get(reverse("wagtailadmin_home"))
|
|
66
|
+
self.assertNotIn("Your most recent edits", response.content.decode("utf-8"))
|
|
67
|
+
|
|
68
|
+
# Login as Alice
|
|
69
|
+
self.client.logout()
|
|
70
|
+
self.login(username="alice", password="password")
|
|
71
|
+
|
|
72
|
+
# Alice changes something
|
|
73
|
+
self.change_something("Alice's edit")
|
|
74
|
+
|
|
75
|
+
# Edit should show up on dashboard
|
|
76
|
+
response = self.go_to_dashboard_response()
|
|
77
|
+
self.assertIn("Your most recent edits", response.content.decode("utf-8"))
|
|
78
|
+
|
|
79
|
+
# Bob changes something
|
|
80
|
+
self.login(username="bob", password="password")
|
|
81
|
+
self.change_something("Bob's edit")
|
|
82
|
+
|
|
83
|
+
# Edit shows up on Bobs dashboard
|
|
84
|
+
response = self.go_to_dashboard_response()
|
|
85
|
+
self.assertIn("Your most recent edits", response.content.decode("utf-8"))
|
|
86
|
+
|
|
87
|
+
# Login as Alice again
|
|
88
|
+
self.client.logout()
|
|
89
|
+
self.login(username="alice", password="password")
|
|
90
|
+
|
|
91
|
+
# Alice's dashboard should still list that first edit
|
|
92
|
+
response = self.go_to_dashboard_response()
|
|
93
|
+
self.assertIn("Your most recent edits", response.content.decode("utf-8"))
|
|
94
|
+
|
|
95
|
+
def test_missing_page_record(self):
|
|
96
|
+
# Ensure that the panel still renders when one of the page IDs returned from querying
|
|
97
|
+
# PageLogEntry has no corresponding Page object. This can happen if a page is deleted,
|
|
98
|
+
# because PageLogEntry records are kept on deletion.
|
|
99
|
+
|
|
100
|
+
self.login(username="alice", password="password")
|
|
101
|
+
self.change_something("Alice's edit")
|
|
102
|
+
self.child_page.delete()
|
|
103
|
+
response = self.client.get(reverse("wagtailadmin_home"))
|
|
104
|
+
self.assertEqual(response.status_code, 200)
|
|
105
|
+
|
|
106
|
+
def test_panel(self):
|
|
107
|
+
"""Test if the panel actually returns expected pages"""
|
|
108
|
+
self.login(username="bob", password="password")
|
|
109
|
+
# change a page
|
|
110
|
+
|
|
111
|
+
edit_timestamp = timezone.now()
|
|
112
|
+
with freeze_time(edit_timestamp):
|
|
113
|
+
self.change_something("Bob's edit")
|
|
114
|
+
|
|
115
|
+
# set a user to 'mock' a request
|
|
116
|
+
self.client.user = get_user_model().objects.get(email="bob@example.com")
|
|
117
|
+
# get the panel to get the last edits
|
|
118
|
+
panel = RecentEditsPanel()
|
|
119
|
+
ctx = panel.get_context_data({"request": self.client})
|
|
120
|
+
|
|
121
|
+
page = Page.objects.get(pk=self.child_page.id).specific
|
|
122
|
+
|
|
123
|
+
# check the timestamp matches the edit
|
|
124
|
+
self.assertEqual(ctx["last_edits"][0][0], edit_timestamp)
|
|
125
|
+
# check if the page in this list is the specific page
|
|
126
|
+
self.assertEqual(ctx["last_edits"][0][1], page)
|
|
127
|
+
|
|
128
|
+
def test_copying_does_not_count_as_an_edit(self):
|
|
129
|
+
self.login(username="bob", password="password")
|
|
130
|
+
# change a page
|
|
131
|
+
self.change_something("Bob was ere")
|
|
132
|
+
|
|
133
|
+
# copy the page
|
|
134
|
+
post_data = {
|
|
135
|
+
"new_title": "Goodbye world!",
|
|
136
|
+
"new_slug": "goodbye-world",
|
|
137
|
+
"new_parent_page": str(self.root_page.id),
|
|
138
|
+
"copy_subpages": False,
|
|
139
|
+
"alias": False,
|
|
140
|
+
}
|
|
141
|
+
self.client.post(
|
|
142
|
+
reverse("wagtailadmin_pages:copy", args=(self.child_page.id,)), post_data
|
|
143
|
+
)
|
|
144
|
+
# check that page has been copied
|
|
145
|
+
self.assertTrue(Page.objects.get(title="Goodbye world!"))
|
|
146
|
+
|
|
147
|
+
response = self.client.get(reverse("wagtailadmin_home"))
|
|
148
|
+
self.assertEqual(response.status_code, 200)
|
|
149
|
+
self.assertContains(response, "Your most recent edits")
|
|
150
|
+
self.assertContains(response, "Bob was ere")
|
|
151
|
+
self.assertNotContains(response, "Goodbye world!")
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class TestRecentEditsQueryCount(WagtailTestUtils, TestCase):
|
|
155
|
+
fixtures = ["test.json"]
|
|
156
|
+
|
|
157
|
+
def setUp(self):
|
|
158
|
+
self.bob = self.create_superuser(username="bob", password="password")
|
|
159
|
+
self.dummy_request = get_dummy_request()
|
|
160
|
+
self.dummy_request.user = self.bob
|
|
161
|
+
workflow = Workflow.objects.first()
|
|
162
|
+
workflow_pages = {5, 6}
|
|
163
|
+
locked_pages = {6, 9}
|
|
164
|
+
scheduled_pages = {9, 12}
|
|
165
|
+
# make a bunch of page edits (all to EventPages, so that calls to specific() don't add
|
|
166
|
+
# an unpredictable number of queries)
|
|
167
|
+
pages_to_edit = list(
|
|
168
|
+
Page.objects.filter(id__in=[4, 5, 6, 9, 12, 13]).order_by("pk").specific()
|
|
169
|
+
)
|
|
170
|
+
for page in pages_to_edit:
|
|
171
|
+
revision = page.save_revision(user=self.bob, log_action=True)
|
|
172
|
+
if page.pk in workflow_pages:
|
|
173
|
+
workflow.start(page, self.bob)
|
|
174
|
+
if page.pk in locked_pages:
|
|
175
|
+
page.locked = True
|
|
176
|
+
page.locked_by = self.bob
|
|
177
|
+
page.locked_at = timezone.now()
|
|
178
|
+
page.save()
|
|
179
|
+
if page.pk in scheduled_pages:
|
|
180
|
+
revision.approved_go_live_at = timezone.now()
|
|
181
|
+
revision.save()
|
|
182
|
+
|
|
183
|
+
def test_panel_query_count(self):
|
|
184
|
+
panel = RecentEditsPanel()
|
|
185
|
+
parent_context = {"request": self.dummy_request}
|
|
186
|
+
# Warm up the cache
|
|
187
|
+
html = panel.render_html(parent_context)
|
|
188
|
+
|
|
189
|
+
with self.assertNumQueries(5):
|
|
190
|
+
# Rendering RecentEditsPanel should not generate N+1 queries -
|
|
191
|
+
# i.e. any number less than 6 would be reasonable here
|
|
192
|
+
html = panel.render_html(parent_context)
|
|
193
|
+
# check that the panel is still actually returning results
|
|
194
|
+
self.assertIn("Ameristralia Day", html)
|
|
195
|
+
soup = self.get_soup(html)
|
|
196
|
+
self.assertEqual(len(soup.select('svg use[href="#icon-lock"]')), 2)
|
|
197
|
+
expected_statuses = [
|
|
198
|
+
"live + draft",
|
|
199
|
+
"live + scheduled",
|
|
200
|
+
"live + scheduled",
|
|
201
|
+
"in moderation",
|
|
202
|
+
"in moderation",
|
|
203
|
+
]
|
|
204
|
+
statuses = [
|
|
205
|
+
"".join(e.find_all(string=True, recursive=False)).strip()
|
|
206
|
+
for e in soup.select(".w-status")
|
|
207
|
+
]
|
|
208
|
+
self.assertEqual(statuses, expected_statuses)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class TestLockedPagesQueryCount(WagtailTestUtils, TestCase):
|
|
212
|
+
fixtures = ["test.json"]
|
|
213
|
+
|
|
214
|
+
def setUp(self):
|
|
215
|
+
self.bob = self.create_superuser(username="bob", password="password")
|
|
216
|
+
self.dummy_request = get_dummy_request()
|
|
217
|
+
self.dummy_request.user = self.bob
|
|
218
|
+
|
|
219
|
+
pages = Page.objects.filter(pk__in=[9, 12, 13]).order_by("pk")
|
|
220
|
+
for i, page in enumerate(pages):
|
|
221
|
+
page.locked = True
|
|
222
|
+
page.locked_by = self.bob
|
|
223
|
+
page.locked_at = timezone.now() + timezone.timedelta(hours=i)
|
|
224
|
+
page.save()
|
|
225
|
+
|
|
226
|
+
def test_panel_query_count(self):
|
|
227
|
+
panel = LockedPagesPanel()
|
|
228
|
+
parent_context = {"request": self.dummy_request, "csrf_token": "dummy"}
|
|
229
|
+
# Warm up the cache
|
|
230
|
+
html = panel.render_html(parent_context)
|
|
231
|
+
|
|
232
|
+
with self.assertNumQueries(1):
|
|
233
|
+
html = panel.render_html(parent_context)
|
|
234
|
+
soup = self.get_soup(html)
|
|
235
|
+
# Should be sorted descending by locked_at
|
|
236
|
+
expected_titles = [
|
|
237
|
+
"Saint Patrick (single event)",
|
|
238
|
+
"Steal underpants",
|
|
239
|
+
"Ameristralia Day",
|
|
240
|
+
]
|
|
241
|
+
titles = [e.get_text(strip=True) for e in soup.select(".title-wrapper a")]
|
|
242
|
+
self.assertEqual(titles, expected_titles)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class UserObjectsInWorkflowModerationQueryCount(WagtailTestUtils, TestCase):
|
|
246
|
+
fixtures = ["test.json"]
|
|
247
|
+
|
|
248
|
+
def setUp(self):
|
|
249
|
+
self.superuser = self.create_superuser(username="admin", password="password")
|
|
250
|
+
self.bob = self.create_user(username="bob", password="password")
|
|
251
|
+
self.someone_else = self.create_user(
|
|
252
|
+
username="someoneelse", password="password"
|
|
253
|
+
)
|
|
254
|
+
editors = Group.objects.get(name="Editors")
|
|
255
|
+
editors.user_set.add(self.bob, self.someone_else)
|
|
256
|
+
|
|
257
|
+
workflow = Workflow.objects.first()
|
|
258
|
+
WorkflowContentType.objects.create(
|
|
259
|
+
workflow=workflow,
|
|
260
|
+
content_type=ContentType.objects.get_for_model(FullFeaturedSnippet),
|
|
261
|
+
)
|
|
262
|
+
GroupPagePermission.objects.create(
|
|
263
|
+
group=editors, page=Page.get_first_root_node(), permission_type="change"
|
|
264
|
+
)
|
|
265
|
+
editors.permissions.add(
|
|
266
|
+
Permission.objects.get(codename="change_fullfeaturedsnippet")
|
|
267
|
+
)
|
|
268
|
+
|
|
269
|
+
# Pages owned by bob, but workflow started by someone else
|
|
270
|
+
Page.objects.filter(id__in=[9, 12]).update(owner=self.bob)
|
|
271
|
+
for page in Page.objects.filter(id__in=[9, 12]).specific():
|
|
272
|
+
page.save_revision()
|
|
273
|
+
workflow.start(page, self.someone_else)
|
|
274
|
+
# Lock it to test the lock indicator
|
|
275
|
+
page.locked = True
|
|
276
|
+
page.locked_by = self.superuser
|
|
277
|
+
page.locked_at = timezone.now()
|
|
278
|
+
page.save()
|
|
279
|
+
|
|
280
|
+
# Page workflow started by bob
|
|
281
|
+
for page in Page.objects.filter(id__in=[4, 13]).specific():
|
|
282
|
+
page.save_revision()
|
|
283
|
+
workflow.start(page, self.bob)
|
|
284
|
+
|
|
285
|
+
# Snippet workflow started by bob
|
|
286
|
+
for i in range(1, 3):
|
|
287
|
+
obj = FullFeaturedSnippet.objects.create(text=f"Some obj {i}")
|
|
288
|
+
obj.save_revision()
|
|
289
|
+
workflow.start(obj, self.bob)
|
|
290
|
+
|
|
291
|
+
self.dummy_request = get_dummy_request()
|
|
292
|
+
self.dummy_request.user = self.bob
|
|
293
|
+
|
|
294
|
+
def test_panel_query_count(self):
|
|
295
|
+
panel = UserObjectsInWorkflowModerationPanel()
|
|
296
|
+
parent_context = {"request": self.dummy_request}
|
|
297
|
+
# Warm up the cache
|
|
298
|
+
html = panel.render_html(parent_context)
|
|
299
|
+
|
|
300
|
+
with self.assertNumQueries(4):
|
|
301
|
+
html = panel.render_html(parent_context)
|
|
302
|
+
|
|
303
|
+
soup = self.get_soup(html)
|
|
304
|
+
self.assertEqual(len(soup.select('svg use[href="#icon-lock"]')), 2)
|
|
305
|
+
expected_titles = [
|
|
306
|
+
"Some obj 2",
|
|
307
|
+
"Some obj 1",
|
|
308
|
+
"Saint Patrick (single event)",
|
|
309
|
+
"Christmas",
|
|
310
|
+
"Steal underpants",
|
|
311
|
+
"Ameristralia Day",
|
|
312
|
+
]
|
|
313
|
+
titles = [e.get_text(strip=True) for e in soup.select(".title-wrapper a")]
|
|
314
|
+
self.assertEqual(titles, expected_titles)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
class WorkflowObjectsToModerateQueryCount(WagtailTestUtils, TestCase):
|
|
318
|
+
fixtures = ["test.json"]
|
|
319
|
+
|
|
320
|
+
def setUp(self):
|
|
321
|
+
self.superuser = self.create_superuser(username="admin", password="password")
|
|
322
|
+
self.bob = self.create_user(username="bob", password="password")
|
|
323
|
+
self.moderator = self.create_user(username="moderator", password="password")
|
|
324
|
+
|
|
325
|
+
editors = Group.objects.get(name="Editors")
|
|
326
|
+
moderators = Group.objects.get(name="Moderators")
|
|
327
|
+
|
|
328
|
+
editors.user_set.add(self.bob)
|
|
329
|
+
moderators.user_set.add(self.moderator)
|
|
330
|
+
|
|
331
|
+
root = Page.get_first_root_node()
|
|
332
|
+
GroupPagePermission.objects.create(
|
|
333
|
+
group=editors, page=root, permission_type="change"
|
|
334
|
+
)
|
|
335
|
+
GroupPagePermission.objects.create(
|
|
336
|
+
group=moderators, page=root, permission_type="change"
|
|
337
|
+
)
|
|
338
|
+
GroupPagePermission.objects.create(
|
|
339
|
+
group=moderators, page=root, permission_type="publish"
|
|
340
|
+
)
|
|
341
|
+
|
|
342
|
+
editors.permissions.add(
|
|
343
|
+
Permission.objects.get(codename="change_fullfeaturedsnippet")
|
|
344
|
+
)
|
|
345
|
+
moderators.permissions.add(
|
|
346
|
+
*Permission.objects.filter(
|
|
347
|
+
codename__in=[
|
|
348
|
+
"change_fullfeaturedsnippet",
|
|
349
|
+
"publish_fullfeaturedsnippet",
|
|
350
|
+
]
|
|
351
|
+
),
|
|
352
|
+
)
|
|
353
|
+
|
|
354
|
+
workflow = Workflow.objects.first()
|
|
355
|
+
WorkflowContentType.objects.create(
|
|
356
|
+
workflow=workflow,
|
|
357
|
+
content_type=ContentType.objects.get_for_model(FullFeaturedSnippet),
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
# Pages workflow started by bob and locked by moderator
|
|
361
|
+
for page in Page.objects.filter(id__in=[9, 12]).specific():
|
|
362
|
+
page.save_revision()
|
|
363
|
+
workflow.start(page, self.bob)
|
|
364
|
+
# Lock it to test the lock indicator
|
|
365
|
+
page.locked = True
|
|
366
|
+
page.locked_by = self.moderator
|
|
367
|
+
page.locked_at = timezone.now()
|
|
368
|
+
page.save()
|
|
369
|
+
|
|
370
|
+
# Page workflow started by bob
|
|
371
|
+
for page in Page.objects.filter(id__in=[4, 13]).specific():
|
|
372
|
+
page.save_revision()
|
|
373
|
+
workflow.start(page, self.bob)
|
|
374
|
+
|
|
375
|
+
# Snippet workflow started by bob
|
|
376
|
+
for i in range(1, 3):
|
|
377
|
+
obj = FullFeaturedSnippet.objects.create(text=f"Some obj {i}")
|
|
378
|
+
obj.save_revision()
|
|
379
|
+
workflow.start(obj, self.bob)
|
|
380
|
+
|
|
381
|
+
self.dummy_request = get_dummy_request()
|
|
382
|
+
self.dummy_request.user = self.moderator
|
|
383
|
+
|
|
384
|
+
def test_panel_query_count(self):
|
|
385
|
+
panel = WorkflowObjectsToModeratePanel()
|
|
386
|
+
parent_context = {"request": self.dummy_request, "csrf_token": "dummy"}
|
|
387
|
+
# Warm up the cache
|
|
388
|
+
html = panel.render_html(parent_context)
|
|
389
|
+
|
|
390
|
+
with self.assertNumQueries(13):
|
|
391
|
+
html = panel.render_html(parent_context)
|
|
392
|
+
|
|
393
|
+
soup = self.get_soup(html)
|
|
394
|
+
self.assertEqual(len(soup.select('svg use[href="#icon-lock"]')), 2)
|
|
395
|
+
expected_titles = [
|
|
396
|
+
"Some obj 2",
|
|
397
|
+
"Some obj 1",
|
|
398
|
+
"Saint Patrick (single event)",
|
|
399
|
+
"Christmas",
|
|
400
|
+
"Steal underpants",
|
|
401
|
+
"Ameristralia Day",
|
|
402
|
+
]
|
|
403
|
+
titles = [e.get_text(strip=True) for e in soup.select(".title-wrapper a")]
|
|
404
|
+
self.assertEqual(titles, expected_titles)
|
|
@@ -9,7 +9,7 @@ class TestDbWhitelisterMethods(WagtailTestUtils, TestCase):
|
|
|
9
9
|
self.whitelister = EditorHTMLConverter().whitelister
|
|
10
10
|
|
|
11
11
|
def test_clean_tag_node_div(self):
|
|
12
|
-
soup = self.get_soup("<div>foo</div>"
|
|
12
|
+
soup = self.get_soup("<div>foo</div>")
|
|
13
13
|
tag = soup.div
|
|
14
14
|
self.assertEqual(tag.name, "div")
|
|
15
15
|
self.whitelister.clean_tag_node(soup, tag)
|
|
@@ -18,7 +18,6 @@ class TestDbWhitelisterMethods(WagtailTestUtils, TestCase):
|
|
|
18
18
|
def test_clean_tag_node_with_data_embedtype(self):
|
|
19
19
|
soup = self.get_soup(
|
|
20
20
|
'<p><a data-embedtype="image" data-id=1 data-format="left" data-alt="bar" irrelevant="baz">foo</a></p>',
|
|
21
|
-
"html5lib",
|
|
22
21
|
)
|
|
23
22
|
tag = soup.p
|
|
24
23
|
self.whitelister.clean_tag_node(soup, tag)
|
|
@@ -29,14 +28,13 @@ class TestDbWhitelisterMethods(WagtailTestUtils, TestCase):
|
|
|
29
28
|
def test_clean_tag_node_with_data_linktype(self):
|
|
30
29
|
soup = self.get_soup(
|
|
31
30
|
'<a data-linktype="document" data-id="1" irrelevant="baz">foo</a>',
|
|
32
|
-
"html5lib",
|
|
33
31
|
)
|
|
34
32
|
tag = soup.a
|
|
35
33
|
self.whitelister.clean_tag_node(soup, tag)
|
|
36
34
|
self.assertEqual(str(tag), '<a id="1" linktype="document">foo</a>')
|
|
37
35
|
|
|
38
36
|
def test_clean_tag_node(self):
|
|
39
|
-
soup = self.get_soup('<a irrelevant="baz">foo</a>'
|
|
37
|
+
soup = self.get_soup('<a irrelevant="baz">foo</a>')
|
|
40
38
|
tag = soup.a
|
|
41
39
|
self.whitelister.clean_tag_node(soup, tag)
|
|
42
40
|
self.assertEqual(str(tag), "<a>foo</a>")
|
|
@@ -52,7 +50,8 @@ class TestDbWhitelister(WagtailTestUtils, TestCase):
|
|
|
52
50
|
(necessary because we can't guarantee the order that attributes are output in)
|
|
53
51
|
"""
|
|
54
52
|
self.assertEqual(
|
|
55
|
-
self.get_soup(str1
|
|
53
|
+
self.get_soup(str1),
|
|
54
|
+
self.get_soup(str2),
|
|
56
55
|
)
|
|
57
56
|
|
|
58
57
|
def test_page_link_is_rewritten(self):
|
|
@@ -189,7 +189,7 @@ class TestGetFormForModel(TestCase):
|
|
|
189
189
|
self.assertIn("speakers", form.formsets)
|
|
190
190
|
self.assertNotIn("related_links", form.formsets)
|
|
191
191
|
|
|
192
|
-
def
|
|
192
|
+
def test_get_form_for_model_with_widget_overrides_by_class(self):
|
|
193
193
|
EventPageForm = get_form_for_model(
|
|
194
194
|
EventPage,
|
|
195
195
|
form_class=WagtailAdminPageForm,
|
|
@@ -201,7 +201,7 @@ class TestGetFormForModel(TestCase):
|
|
|
201
201
|
self.assertEqual(type(form.fields["date_from"]), forms.DateField)
|
|
202
202
|
self.assertEqual(type(form.fields["date_from"].widget), forms.PasswordInput)
|
|
203
203
|
|
|
204
|
-
def
|
|
204
|
+
def test_get_form_for_model_with_widget_overrides_by_instance(self):
|
|
205
205
|
EventPageForm = get_form_for_model(
|
|
206
206
|
EventPage,
|
|
207
207
|
form_class=WagtailAdminPageForm,
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import json
|
|
2
|
+
import re
|
|
3
|
+
|
|
4
|
+
from django.test import TestCase
|
|
5
|
+
from django.test.client import Client
|
|
6
|
+
from django.urls import reverse
|
|
7
|
+
|
|
8
|
+
from wagtail.test.utils import WagtailTestUtils
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class TestKeyboardShortcutsDialog(WagtailTestUtils, TestCase):
|
|
12
|
+
def setUp(self):
|
|
13
|
+
self.login()
|
|
14
|
+
|
|
15
|
+
def test_keyboard_shortcuts_trigger_in_sidebar(self):
|
|
16
|
+
response = self.client.get(reverse("wagtailadmin_home"))
|
|
17
|
+
self.assertEqual(response.status_code, 200)
|
|
18
|
+
|
|
19
|
+
sidebar_data = (
|
|
20
|
+
self.get_soup(response.content)
|
|
21
|
+
.select_one("#wagtail-sidebar-props")
|
|
22
|
+
.contents[0]
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
self.assertIn(
|
|
26
|
+
json.dumps(
|
|
27
|
+
{
|
|
28
|
+
"role": "button",
|
|
29
|
+
"data-a11y-dialog-show": "keyboard-shortcuts-dialog",
|
|
30
|
+
"data-action": "w-action#noop:prevent:stop",
|
|
31
|
+
"data-controller": "w-action",
|
|
32
|
+
}
|
|
33
|
+
),
|
|
34
|
+
sidebar_data,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def test_keyboard_shortcuts_dialog(self):
|
|
38
|
+
response = self.client.get(reverse("wagtailadmin_home"))
|
|
39
|
+
|
|
40
|
+
self.assertEqual(response.status_code, 200)
|
|
41
|
+
self.assertTemplateUsed(
|
|
42
|
+
response, "wagtailadmin/shared/keyboard_shortcuts_dialog.html"
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
soup = self.get_soup(response.content)
|
|
46
|
+
|
|
47
|
+
# Check that the keyboard shortcuts dialog is present
|
|
48
|
+
shortcuts_dialog = soup.select_one("#keyboard-shortcuts-dialog")
|
|
49
|
+
self.assertIsNotNone(shortcuts_dialog)
|
|
50
|
+
|
|
51
|
+
# Check that the keyboard shortcuts dialog has basic accessible content
|
|
52
|
+
self.assertIn(
|
|
53
|
+
"All keyboard shortcuts", shortcuts_dialog.find("caption").prettify()
|
|
54
|
+
)
|
|
55
|
+
self.assertIn("Keyboard shortcut", shortcuts_dialog.find("thead").prettify())
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
class TestMacKeyboardShortcutsDialog(WagtailTestUtils, TestCase):
|
|
59
|
+
def setUp(self):
|
|
60
|
+
# Creates a client with a Mac user agent
|
|
61
|
+
self.client = Client(
|
|
62
|
+
headers={
|
|
63
|
+
"user-agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/107.0.0.0 Safari/537.36"
|
|
64
|
+
}
|
|
65
|
+
)
|
|
66
|
+
self.login()
|
|
67
|
+
|
|
68
|
+
def test_mac_useragent_and_behavior(self):
|
|
69
|
+
response = self.client.get(reverse("wagtailadmin_home"))
|
|
70
|
+
|
|
71
|
+
# Check that the user agent is a Mac
|
|
72
|
+
user_agent = response.context["request"].headers.get("User-Agent", "")
|
|
73
|
+
is_mac = re.search(r"Mac|iPod|iPhone|iPad", user_agent)
|
|
74
|
+
|
|
75
|
+
# Add assertions based on expected Mac behavior
|
|
76
|
+
self.assertTrue(is_mac)
|
|
77
|
+
|
|
78
|
+
# Check that the keyboard shortcuts dialog has Mac-specific content
|
|
79
|
+
soup = self.get_soup(response.content)
|
|
80
|
+
shortcuts_dialog = soup.select_one("#keyboard-shortcuts-dialog")
|
|
81
|
+
all_shortcuts = shortcuts_dialog.select("kbd")
|
|
82
|
+
for shortcut in all_shortcuts:
|
|
83
|
+
# All shortcuts should have the ⌘ symbol
|
|
84
|
+
self.assertIn("⌘", shortcut.prettify())
|
|
@@ -4,6 +4,7 @@ import urllib.parse as urlparse
|
|
|
4
4
|
from django.contrib.auth import get_user_model
|
|
5
5
|
from django.test import TestCase, TransactionTestCase, override_settings
|
|
6
6
|
from django.urls import reverse
|
|
7
|
+
from django.utils.html import escape
|
|
7
8
|
from django.utils.http import urlencode
|
|
8
9
|
|
|
9
10
|
from wagtail.admin.views.chooser import can_choose_page
|
|
@@ -1253,7 +1254,7 @@ class TestPageChooserLocaleSelector(WagtailTestUtils, TestCase):
|
|
|
1253
1254
|
self.child_page_fr.save()
|
|
1254
1255
|
|
|
1255
1256
|
switch_to_french_url = self.get_choose_page_url(
|
|
1256
|
-
|
|
1257
|
+
parent_page_id=self.child_page_fr.pk
|
|
1257
1258
|
)
|
|
1258
1259
|
self.LOCALE_SELECTOR_HTML_FR = (
|
|
1259
1260
|
f'<a href="{switch_to_french_url}" data-locale-selector-link>'
|
|
@@ -1266,20 +1267,12 @@ class TestPageChooserLocaleSelector(WagtailTestUtils, TestCase):
|
|
|
1266
1267
|
reverse("wagtailadmin_choose_page_child", args=[parent_page_id])
|
|
1267
1268
|
)
|
|
1268
1269
|
|
|
1269
|
-
def get_choose_page_url(self,
|
|
1270
|
+
def get_choose_page_url(self, parent_page_id=None, params=""):
|
|
1270
1271
|
if parent_page_id is not None:
|
|
1271
1272
|
url = reverse("wagtailadmin_choose_page_child", args=[parent_page_id])
|
|
1272
1273
|
else:
|
|
1273
1274
|
url = reverse("wagtailadmin_choose_page")
|
|
1274
|
-
|
|
1275
|
-
suffix = ""
|
|
1276
|
-
if parent_page_id is None:
|
|
1277
|
-
# the locale param should only be appended at the root level
|
|
1278
|
-
if locale is None:
|
|
1279
|
-
locale = self.fr_locale
|
|
1280
|
-
separator = "&" if html else "&"
|
|
1281
|
-
suffix = f"{separator}locale={locale.language_code}"
|
|
1282
|
-
return f"{url}?page_type=wagtailcore.page{suffix}"
|
|
1275
|
+
return f"{url}?{params}"
|
|
1283
1276
|
|
|
1284
1277
|
def test_locale_selector_present_in_root_view(self):
|
|
1285
1278
|
response = self.client.get(reverse("wagtailadmin_choose_page"))
|
|
@@ -1287,7 +1280,7 @@ class TestPageChooserLocaleSelector(WagtailTestUtils, TestCase):
|
|
|
1287
1280
|
|
|
1288
1281
|
self.assertRegex(html, self.LOCALE_SELECTOR_HTML)
|
|
1289
1282
|
|
|
1290
|
-
switch_to_french_url = self.get_choose_page_url(locale=
|
|
1283
|
+
switch_to_french_url = self.get_choose_page_url(params="locale=fr")
|
|
1291
1284
|
fr_selector = f'<a href="{switch_to_french_url}" data-locale-selector-link>'
|
|
1292
1285
|
self.assertIn(fr_selector, html)
|
|
1293
1286
|
|
|
@@ -1306,9 +1299,7 @@ class TestPageChooserLocaleSelector(WagtailTestUtils, TestCase):
|
|
|
1306
1299
|
self.assertNotIn("data-locale-selector", html)
|
|
1307
1300
|
|
|
1308
1301
|
def test_locale_selector_with_active_locale(self):
|
|
1309
|
-
switch_to_french_url = self.get_choose_page_url(
|
|
1310
|
-
locale=self.fr_locale, html=False
|
|
1311
|
-
)
|
|
1302
|
+
switch_to_french_url = self.get_choose_page_url(params="locale=fr")
|
|
1312
1303
|
response = self.client.get(switch_to_french_url)
|
|
1313
1304
|
html = response.json().get("html")
|
|
1314
1305
|
|
|
@@ -1319,9 +1310,7 @@ class TestPageChooserLocaleSelector(WagtailTestUtils, TestCase):
|
|
|
1319
1310
|
html,
|
|
1320
1311
|
r"data-locale-selector[^<]+<button[^<]+<svg[^<]+<use[^<]+<\/use[^<]+<\/svg[^<]+French",
|
|
1321
1312
|
)
|
|
1322
|
-
switch_to_english_url = self.get_choose_page_url(
|
|
1323
|
-
locale=Locale.objects.get(language_code="en")
|
|
1324
|
-
)
|
|
1313
|
+
switch_to_english_url = self.get_choose_page_url(params="locale=en")
|
|
1325
1314
|
self.assertIn(
|
|
1326
1315
|
f'<a href="{switch_to_english_url}" data-locale-selector-link>',
|
|
1327
1316
|
html,
|
|
@@ -1332,3 +1321,27 @@ class TestPageChooserLocaleSelector(WagtailTestUtils, TestCase):
|
|
|
1332
1321
|
response = self.get(self.child_page.pk)
|
|
1333
1322
|
html = response.json().get("html")
|
|
1334
1323
|
self.assertNotIn("data-locale-selector", html)
|
|
1324
|
+
|
|
1325
|
+
def test_query_params_preserved(self):
|
|
1326
|
+
choose_url = reverse(
|
|
1327
|
+
"wagtailadmin_choose_page_child", args=[self.child_page.pk]
|
|
1328
|
+
)
|
|
1329
|
+
params = "can_choose_root=false&user_perms=copy_to&match_subclass=true"
|
|
1330
|
+
response = self.client.get(f"{choose_url}?{params}&p=1")
|
|
1331
|
+
html = response.json().get("html")
|
|
1332
|
+
self.assertIn("data-locale-selector", html)
|
|
1333
|
+
|
|
1334
|
+
switch_to_french_url = self.get_choose_page_url(
|
|
1335
|
+
parent_page_id=self.child_page_fr.pk, params=params
|
|
1336
|
+
)
|
|
1337
|
+
self.assertIn(escape(switch_to_french_url), html)
|
|
1338
|
+
|
|
1339
|
+
def test_query_params_preserved_in_root_view(self):
|
|
1340
|
+
choose_url = reverse("wagtailadmin_choose_page")
|
|
1341
|
+
params = "can_choose_root=false&user_perms=copy_to&match_subclass=true"
|
|
1342
|
+
response = self.client.get(f"{choose_url}?{params}&p=1")
|
|
1343
|
+
html = response.json().get("html")
|
|
1344
|
+
self.assertIn("data-locale-selector", html)
|
|
1345
|
+
|
|
1346
|
+
switch_to_french_url = self.get_choose_page_url(params=params + "&locale=fr")
|
|
1347
|
+
self.assertIn(escape(switch_to_french_url), html)
|