wagtail 7.2.1__py3-none-any.whl → 7.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 +4 -2
- wagtail/admin/action_menu.py +4 -1
- wagtail/admin/api/actions/convert_alias.py +2 -2
- wagtail/admin/api/actions/copy.py +2 -2
- wagtail/admin/api/actions/copy_for_translation.py +2 -2
- wagtail/admin/api/actions/create_alias.py +2 -2
- wagtail/admin/api/actions/delete.py +1 -1
- wagtail/admin/api/actions/move.py +1 -1
- wagtail/admin/api/actions/publish.py +2 -2
- wagtail/admin/api/actions/revert_to_page_revision.py +2 -2
- wagtail/admin/api/actions/unpublish.py +1 -1
- wagtail/admin/api/filters.py +2 -2
- wagtail/admin/compare.py +22 -0
- wagtail/admin/forms/account.py +52 -1
- wagtail/admin/forms/comments.py +53 -0
- wagtail/admin/forms/models.py +36 -0
- wagtail/admin/forms/pages.py +7 -0
- wagtail/admin/forms/workflows.py +5 -2
- wagtail/admin/icons.py +4 -3
- wagtail/admin/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/ar/LC_MESSAGES/django.po +35 -0
- wagtail/admin/locale/en/LC_MESSAGES/django.po +262 -234
- wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +72 -43
- wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/it/LC_MESSAGES/django.po +566 -6
- wagtail/admin/locale/it/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/it/LC_MESSAGES/djangojs.po +40 -2
- wagtail/admin/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/nl/LC_MESSAGES/django.po +2 -2
- wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/sv/LC_MESSAGES/django.po +330 -15
- wagtail/admin/locale/sv/LC_MESSAGES/djangojs.mo +0 -0
- wagtail/admin/locale/sv/LC_MESSAGES/djangojs.po +3 -2
- wagtail/admin/panels/comment_panel.py +1 -51
- wagtail/admin/panels/title_field_panel.py +3 -1
- wagtail/admin/static/wagtailadmin/css/core.css +1 -1
- wagtail/admin/static/wagtailadmin/js/bulk-actions.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/draftail.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/telepath/blocks.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/templates/wagtailadmin/base.html +1 -1
- wagtail/admin/templates/wagtailadmin/generic/edit_partials.html +100 -0
- wagtail/admin/templates/wagtailadmin/generic/form.html +26 -5
- wagtail/admin/templates/wagtailadmin/generic/includes/_loaded_revision_inputs.html +3 -0
- wagtail/admin/templates/wagtailadmin/generic/listing_results.html +1 -17
- wagtail/admin/templates/wagtailadmin/pages/create.html +14 -4
- wagtail/admin/templates/wagtailadmin/pages/edit.html +16 -3
- wagtail/admin/templates/wagtailadmin/pages/edit_partials.html +31 -0
- wagtail/admin/templates/wagtailadmin/pages/index_results.html +1 -9
- wagtail/admin/templates/wagtailadmin/shared/autosave/indicator.html +36 -0
- wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/editing_sessions/list.html +2 -2
- wagtail/admin/templates/wagtailadmin/shared/editing_sessions/module.html +19 -3
- wagtail/admin/templates/wagtailadmin/shared/headers/_actions.html +5 -0
- wagtail/admin/templates/wagtailadmin/shared/headers/_history_icon_link.html +2 -2
- wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +8 -9
- wagtail/admin/templates/wagtailadmin/shared/listing/filter_partials.html +19 -0
- wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +3 -3
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/side_panel.html +3 -0
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/privacy.html +18 -6
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/usage.html +11 -4
- wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/workflow.html +22 -1
- wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +1 -0
- wagtail/admin/templates/wagtailadmin/shared/side_panels.html +1 -2
- wagtail/admin/templates/wagtailadmin/shared/unsaved_changes_warning.html +20 -20
- wagtail/admin/templates/wagtailadmin/userbar/base.html +2 -0
- wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +1 -1
- wagtail/admin/templatetags/wagtailadmin_tags.py +6 -14
- wagtail/admin/tests/pages/test_create_page.py +298 -1
- wagtail/admin/tests/pages/test_custom_listing.py +48 -2
- wagtail/admin/tests/pages/test_edit_page.py +721 -7
- wagtail/admin/tests/pages/test_explorer_view.py +9 -5
- wagtail/admin/tests/pages/test_page_search.py +15 -0
- wagtail/admin/tests/pages/test_revisions.py +4 -0
- wagtail/admin/tests/test_account_management.py +88 -2
- wagtail/admin/tests/test_collections_views.py +15 -15
- wagtail/admin/tests/test_compare.py +34 -0
- wagtail/admin/tests/test_editing_sessions.py +230 -8
- wagtail/admin/tests/test_forms.py +192 -1
- wagtail/admin/tests/test_icon_sprite.py +22 -2
- wagtail/admin/tests/test_page_chooser.py +13 -13
- wagtail/admin/tests/test_reports_views.py +4 -1
- wagtail/admin/tests/test_userbar.py +69 -5
- wagtail/admin/tests/test_views_generic.py +5 -5
- wagtail/admin/tests/test_workflows.py +14 -12
- wagtail/admin/tests/viewsets/test_model_viewset.py +13 -0
- wagtail/admin/ui/autosave.py +5 -0
- wagtail/admin/ui/editing_sessions.py +3 -0
- wagtail/admin/ui/side_panels.py +19 -20
- wagtail/admin/userbar.py +48 -27
- wagtail/admin/views/bulk_action/dispatcher.py +2 -2
- wagtail/admin/views/chooser.py +6 -6
- wagtail/admin/views/editing_sessions.py +20 -7
- wagtail/admin/views/generic/__init__.py +1 -0
- wagtail/admin/views/generic/chooser.py +5 -5
- wagtail/admin/views/generic/mixins.py +143 -26
- wagtail/admin/views/generic/models.py +157 -27
- wagtail/admin/views/generic/ordering.py +1 -1
- wagtail/admin/views/generic/preview.py +4 -4
- wagtail/admin/views/home.py +3 -1
- wagtail/admin/views/pages/create.py +77 -29
- wagtail/admin/views/pages/edit.py +160 -34
- wagtail/admin/views/pages/preview.py +4 -4
- wagtail/admin/views/pages/revisions.py +3 -0
- wagtail/admin/views/pages/search.py +4 -4
- wagtail/admin/views/pages/usage.py +2 -2
- wagtail/admin/views/tags.py +2 -2
- wagtail/admin/views/workflows.py +73 -54
- wagtail/admin/viewsets/model.py +34 -8
- wagtail/admin/widgets/button.py +11 -4
- wagtail/admin/widgets/chooser.py +6 -4
- wagtail/admin/widgets/slug.py +1 -1
- wagtail/api/v2/filters.py +27 -21
- wagtail/api/v2/pagination.py +4 -4
- wagtail/api/v2/views.py +7 -7
- wagtail/blocks/list_block.py +0 -8
- wagtail/blocks/migrations/migrate_operation.py +8 -1
- wagtail/blocks/stream_block.py +2 -10
- wagtail/blocks/struct_block.py +192 -12
- wagtail/compat.py +2 -2
- wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/forms/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/forms/locale/it/LC_MESSAGES/django.po +40 -2
- wagtail/contrib/frontend_cache/backends/azure.py +6 -6
- wagtail/contrib/frontend_cache/backends/cloudfront.py +2 -2
- wagtail/contrib/frontend_cache/utils.py +1 -1
- wagtail/contrib/redirects/forms.py +1 -1
- wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +14 -14
- wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.po +15 -2
- wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.po +16 -3
- wagtail/contrib/redirects/middleware.py +11 -7
- wagtail/contrib/redirects/models.py +17 -1
- wagtail/contrib/redirects/tests/test_import_admin_views.py +3 -3
- wagtail/contrib/redirects/tests/test_redirects.py +56 -8
- wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +6 -6
- wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +43 -2
- wagtail/contrib/search_promotions/tests.py +1 -1
- wagtail/contrib/search_promotions/views/settings.py +24 -25
- wagtail/contrib/settings/jinja2tags.py +2 -2
- wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +2 -2
- wagtail/contrib/settings/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/settings/locale/it/LC_MESSAGES/django.po +6 -2
- wagtail/contrib/settings/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/settings/locale/sv/LC_MESSAGES/django.po +3 -2
- wagtail/contrib/settings/tests/generic/test_admin.py +118 -2
- wagtail/contrib/settings/tests/site_specific/test_admin.py +78 -15
- wagtail/contrib/settings/tests/site_specific/test_model.py +2 -2
- wagtail/contrib/settings/views.py +7 -0
- wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/simple_translation/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/simple_translation/locale/it/LC_MESSAGES/django.po +5 -2
- wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
- wagtail/contrib/styleguide/tests.py +2 -0
- wagtail/contrib/styleguide/views.py +4 -5
- wagtail/contrib/table_block/locale/ar/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/table_block/locale/ar/LC_MESSAGES/django.po +5 -1
- wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.po +5 -2
- wagtail/contrib/typed_table_block/blocks.py +37 -0
- wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
- wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/contrib/typed_table_block/locale/sv/LC_MESSAGES/django.po +4 -3
- wagtail/contrib/typed_table_block/tests.py +108 -0
- wagtail/coreutils.py +4 -4
- wagtail/documents/__init__.py +4 -4
- wagtail/documents/locale/en/LC_MESSAGES/django.po +15 -15
- wagtail/documents/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/it/LC_MESSAGES/django.po +32 -2
- wagtail/documents/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/sv/LC_MESSAGES/django.po +32 -4
- wagtail/documents/models.py +1 -1
- wagtail/documents/tests/test_admin_views.py +19 -4
- wagtail/documents/tests/test_search.py +0 -2
- wagtail/documents/tests/test_views.py +6 -4
- wagtail/documents/views/bulk_actions/add_tags.py +1 -1
- wagtail/embeds/finders/facebook.py +3 -3
- wagtail/embeds/finders/instagram.py +3 -3
- wagtail/embeds/finders/oembed.py +7 -2
- wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/embeds/oembed_providers.py +8 -0
- wagtail/embeds/tests/test_embeds.py +35 -0
- wagtail/images/__init__.py +4 -4
- wagtail/images/admin_urls.py +3 -3
- wagtail/images/blocks.py +1 -1
- wagtail/images/formats.py +2 -2
- wagtail/images/image_operations.py +2 -2
- wagtail/images/locale/en/LC_MESSAGES/django.po +44 -40
- wagtail/images/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/it/LC_MESSAGES/django.po +50 -4
- wagtail/images/locale/nl/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/nl/LC_MESSAGES/django.po +3 -3
- wagtail/images/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/sv/LC_MESSAGES/django.po +49 -6
- wagtail/images/models.py +11 -10
- wagtail/images/templates/wagtailimages/images/index_results.html +1 -1
- wagtail/images/templates/wagtailimages/images/url_generator.html +17 -38
- wagtail/images/templates/wagtailimages/images/url_generator_output.html +28 -0
- wagtail/images/templatetags/wagtailimages_tags.py +4 -4
- wagtail/images/tests/test_admin_views.py +432 -59
- wagtail/images/tests/test_image_operations.py +2 -2
- wagtail/images/tests/test_models.py +0 -2
- wagtail/images/views/bulk_actions/add_tags.py +1 -1
- wagtail/images/views/images.py +72 -39
- wagtail/locale/en/LC_MESSAGES/django.po +103 -105
- wagtail/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/it/LC_MESSAGES/django.po +105 -2
- wagtail/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/locale/sv/LC_MESSAGES/django.po +95 -12
- wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/locales/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/locales/locale/it/LC_MESSAGES/django.po +13 -2
- wagtail/locales/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/locales/locale/sv/LC_MESSAGES/django.po +4 -3
- wagtail/locales/tests.py +5 -5
- wagtail/models/i18n.py +4 -6
- wagtail/models/media.py +18 -0
- wagtail/models/pages.py +65 -11
- wagtail/models/reference_index.py +15 -0
- wagtail/models/revisions.py +40 -12
- wagtail/models/workflows.py +0 -3
- wagtail/permission_policies/base.py +2 -2
- wagtail/permission_policies/collections.py +2 -2
- wagtail/project_template/requirements.txt +2 -2
- wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/signal_handlers.py +1 -0
- wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/sites/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/sites/locale/sv/LC_MESSAGES/django.po +3 -2
- wagtail/sites/tests.py +7 -7
- wagtail/snippets/action_menu.py +2 -1
- wagtail/snippets/locale/en/LC_MESSAGES/django.po +23 -13
- wagtail/snippets/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/snippets/locale/sv/LC_MESSAGES/django.po +12 -1
- wagtail/snippets/tests/test_chooser_block.py +197 -0
- wagtail/snippets/tests/test_chooser_panel.py +149 -0
- wagtail/snippets/tests/test_chooser_views.py +375 -0
- wagtail/snippets/tests/test_chooser_widget.py +22 -0
- wagtail/snippets/tests/test_compare_revisions_view.py +167 -0
- wagtail/snippets/tests/test_copy_view.py +38 -0
- wagtail/snippets/tests/test_create_view.py +1159 -0
- wagtail/snippets/tests/test_delete_view.py +225 -0
- wagtail/snippets/tests/test_edit_view.py +2882 -0
- wagtail/snippets/tests/test_history_view.py +182 -0
- wagtail/snippets/tests/test_index_view.py +105 -0
- wagtail/snippets/tests/test_list_view.py +786 -0
- wagtail/snippets/tests/test_locking.py +28 -0
- wagtail/snippets/tests/test_permissions.py +167 -0
- wagtail/snippets/tests/test_preview.py +3 -3
- wagtail/snippets/tests/test_revert_view.py +343 -0
- wagtail/snippets/tests/test_snippet_models.py +112 -0
- wagtail/snippets/tests/test_unpublish_view.py +228 -0
- wagtail/snippets/tests/test_unschedule_view.py +151 -0
- wagtail/snippets/tests/test_viewset.py +38 -5
- wagtail/snippets/views/snippets.py +78 -30
- wagtail/snippets/widgets.py +2 -2
- wagtail/templatetags/wagtailcore_tags.py +2 -2
- wagtail/test/dummy_external_storage.py +1 -1
- wagtail/test/testapp/fixtures/test.json +22 -0
- wagtail/test/testapp/fixtures/test_empty.json +2 -0
- wagtail/test/testapp/fixtures/test_explorable_pages.json +10 -0
- wagtail/test/testapp/fixtures/test_specific.json +9 -0
- wagtail/test/testapp/migrations/0059_nopromotepage.py +25 -0
- wagtail/test/testapp/models.py +7 -0
- wagtail/test/testapp/views.py +5 -0
- wagtail/test/utils/page_tests.py +7 -7
- wagtail/test/utils/wagtail_factories/builder.py +2 -2
- wagtail/tests/streamfield_migrations/test_migrations.py +35 -0
- wagtail/tests/test_blocks.py +321 -61
- wagtail/tests/test_collection_model.py +12 -0
- wagtail/tests/test_page_model.py +190 -1
- wagtail/tests/test_page_privacy.py +5 -0
- wagtail/tests/test_reference_index.py +42 -0
- wagtail/tests/test_revision_model.py +118 -1
- wagtail/tests/test_utils.py +2 -2
- wagtail/users/locale/en/LC_MESSAGES/django.po +14 -14
- wagtail/users/locale/id_ID/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/id_ID/LC_MESSAGES/django.po +6 -2
- wagtail/users/locale/it/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/it/LC_MESSAGES/django.po +14 -2
- wagtail/users/locale/sv/LC_MESSAGES/django.mo +0 -0
- wagtail/users/locale/sv/LC_MESSAGES/django.po +48 -17
- wagtail/users/tests/test_admin_views.py +39 -25
- wagtail/users/utils.py +4 -1
- wagtail/users/views/groups.py +19 -5
- wagtail/users/wagtail_hooks.py +1 -1
- wagtail/utils/loading.py +2 -2
- wagtail-7.3rc1.data/data/AGENTS.md +21 -0
- wagtail-7.3rc1.data/data/CHANGELOG.txt +4941 -0
- wagtail-7.3rc1.data/data/CODE_OF_CONDUCT.md +71 -0
- wagtail-7.3rc1.data/data/CONTRIBUTORS.md +999 -0
- wagtail-7.3rc1.data/data/SPONSORS.md +55 -0
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/METADATA +6 -6
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/RECORD +310 -280
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/WHEEL +1 -1
- wagtail/images/static/wagtailimages/js/image-url-generator.js +0 -1
- wagtail/snippets/tests/test_snippets.py +0 -6377
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/entry_points.txt +0 -0
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/licenses/LICENSE +0 -0
- {wagtail-7.2.1.dist-info → wagtail-7.3rc1.dist-info}/top_level.txt +0 -0
|
@@ -345,6 +345,34 @@ class TestEditLockedSnippet(BaseLockingTestCase):
|
|
|
345
345
|
# Check that the snippet is not edited
|
|
346
346
|
self.assertEqual(self.snippet.text, "I'm a lockable snippet!")
|
|
347
347
|
|
|
348
|
+
def test_edit_post_locked_with_json_response(self):
|
|
349
|
+
# Lock the snippet
|
|
350
|
+
self.lock_snippet(self.create_user("user2"))
|
|
351
|
+
|
|
352
|
+
# Try to edit the snippet
|
|
353
|
+
response = self.client.post(
|
|
354
|
+
self.get_url("edit"),
|
|
355
|
+
{"text": "Edited while locked"},
|
|
356
|
+
headers={"Accept": "application/json"},
|
|
357
|
+
)
|
|
358
|
+
self.refresh_snippet()
|
|
359
|
+
|
|
360
|
+
self.assertEqual(response.status_code, 400)
|
|
361
|
+
self.assertEqual(
|
|
362
|
+
response.json(),
|
|
363
|
+
{
|
|
364
|
+
"success": False,
|
|
365
|
+
"error_code": "locked",
|
|
366
|
+
"error_message": f"The {self.model_name} could not be saved as it is locked",
|
|
367
|
+
},
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
# Check that the snippet is still locked
|
|
371
|
+
self.assertTrue(self.snippet.locked)
|
|
372
|
+
|
|
373
|
+
# Check that the snippet is not edited
|
|
374
|
+
self.assertEqual(self.snippet.text, "I'm a lockable snippet!")
|
|
375
|
+
|
|
348
376
|
def test_edit_post_locked_by_self(self):
|
|
349
377
|
"""A user can edit a snippet that is locked by themselves."""
|
|
350
378
|
# Lock the snippet
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
from django.contrib.admin.utils import quote
|
|
2
|
+
from django.contrib.auth.models import Permission
|
|
3
|
+
from django.test import TestCase
|
|
4
|
+
from django.urls import reverse
|
|
5
|
+
|
|
6
|
+
from wagtail.test.testapp.models import Advert
|
|
7
|
+
from wagtail.test.utils import WagtailTestUtils
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class TestAddOnlyPermissions(WagtailTestUtils, TestCase):
|
|
11
|
+
fixtures = ["test.json"]
|
|
12
|
+
|
|
13
|
+
def setUp(self):
|
|
14
|
+
self.test_snippet = Advert.objects.get(pk=1)
|
|
15
|
+
|
|
16
|
+
# Create a user with add_advert permission but not change_advert
|
|
17
|
+
user = self.create_user(
|
|
18
|
+
username="addonly", email="addonly@example.com", password="password"
|
|
19
|
+
)
|
|
20
|
+
add_permission = Permission.objects.get(
|
|
21
|
+
content_type__app_label="tests", codename="add_advert"
|
|
22
|
+
)
|
|
23
|
+
admin_permission = Permission.objects.get(
|
|
24
|
+
content_type__app_label="wagtailadmin", codename="access_admin"
|
|
25
|
+
)
|
|
26
|
+
user.user_permissions.add(add_permission, admin_permission)
|
|
27
|
+
self.login(username="addonly", password="password")
|
|
28
|
+
|
|
29
|
+
def test_get_index(self):
|
|
30
|
+
response = self.client.get(reverse("wagtailsnippets_tests_advert:list"))
|
|
31
|
+
self.assertEqual(response.status_code, 200)
|
|
32
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
|
|
33
|
+
|
|
34
|
+
# user should get an "Add advert" button
|
|
35
|
+
self.assertContains(response, "Add advert")
|
|
36
|
+
|
|
37
|
+
def test_get_add(self):
|
|
38
|
+
response = self.client.get(reverse("wagtailsnippets_tests_advert:add"))
|
|
39
|
+
self.assertEqual(response.status_code, 200)
|
|
40
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/create.html")
|
|
41
|
+
self.assertEqual(response.context["header_icon"], "snippet")
|
|
42
|
+
|
|
43
|
+
def test_get_edit(self):
|
|
44
|
+
response = self.client.get(
|
|
45
|
+
reverse(
|
|
46
|
+
"wagtailsnippets_tests_advert:edit",
|
|
47
|
+
args=[quote(self.test_snippet.pk)],
|
|
48
|
+
)
|
|
49
|
+
)
|
|
50
|
+
# permission should be denied
|
|
51
|
+
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
|
52
|
+
|
|
53
|
+
def test_get_delete(self):
|
|
54
|
+
response = self.client.get(
|
|
55
|
+
reverse(
|
|
56
|
+
"wagtailsnippets_tests_advert:delete",
|
|
57
|
+
args=[quote(self.test_snippet.pk)],
|
|
58
|
+
)
|
|
59
|
+
)
|
|
60
|
+
# permission should be denied
|
|
61
|
+
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class TestEditOnlyPermissions(WagtailTestUtils, TestCase):
|
|
65
|
+
fixtures = ["test.json"]
|
|
66
|
+
|
|
67
|
+
def setUp(self):
|
|
68
|
+
self.test_snippet = Advert.objects.get(pk=1)
|
|
69
|
+
|
|
70
|
+
# Create a user with change_advert permission but not add_advert
|
|
71
|
+
user = self.create_user(
|
|
72
|
+
username="changeonly", email="changeonly@example.com", password="password"
|
|
73
|
+
)
|
|
74
|
+
change_permission = Permission.objects.get(
|
|
75
|
+
content_type__app_label="tests", codename="change_advert"
|
|
76
|
+
)
|
|
77
|
+
admin_permission = Permission.objects.get(
|
|
78
|
+
content_type__app_label="wagtailadmin", codename="access_admin"
|
|
79
|
+
)
|
|
80
|
+
user.user_permissions.add(change_permission, admin_permission)
|
|
81
|
+
self.login(username="changeonly", password="password")
|
|
82
|
+
|
|
83
|
+
def test_get_index(self):
|
|
84
|
+
response = self.client.get(reverse("wagtailsnippets_tests_advert:list"))
|
|
85
|
+
self.assertEqual(response.status_code, 200)
|
|
86
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
|
|
87
|
+
|
|
88
|
+
# user should not get an "Add advert" button
|
|
89
|
+
self.assertNotContains(response, "Add advert")
|
|
90
|
+
|
|
91
|
+
def test_get_add(self):
|
|
92
|
+
response = self.client.get(reverse("wagtailsnippets_tests_advert:add"))
|
|
93
|
+
# permission should be denied
|
|
94
|
+
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
|
95
|
+
|
|
96
|
+
def test_get_edit(self):
|
|
97
|
+
response = self.client.get(
|
|
98
|
+
reverse(
|
|
99
|
+
"wagtailsnippets_tests_advert:edit",
|
|
100
|
+
args=[quote(self.test_snippet.pk)],
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
self.assertEqual(response.status_code, 200)
|
|
104
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
|
|
105
|
+
self.assertEqual(response.context["header_icon"], "snippet")
|
|
106
|
+
|
|
107
|
+
def test_get_delete(self):
|
|
108
|
+
response = self.client.get(
|
|
109
|
+
reverse(
|
|
110
|
+
"wagtailsnippets_tests_advert:delete",
|
|
111
|
+
args=[quote(self.test_snippet.pk)],
|
|
112
|
+
)
|
|
113
|
+
)
|
|
114
|
+
# permission should be denied
|
|
115
|
+
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class TestDeleteOnlyPermissions(WagtailTestUtils, TestCase):
|
|
119
|
+
fixtures = ["test.json"]
|
|
120
|
+
|
|
121
|
+
def setUp(self):
|
|
122
|
+
self.test_snippet = Advert.objects.get(pk=1)
|
|
123
|
+
|
|
124
|
+
# Create a user with delete_advert permission
|
|
125
|
+
user = self.create_user(username="deleteonly", password="password")
|
|
126
|
+
change_permission = Permission.objects.get(
|
|
127
|
+
content_type__app_label="tests", codename="delete_advert"
|
|
128
|
+
)
|
|
129
|
+
admin_permission = Permission.objects.get(
|
|
130
|
+
content_type__app_label="wagtailadmin", codename="access_admin"
|
|
131
|
+
)
|
|
132
|
+
user.user_permissions.add(change_permission, admin_permission)
|
|
133
|
+
self.login(username="deleteonly", password="password")
|
|
134
|
+
|
|
135
|
+
def test_get_index(self):
|
|
136
|
+
response = self.client.get(reverse("wagtailsnippets_tests_advert:list"))
|
|
137
|
+
self.assertEqual(response.status_code, 200)
|
|
138
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
|
|
139
|
+
|
|
140
|
+
# user should not get an "Add advert" button
|
|
141
|
+
self.assertNotContains(response, "Add advert")
|
|
142
|
+
|
|
143
|
+
def test_get_add(self):
|
|
144
|
+
response = self.client.get(reverse("wagtailsnippets_tests_advert:add"))
|
|
145
|
+
# permission should be denied
|
|
146
|
+
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
|
147
|
+
|
|
148
|
+
def test_get_edit(self):
|
|
149
|
+
response = self.client.get(
|
|
150
|
+
reverse(
|
|
151
|
+
"wagtailsnippets_tests_advert:edit",
|
|
152
|
+
args=[quote(self.test_snippet.pk)],
|
|
153
|
+
)
|
|
154
|
+
)
|
|
155
|
+
# permission should be denied
|
|
156
|
+
self.assertRedirects(response, reverse("wagtailadmin_home"))
|
|
157
|
+
|
|
158
|
+
def test_get_delete(self):
|
|
159
|
+
response = self.client.get(
|
|
160
|
+
reverse(
|
|
161
|
+
"wagtailsnippets_tests_advert:delete",
|
|
162
|
+
args=[quote(self.test_snippet.pk)],
|
|
163
|
+
)
|
|
164
|
+
)
|
|
165
|
+
self.assertEqual(response.status_code, 200)
|
|
166
|
+
self.assertTemplateUsed(response, "wagtailadmin/generic/confirm_delete.html")
|
|
167
|
+
self.assertEqual(response.context["header_icon"], "snippet")
|
|
@@ -480,7 +480,7 @@ class TestEnablePreview(WagtailTestUtils, TestCase):
|
|
|
480
480
|
def test_show_preview_panel_on_edit_with_single_mode(self):
|
|
481
481
|
edit_url = self.get_url(self.single, "edit", args=(self.single.pk,))
|
|
482
482
|
preview_url = self.get_url(
|
|
483
|
-
self.single, "preview_on_edit", args=(self.
|
|
483
|
+
self.single, "preview_on_edit", args=(self.single.pk,)
|
|
484
484
|
)
|
|
485
485
|
new_tab_url = preview_url + "?mode="
|
|
486
486
|
response = self.client.get(edit_url)
|
|
@@ -574,7 +574,7 @@ class TestEnablePreview(WagtailTestUtils, TestCase):
|
|
|
574
574
|
def test_custom_auto_update_interval(self):
|
|
575
575
|
edit_url = self.get_url(self.single, "edit", args=(self.single.pk,))
|
|
576
576
|
preview_url = self.get_url(
|
|
577
|
-
self.single, "preview_on_edit", args=(self.
|
|
577
|
+
self.single, "preview_on_edit", args=(self.single.pk,)
|
|
578
578
|
)
|
|
579
579
|
response = self.client.get(edit_url)
|
|
580
580
|
|
|
@@ -603,7 +603,7 @@ class TestEnablePreview(WagtailTestUtils, TestCase):
|
|
|
603
603
|
def test_disable_auto_update_using_zero_interval(self):
|
|
604
604
|
edit_url = self.get_url(self.single, "edit", args=(self.single.pk,))
|
|
605
605
|
preview_url = self.get_url(
|
|
606
|
-
self.single, "preview_on_edit", args=(self.
|
|
606
|
+
self.single, "preview_on_edit", args=(self.single.pk,)
|
|
607
607
|
)
|
|
608
608
|
response = self.client.get(edit_url)
|
|
609
609
|
|
|
@@ -0,0 +1,343 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.contrib.admin.utils import quote
|
|
3
|
+
from django.contrib.auth.models import Permission
|
|
4
|
+
from django.contrib.contenttypes.models import ContentType
|
|
5
|
+
from django.test import TestCase
|
|
6
|
+
from django.urls import reverse
|
|
7
|
+
from django.utils.timezone import now
|
|
8
|
+
from freezegun import freeze_time
|
|
9
|
+
|
|
10
|
+
from wagtail.models import ModelLogEntry
|
|
11
|
+
from wagtail.test.testapp.models import (
|
|
12
|
+
Advert,
|
|
13
|
+
DraftStateModel,
|
|
14
|
+
MultiPreviewModesModel,
|
|
15
|
+
RevisableModel,
|
|
16
|
+
)
|
|
17
|
+
from wagtail.test.utils import WagtailTestUtils
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class TestSnippetRevisions(WagtailTestUtils, TestCase):
|
|
21
|
+
@property
|
|
22
|
+
def revert_url(self):
|
|
23
|
+
return self.get_url(
|
|
24
|
+
"revisions_revert", args=[quote(self.snippet.pk), self.initial_revision.pk]
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
def get(self):
|
|
28
|
+
return self.client.get(self.revert_url)
|
|
29
|
+
|
|
30
|
+
def post(self, post_data=None):
|
|
31
|
+
return self.client.post(self.revert_url, post_data)
|
|
32
|
+
|
|
33
|
+
def get_url(self, url_name, args=None):
|
|
34
|
+
view_name = self.snippet.snippet_viewset.get_url_name(url_name)
|
|
35
|
+
if args is None:
|
|
36
|
+
args = [quote(self.snippet.pk)]
|
|
37
|
+
return reverse(view_name, args=args)
|
|
38
|
+
|
|
39
|
+
def setUp(self):
|
|
40
|
+
self.user = self.login()
|
|
41
|
+
|
|
42
|
+
with freeze_time("2022-05-10 11:00:00"):
|
|
43
|
+
self.snippet = RevisableModel.objects.create(text="The original text")
|
|
44
|
+
self.initial_revision = self.snippet.save_revision(user=self.user)
|
|
45
|
+
ModelLogEntry.objects.create(
|
|
46
|
+
content_type=ContentType.objects.get_for_model(RevisableModel),
|
|
47
|
+
label="The original text",
|
|
48
|
+
action="wagtail.create",
|
|
49
|
+
timestamp=now(),
|
|
50
|
+
object_id=self.snippet.pk,
|
|
51
|
+
revision=self.initial_revision,
|
|
52
|
+
content_changed=True,
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
self.snippet.text = "The edited text"
|
|
56
|
+
self.snippet.save()
|
|
57
|
+
self.edit_revision = self.snippet.save_revision(user=self.user, log_action=True)
|
|
58
|
+
|
|
59
|
+
def test_get_revert_revision(self):
|
|
60
|
+
response = self.get()
|
|
61
|
+
self.assertEqual(response.status_code, 200)
|
|
62
|
+
|
|
63
|
+
if settings.USE_TZ:
|
|
64
|
+
# the default timezone is "Asia/Tokyo", so we expect UTC +9
|
|
65
|
+
expected_date_string = "May 10, 2022, 8 p.m."
|
|
66
|
+
else:
|
|
67
|
+
expected_date_string = "May 10, 2022, 11 a.m."
|
|
68
|
+
|
|
69
|
+
# Message should be shown
|
|
70
|
+
self.assertContains(
|
|
71
|
+
response,
|
|
72
|
+
f"You are viewing a previous version of this Revisable model from <b>{expected_date_string}</b> by",
|
|
73
|
+
count=1,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
# Form should show the content of the revision, not the current draft
|
|
77
|
+
soup = self.get_soup(response.content)
|
|
78
|
+
textarea = soup.select_one("textarea[name='text']")
|
|
79
|
+
self.assertIsNotNone(textarea)
|
|
80
|
+
self.assertEqual(textarea.text.strip(), "The original text")
|
|
81
|
+
|
|
82
|
+
# Form action url should point to the revisions_revert view
|
|
83
|
+
form_tag = f'<form action="{self.revert_url}" method="POST">'
|
|
84
|
+
html = response.content.decode()
|
|
85
|
+
self.assertTagInHTML(form_tag, html, count=1, allow_extra_attrs=True)
|
|
86
|
+
|
|
87
|
+
# Buttons should be relabelled
|
|
88
|
+
self.assertContains(response, "Replace current revision", count=1)
|
|
89
|
+
|
|
90
|
+
soup = self.get_soup(response.content)
|
|
91
|
+
form = soup.select_one("form[data-edit-form]")
|
|
92
|
+
self.assertIsNotNone(form)
|
|
93
|
+
|
|
94
|
+
# Autosave should be disabled
|
|
95
|
+
self.assertNotIn("w-autosave", form["data-controller"].split())
|
|
96
|
+
self.assertNotIn("w-autosave", form["data-action"])
|
|
97
|
+
self.assertIsNone(form.attrs.get("data-w-autosave-interval-value"))
|
|
98
|
+
|
|
99
|
+
def test_get_revert_revision_with_non_revisable_snippet(self):
|
|
100
|
+
snippet = Advert.objects.create(text="foo")
|
|
101
|
+
response = self.client.get(
|
|
102
|
+
f"/admin/snippets/tests/advert/history/{snippet.pk}/revisions/1/revert/"
|
|
103
|
+
)
|
|
104
|
+
self.assertEqual(response.status_code, 404)
|
|
105
|
+
|
|
106
|
+
def test_get_with_limited_permissions(self):
|
|
107
|
+
self.user.is_superuser = False
|
|
108
|
+
self.user.user_permissions.add(
|
|
109
|
+
Permission.objects.get(
|
|
110
|
+
content_type__app_label="wagtailadmin", codename="access_admin"
|
|
111
|
+
)
|
|
112
|
+
)
|
|
113
|
+
self.user.save()
|
|
114
|
+
|
|
115
|
+
response = self.get()
|
|
116
|
+
self.assertEqual(response.status_code, 302)
|
|
117
|
+
|
|
118
|
+
def test_get_with_draft_state_snippet(self):
|
|
119
|
+
self.snippet = DraftStateModel.objects.create(text="Draft-enabled Foo")
|
|
120
|
+
self.initial_revision = self.snippet.save_revision()
|
|
121
|
+
response = self.get()
|
|
122
|
+
|
|
123
|
+
self.assertEqual(response.status_code, 200)
|
|
124
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
|
|
125
|
+
soup = self.get_soup(response.content)
|
|
126
|
+
|
|
127
|
+
# The save button should be labelled "Replace current draft"
|
|
128
|
+
footer = soup.select_one("footer")
|
|
129
|
+
save_button = footer.select_one(
|
|
130
|
+
'button[type="submit"]:not([name="action-publish"])'
|
|
131
|
+
)
|
|
132
|
+
self.assertIsNotNone(save_button)
|
|
133
|
+
self.assertEqual(save_button.text.strip(), "Replace current draft")
|
|
134
|
+
# The publish button should exist and have name="action-publish"
|
|
135
|
+
publish_button = footer.select_one(
|
|
136
|
+
'button[type="submit"][name="action-publish"]'
|
|
137
|
+
)
|
|
138
|
+
self.assertIsNotNone(publish_button)
|
|
139
|
+
self.assertEqual(publish_button.text.strip(), "Publish this version")
|
|
140
|
+
self.assertEqual(
|
|
141
|
+
set(publish_button.get("class")),
|
|
142
|
+
{"button", "action-save", "button-longrunning"},
|
|
143
|
+
)
|
|
144
|
+
|
|
145
|
+
# Should not show the Unpublish action menu item
|
|
146
|
+
unpublish_url = reverse(
|
|
147
|
+
"wagtailsnippets_tests_draftstatemodel:unpublish",
|
|
148
|
+
args=(quote(self.snippet.pk),),
|
|
149
|
+
)
|
|
150
|
+
unpublish_button = footer.select_one(f'a[href="{unpublish_url}"]')
|
|
151
|
+
self.assertIsNone(unpublish_button)
|
|
152
|
+
|
|
153
|
+
def test_get_with_previewable_snippet(self):
|
|
154
|
+
self.snippet = MultiPreviewModesModel.objects.create(text="Preview-enabled foo")
|
|
155
|
+
self.initial_revision = self.snippet.save_revision()
|
|
156
|
+
|
|
157
|
+
self.snippet.text = "Preview-enabled bar"
|
|
158
|
+
self.snippet.save_revision()
|
|
159
|
+
|
|
160
|
+
response = self.get()
|
|
161
|
+
|
|
162
|
+
self.assertEqual(response.status_code, 200)
|
|
163
|
+
self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
|
|
164
|
+
|
|
165
|
+
# Message should be shown
|
|
166
|
+
self.assertContains(
|
|
167
|
+
response,
|
|
168
|
+
"You are viewing a previous version of this",
|
|
169
|
+
count=1,
|
|
170
|
+
)
|
|
171
|
+
|
|
172
|
+
# Form should show the content of the revision, not the current draft
|
|
173
|
+
self.assertContains(response, "Preview-enabled foo")
|
|
174
|
+
|
|
175
|
+
# Form action url should point to the revisions_revert view
|
|
176
|
+
form_tag = f'<form action="{self.revert_url}" method="POST">'
|
|
177
|
+
html = response.content.decode()
|
|
178
|
+
self.assertTagInHTML(form_tag, html, count=1, allow_extra_attrs=True)
|
|
179
|
+
|
|
180
|
+
# Buttons should be relabelled
|
|
181
|
+
self.assertContains(response, "Replace current revision", count=1)
|
|
182
|
+
|
|
183
|
+
# Should show the preview panel
|
|
184
|
+
preview_url = self.get_url("preview_on_edit")
|
|
185
|
+
self.assertContains(response, 'data-side-panel="preview"')
|
|
186
|
+
soup = self.get_soup(response.content)
|
|
187
|
+
controller = soup.select_one('[data-controller="w-preview"]')
|
|
188
|
+
self.assertIsNotNone(controller)
|
|
189
|
+
self.assertEqual(controller.get("data-w-preview-url-value"), preview_url)
|
|
190
|
+
|
|
191
|
+
# Should have the preview side panel toggle button
|
|
192
|
+
toggle_button = soup.find("button", {"data-side-panel-toggle": "preview"})
|
|
193
|
+
self.assertIsNotNone(toggle_button)
|
|
194
|
+
self.assertEqual("w-tooltip w-kbd", toggle_button["data-controller"])
|
|
195
|
+
self.assertEqual("mod+p", toggle_button["data-w-kbd-key-value"])
|
|
196
|
+
|
|
197
|
+
def test_replace_revision(self):
|
|
198
|
+
get_response = self.get()
|
|
199
|
+
text_from_revision = get_response.context["form"].initial["text"]
|
|
200
|
+
|
|
201
|
+
post_response = self.post(
|
|
202
|
+
post_data={
|
|
203
|
+
"text": text_from_revision + " reverted",
|
|
204
|
+
"revision": self.initial_revision.pk,
|
|
205
|
+
}
|
|
206
|
+
)
|
|
207
|
+
self.assertRedirects(post_response, self.get_url("list", args=[]))
|
|
208
|
+
|
|
209
|
+
self.snippet.refresh_from_db()
|
|
210
|
+
latest_revision = self.snippet.get_latest_revision()
|
|
211
|
+
log_entry = ModelLogEntry.objects.filter(revision=latest_revision).first()
|
|
212
|
+
|
|
213
|
+
# The instance should be updated
|
|
214
|
+
self.assertEqual(self.snippet.text, "The original text reverted")
|
|
215
|
+
# The initial revision, edited revision, and revert revision
|
|
216
|
+
self.assertEqual(self.snippet.revisions.count(), 3)
|
|
217
|
+
# The latest revision should be the revert revision
|
|
218
|
+
self.assertEqual(latest_revision.content["text"], "The original text reverted")
|
|
219
|
+
|
|
220
|
+
# A new log entry with "wagtail.revert" action should be created
|
|
221
|
+
self.assertIsNotNone(log_entry)
|
|
222
|
+
self.assertEqual(log_entry.action, "wagtail.revert")
|
|
223
|
+
|
|
224
|
+
def test_replace_with_limited_permissions(self):
|
|
225
|
+
self.user.is_superuser = False
|
|
226
|
+
self.user.user_permissions.add(
|
|
227
|
+
Permission.objects.get(
|
|
228
|
+
content_type__app_label="wagtailadmin", codename="access_admin"
|
|
229
|
+
)
|
|
230
|
+
)
|
|
231
|
+
self.user.save()
|
|
232
|
+
|
|
233
|
+
response = self.post(
|
|
234
|
+
post_data={
|
|
235
|
+
"text": "test text",
|
|
236
|
+
"revision": self.initial_revision.pk,
|
|
237
|
+
}
|
|
238
|
+
)
|
|
239
|
+
self.assertEqual(response.status_code, 302)
|
|
240
|
+
|
|
241
|
+
self.snippet.refresh_from_db()
|
|
242
|
+
self.assertNotEqual(self.snippet.text, "test text")
|
|
243
|
+
|
|
244
|
+
# Only the initial revision and edited revision, no revert revision
|
|
245
|
+
self.assertEqual(self.snippet.revisions.count(), 2)
|
|
246
|
+
|
|
247
|
+
def test_replace_draft(self):
|
|
248
|
+
self.snippet = DraftStateModel.objects.create(
|
|
249
|
+
text="Draft-enabled Foo", live=False
|
|
250
|
+
)
|
|
251
|
+
self.initial_revision = self.snippet.save_revision()
|
|
252
|
+
self.snippet.text = "Draft-enabled Foo edited"
|
|
253
|
+
self.edit_revision = self.snippet.save_revision()
|
|
254
|
+
get_response = self.get()
|
|
255
|
+
text_from_revision = get_response.context["form"].initial["text"]
|
|
256
|
+
|
|
257
|
+
post_response = self.post(
|
|
258
|
+
post_data={
|
|
259
|
+
"text": text_from_revision + " reverted",
|
|
260
|
+
"revision": self.initial_revision.pk,
|
|
261
|
+
}
|
|
262
|
+
)
|
|
263
|
+
self.assertRedirects(post_response, self.get_url("edit"))
|
|
264
|
+
|
|
265
|
+
self.snippet.refresh_from_db()
|
|
266
|
+
latest_revision = self.snippet.get_latest_revision()
|
|
267
|
+
log_entry = ModelLogEntry.objects.filter(revision=latest_revision).first()
|
|
268
|
+
publish_log_entries = ModelLogEntry.objects.filter(
|
|
269
|
+
content_type=ContentType.objects.get_for_model(DraftStateModel),
|
|
270
|
+
action="wagtail.publish",
|
|
271
|
+
object_id=self.snippet.pk,
|
|
272
|
+
)
|
|
273
|
+
|
|
274
|
+
# The instance should be updated, since it is still a draft
|
|
275
|
+
self.assertEqual(self.snippet.text, "Draft-enabled Foo reverted")
|
|
276
|
+
# The initial revision, edited revision, and revert revision
|
|
277
|
+
self.assertEqual(self.snippet.revisions.count(), 3)
|
|
278
|
+
# The latest revision should be the revert revision
|
|
279
|
+
self.assertEqual(latest_revision.content["text"], "Draft-enabled Foo reverted")
|
|
280
|
+
|
|
281
|
+
# A new log entry with "wagtail.revert" action should be created
|
|
282
|
+
self.assertIsNotNone(log_entry)
|
|
283
|
+
self.assertEqual(log_entry.action, "wagtail.revert")
|
|
284
|
+
|
|
285
|
+
# There should be no log entries for the publish action
|
|
286
|
+
self.assertEqual(publish_log_entries.count(), 0)
|
|
287
|
+
|
|
288
|
+
# The instance should still be a draft
|
|
289
|
+
self.assertFalse(self.snippet.live)
|
|
290
|
+
self.assertTrue(self.snippet.has_unpublished_changes)
|
|
291
|
+
self.assertIsNone(self.snippet.first_published_at)
|
|
292
|
+
self.assertIsNone(self.snippet.last_published_at)
|
|
293
|
+
self.assertIsNone(self.snippet.live_revision)
|
|
294
|
+
|
|
295
|
+
def test_replace_publish(self):
|
|
296
|
+
self.snippet = DraftStateModel.objects.create(text="Draft-enabled Foo")
|
|
297
|
+
self.initial_revision = self.snippet.save_revision()
|
|
298
|
+
self.snippet.text = "Draft-enabled Foo edited"
|
|
299
|
+
self.edit_revision = self.snippet.save_revision()
|
|
300
|
+
get_response = self.get()
|
|
301
|
+
text_from_revision = get_response.context["form"].initial["text"]
|
|
302
|
+
|
|
303
|
+
timestamp = now()
|
|
304
|
+
with freeze_time(timestamp):
|
|
305
|
+
post_response = self.post(
|
|
306
|
+
post_data={
|
|
307
|
+
"text": text_from_revision + " reverted",
|
|
308
|
+
"revision": self.initial_revision.pk,
|
|
309
|
+
"action-publish": "action-publish",
|
|
310
|
+
}
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
self.assertRedirects(post_response, self.get_url("list", args=[]))
|
|
314
|
+
|
|
315
|
+
self.snippet.refresh_from_db()
|
|
316
|
+
latest_revision = self.snippet.get_latest_revision()
|
|
317
|
+
log_entry = ModelLogEntry.objects.filter(revision=latest_revision).first()
|
|
318
|
+
revert_log_entries = ModelLogEntry.objects.filter(
|
|
319
|
+
content_type=ContentType.objects.get_for_model(DraftStateModel),
|
|
320
|
+
action="wagtail.revert",
|
|
321
|
+
object_id=self.snippet.pk,
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# The instance should be updated
|
|
325
|
+
self.assertEqual(self.snippet.text, "Draft-enabled Foo reverted")
|
|
326
|
+
# The initial revision, edited revision, and revert revision
|
|
327
|
+
self.assertEqual(self.snippet.revisions.count(), 3)
|
|
328
|
+
# The latest revision should be the revert revision
|
|
329
|
+
self.assertEqual(latest_revision.content["text"], "Draft-enabled Foo reverted")
|
|
330
|
+
|
|
331
|
+
# The latest log entry should use the "wagtail.publish" action
|
|
332
|
+
self.assertIsNotNone(log_entry)
|
|
333
|
+
self.assertEqual(log_entry.action, "wagtail.publish")
|
|
334
|
+
|
|
335
|
+
# There should be a log entry for the revert action
|
|
336
|
+
self.assertEqual(revert_log_entries.count(), 1)
|
|
337
|
+
|
|
338
|
+
# The instance should be live
|
|
339
|
+
self.assertTrue(self.snippet.live)
|
|
340
|
+
self.assertFalse(self.snippet.has_unpublished_changes)
|
|
341
|
+
self.assertEqual(self.snippet.first_published_at, timestamp)
|
|
342
|
+
self.assertEqual(self.snippet.last_published_at, timestamp)
|
|
343
|
+
self.assertEqual(self.snippet.live_revision, self.snippet.latest_revision)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
from django.core import checks
|
|
2
|
+
from django.test import TestCase
|
|
3
|
+
from django.urls import reverse
|
|
4
|
+
|
|
5
|
+
from wagtail.admin.forms import WagtailAdminModelForm
|
|
6
|
+
from wagtail.admin.panels import FieldPanel, get_edit_handler
|
|
7
|
+
from wagtail.snippets.models import SNIPPET_MODELS, register_snippet
|
|
8
|
+
from wagtail.test.snippets.forms import FancySnippetForm
|
|
9
|
+
from wagtail.test.snippets.models import (
|
|
10
|
+
AlphaSnippet,
|
|
11
|
+
FancySnippet,
|
|
12
|
+
RegisterDecorator,
|
|
13
|
+
RegisterFunction,
|
|
14
|
+
StandardSnippet,
|
|
15
|
+
ZuluSnippet,
|
|
16
|
+
)
|
|
17
|
+
from wagtail.test.testapp.models import AdvertWithTabbedInterface
|
|
18
|
+
from wagtail.test.utils import WagtailTestUtils
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class TestModelOrdering(WagtailTestUtils, TestCase):
|
|
22
|
+
def setUp(self):
|
|
23
|
+
for i in range(1, 10):
|
|
24
|
+
AdvertWithTabbedInterface.objects.create(text="advert %d" % i)
|
|
25
|
+
AdvertWithTabbedInterface.objects.create(text="aaaadvert")
|
|
26
|
+
self.login()
|
|
27
|
+
|
|
28
|
+
def test_listing_respects_model_ordering(self):
|
|
29
|
+
response = self.client.get(
|
|
30
|
+
reverse("wagtailsnippets_tests_advertwithtabbedinterface:list")
|
|
31
|
+
)
|
|
32
|
+
self.assertEqual(response.status_code, 200)
|
|
33
|
+
self.assertEqual(response.context["page_obj"][0].text, "aaaadvert")
|
|
34
|
+
|
|
35
|
+
def test_chooser_respects_model_ordering(self):
|
|
36
|
+
response = self.client.get(
|
|
37
|
+
reverse("wagtailsnippetchoosers_tests_advertwithtabbedinterface:choose")
|
|
38
|
+
)
|
|
39
|
+
self.assertEqual(response.status_code, 200)
|
|
40
|
+
self.assertEqual(response.context["results"][0].text, "aaaadvert")
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class TestSnippetRegistering(TestCase):
|
|
44
|
+
def test_register_function(self):
|
|
45
|
+
self.assertIn(RegisterFunction, SNIPPET_MODELS)
|
|
46
|
+
|
|
47
|
+
def test_register_decorator(self):
|
|
48
|
+
# Misbehaving decorators often return None
|
|
49
|
+
self.assertIsNotNone(RegisterDecorator)
|
|
50
|
+
self.assertIn(RegisterDecorator, SNIPPET_MODELS)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class TestSnippetOrdering(TestCase):
|
|
54
|
+
def setUp(self):
|
|
55
|
+
register_snippet(ZuluSnippet)
|
|
56
|
+
register_snippet(AlphaSnippet)
|
|
57
|
+
|
|
58
|
+
def test_snippets_ordering(self):
|
|
59
|
+
# Ensure AlphaSnippet is before ZuluSnippet
|
|
60
|
+
# Cannot check first and last position as other snippets
|
|
61
|
+
# may get registered elsewhere during test
|
|
62
|
+
self.assertLess(
|
|
63
|
+
SNIPPET_MODELS.index(AlphaSnippet), SNIPPET_MODELS.index(ZuluSnippet)
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class TestSnippetEditHandlers(WagtailTestUtils, TestCase):
|
|
68
|
+
def test_standard_edit_handler(self):
|
|
69
|
+
edit_handler = get_edit_handler(StandardSnippet)
|
|
70
|
+
form_class = edit_handler.get_form_class()
|
|
71
|
+
self.assertTrue(issubclass(form_class, WagtailAdminModelForm))
|
|
72
|
+
self.assertFalse(issubclass(form_class, FancySnippetForm))
|
|
73
|
+
|
|
74
|
+
def test_fancy_edit_handler(self):
|
|
75
|
+
edit_handler = get_edit_handler(FancySnippet)
|
|
76
|
+
form_class = edit_handler.get_form_class()
|
|
77
|
+
self.assertTrue(issubclass(form_class, WagtailAdminModelForm))
|
|
78
|
+
self.assertTrue(issubclass(form_class, FancySnippetForm))
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TestPanelConfigurationChecks(WagtailTestUtils, TestCase):
|
|
82
|
+
def setUp(self):
|
|
83
|
+
self.warning_id = "wagtailadmin.W002"
|
|
84
|
+
|
|
85
|
+
def get_checks_result():
|
|
86
|
+
# run checks only with the 'panels' tag
|
|
87
|
+
checks_result = checks.run_checks(tags=["panels"])
|
|
88
|
+
return [
|
|
89
|
+
warning for warning in checks_result if warning.id == self.warning_id
|
|
90
|
+
]
|
|
91
|
+
|
|
92
|
+
self.get_checks_result = get_checks_result
|
|
93
|
+
|
|
94
|
+
def test_model_with_single_tabbed_panel_only(self):
|
|
95
|
+
StandardSnippet.content_panels = [FieldPanel("text")]
|
|
96
|
+
|
|
97
|
+
warning = checks.Warning(
|
|
98
|
+
"StandardSnippet.content_panels will have no effect on snippets editing",
|
|
99
|
+
hint="""Ensure that StandardSnippet uses `panels` instead of `content_panels` \
|
|
100
|
+
or set up an `edit_handler` if you want a tabbed editing interface.
|
|
101
|
+
There are no default tabs on non-Page models so there will be no\
|
|
102
|
+
Content tab for the content_panels to render in.""",
|
|
103
|
+
obj=StandardSnippet,
|
|
104
|
+
id="wagtailadmin.W002",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
checks_results = self.get_checks_result()
|
|
108
|
+
|
|
109
|
+
self.assertEqual([warning], checks_results)
|
|
110
|
+
|
|
111
|
+
# clean up for future checks
|
|
112
|
+
delattr(StandardSnippet, "content_panels")
|