wagtail 6.1.3__py3-none-any.whl → 6.2rc1__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 +15 -1
- wagtail/admin/checks.py +20 -30
- wagtail/admin/forms/pages.py +10 -0
- wagtail/admin/icons.py +43 -0
- wagtail/admin/locale/en/LC_MESSAGES/django.po +405 -295
- wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +21 -3
- wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/admin/locale/sl/LC_MESSAGES/django.po +30 -0
- wagtail/admin/menu.py +2 -2
- wagtail/admin/migrations/0004_editingsession.py +57 -0
- wagtail/admin/migrations/0005_editingsession_is_editing.py +18 -0
- wagtail/admin/models.py +36 -3
- wagtail/admin/rich_text/editors/draftail/__init__.py +2 -20
- wagtail/admin/static/wagtailadmin/css/core.css +1 -1
- 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/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 +2 -1
- wagtail/admin/static/wagtailadmin/js/preview-panel.js.LICENSE.txt +11 -0
- 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/widgets.js +1 -1
- wagtail/admin/static/wagtailadmin/js/userbar.js +2 -1
- wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +11 -0
- wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
- wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +0 -12
- wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
- wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
- wagtail/admin/templates/wagtailadmin/collection_privacy/ancestor_privacy.html +2 -6
- wagtail/admin/templates/wagtailadmin/generic/index_results.html +1 -17
- wagtail/admin/templates/wagtailadmin/generic/listing_results.html +20 -1
- wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +2 -11
- wagtail/admin/templates/wagtailadmin/page_privacy/ancestor_privacy.html +2 -6
- wagtail/admin/templates/wagtailadmin/page_privacy/no_privacy.html +2 -0
- wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -1
- wagtail/admin/templates/wagtailadmin/pages/action_menu/menu.html +1 -1
- wagtail/admin/templates/wagtailadmin/reports/aging_pages_results.html +54 -0
- wagtail/admin/templates/wagtailadmin/reports/base_page_report.html +1 -17
- wagtail/admin/templates/wagtailadmin/reports/base_page_report_results.html +10 -0
- wagtail/admin/templates/wagtailadmin/reports/base_report.html +1 -40
- wagtail/admin/templates/wagtailadmin/reports/base_report_results.html +1 -0
- wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_report.html +21 -27
- wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_types_usage.html +48 -54
- wagtail/admin/templates/wagtailadmin/reports/{locked_pages.html → locked_pages_results.html} +3 -3
- wagtail/admin/templates/wagtailadmin/reports/page_types_usage_results.html +10 -0
- wagtail/admin/templates/wagtailadmin/reports/site_history_results.html +53 -0
- wagtail/admin/templates/wagtailadmin/reports/workflow_results.html +74 -0
- wagtail/admin/templates/wagtailadmin/reports/workflow_tasks_results.html +56 -0
- wagtail/admin/templates/wagtailadmin/shared/_workflow_init.html +8 -44
- wagtail/admin/templates/wagtailadmin/shared/avatar.html +11 -1
- wagtail/admin/templates/wagtailadmin/shared/dialog/dialog.html +5 -4
- wagtail/admin/templates/wagtailadmin/shared/dropdown/dropdown_button.html +2 -1
- wagtail/admin/templates/wagtailadmin/shared/editing_sessions/list.html +132 -0
- wagtail/admin/templates/wagtailadmin/shared/editing_sessions/module.html +44 -0
- wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +7 -1
- wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +1 -1
- wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html +32 -16
- wagtail/admin/templates/wagtailadmin/skeleton.html +1 -1
- wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +9 -11
- wagtail/admin/templatetags/wagtailadmin_tags.py +13 -2
- wagtail/admin/tests/formats/en/__init__.py +0 -0
- wagtail/admin/tests/formats/en/formats.py +1 -0
- wagtail/admin/tests/pages/test_create_page.py +47 -0
- wagtail/admin/tests/pages/test_edit_page.py +10 -8
- wagtail/admin/tests/pages/test_parent_page_chooser_view.py +45 -1
- wagtail/admin/tests/test_checks.py +53 -3
- wagtail/admin/tests/test_collections_views.py +62 -1
- wagtail/admin/tests/test_edit_handlers.py +37 -0
- wagtail/admin/tests/test_editing_sessions.py +1336 -0
- wagtail/admin/tests/test_icon_sprite.py +12 -21
- wagtail/admin/tests/test_page_chooser.py +309 -7
- wagtail/admin/tests/test_privacy.py +82 -0
- wagtail/admin/tests/test_reports_views.py +464 -70
- wagtail/admin/tests/test_userbar.py +93 -6
- wagtail/admin/tests/test_workflows.py +223 -33
- wagtail/admin/tests/viewsets/test_model_viewset.py +151 -2
- wagtail/admin/ui/editing_sessions.py +57 -0
- wagtail/admin/urls/__init__.py +9 -15
- wagtail/admin/urls/editing_sessions.py +17 -0
- wagtail/admin/urls/reports.py +33 -1
- wagtail/admin/userbar.py +77 -20
- wagtail/admin/views/chooser.py +49 -22
- wagtail/admin/views/collections.py +0 -11
- wagtail/admin/views/editing_sessions.py +193 -0
- wagtail/admin/views/generic/__init__.py +1 -0
- wagtail/admin/views/generic/history.py +9 -3
- wagtail/admin/views/generic/mixins.py +44 -3
- wagtail/admin/views/generic/models.py +46 -72
- wagtail/admin/views/generic/permissions.py +20 -10
- wagtail/admin/views/home.py +2 -31
- wagtail/admin/views/page_privacy.py +20 -5
- wagtail/admin/views/pages/choose_parent.py +62 -0
- wagtail/admin/views/pages/edit.py +28 -0
- wagtail/admin/views/reports/aging_pages.py +6 -10
- wagtail/admin/views/reports/audit_logging.py +13 -42
- wagtail/admin/views/reports/base.py +31 -4
- wagtail/admin/views/reports/locked_pages.py +5 -8
- wagtail/admin/views/reports/page_types_usage.py +6 -10
- wagtail/admin/views/reports/workflows.py +36 -12
- wagtail/admin/viewsets/base.py +8 -3
- wagtail/admin/viewsets/chooser.py +1 -1
- wagtail/admin/viewsets/model.py +26 -1
- wagtail/admin/wagtail_hooks.py +2 -1
- wagtail/api/v2/filters.py +6 -0
- wagtail/api/v2/tests/test_documents.py +1 -1
- wagtail/api/v2/tests/test_images.py +1 -1
- wagtail/api/v2/tests/test_pages.py +11 -1
- wagtail/api/v2/utils.py +2 -2
- wagtail/blocks/base.py +35 -12
- wagtail/blocks/definition_lookup.py +85 -0
- wagtail/blocks/list_block.py +12 -0
- wagtail/blocks/migrations/migrate_operation.py +2 -0
- wagtail/blocks/stream_block.py +19 -0
- wagtail/blocks/struct_block.py +19 -0
- wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/frontend_cache/backends/__init__.py +5 -0
- wagtail/contrib/frontend_cache/backends/azure.py +179 -0
- wagtail/contrib/frontend_cache/backends/base.py +28 -0
- wagtail/contrib/frontend_cache/backends/cloudflare.py +114 -0
- wagtail/contrib/frontend_cache/backends/cloudfront.py +99 -0
- wagtail/contrib/frontend_cache/backends/http.py +64 -0
- wagtail/contrib/frontend_cache/tests.py +59 -17
- wagtail/contrib/frontend_cache/utils.py +26 -8
- wagtail/contrib/redirects/filters.py +15 -1
- wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +37 -72
- wagtail/contrib/redirects/models.py +6 -5
- wagtail/contrib/redirects/templates/wagtailredirects/edit.html +1 -38
- wagtail/contrib/redirects/tests/test_redirects.py +141 -1
- wagtail/contrib/redirects/urls.py +1 -2
- wagtail/contrib/redirects/views.py +39 -80
- wagtail/contrib/routable_page/models.py +6 -4
- wagtail/contrib/routable_page/tests.py +11 -0
- wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +4 -4
- wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +5 -1
- wagtail/contrib/simple_translation/models.py +2 -1
- wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
- 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/blocks.py +19 -0
- wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
- wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
- wagtail/contrib/typed_table_block/tests.py +38 -0
- wagtail/coreutils.py +1 -1
- wagtail/documents/__init__.py +1 -1
- wagtail/documents/locale/en/LC_MESSAGES/django.po +8 -8
- wagtail/documents/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/documents/locale/sl/LC_MESSAGES/django.po +20 -0
- wagtail/documents/models.py +5 -1
- 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/tests/test_models.py +5 -1
- wagtail/embeds/apps.py +2 -0
- wagtail/embeds/embeds.py +12 -14
- wagtail/embeds/finders/__init__.py +2 -0
- wagtail/embeds/finders/facebook.py +17 -33
- wagtail/embeds/finders/instagram.py +19 -16
- wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/embeds/signal_handlers.py +13 -0
- wagtail/embeds/tests/test_embeds.py +7 -7
- wagtail/fields.py +58 -14
- wagtail/images/__init__.py +1 -1
- wagtail/images/locale/en/LC_MESSAGES/django.po +34 -34
- wagtail/images/locale/sl/LC_MESSAGES/django.mo +0 -0
- wagtail/images/locale/sl/LC_MESSAGES/django.po +20 -0
- wagtail/images/models.py +2 -0
- 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/edit.html +4 -4
- wagtail/images/tests/test_admin_views.py +26 -2
- wagtail/images/views/chooser.py +6 -1
- wagtail/locale/en/LC_MESSAGES/django.po +84 -80
- wagtail/locales/locale/en/LC_MESSAGES/django.po +2 -2
- wagtail/locales/tests.py +16 -0
- wagtail/locales/wagtail_hooks.py +0 -9
- wagtail/migrations/0094_alter_page_locale.py +19 -0
- wagtail/models/__init__.py +11 -5
- wagtail/models/i18n.py +6 -1
- wagtail/project_template/requirements.txt +1 -1
- wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
- wagtail/signals.py +4 -0
- wagtail/sites/locale/en/LC_MESSAGES/django.po +2 -2
- wagtail/sites/tests.py +15 -0
- wagtail/sites/wagtail_hooks.py +0 -9
- wagtail/snippets/locale/en/LC_MESSAGES/django.po +9 -9
- wagtail/snippets/permissions.py +5 -3
- 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/menu.html +1 -1
- wagtail/snippets/tests/test_snippets.py +78 -12
- wagtail/snippets/tests/test_viewset.py +22 -0
- wagtail/snippets/views/snippets.py +19 -14
- wagtail/snippets/wagtail_hooks.py +2 -10
- wagtail/templatetags/wagtailcore_tags.py +3 -0
- wagtail/test/dummy_external_storage.py +1 -1
- wagtail/test/i18n/migrations/0003_alter_clusterabletestmodel_locale_and_more.py +40 -0
- wagtail/test/routablepage/models.py +4 -0
- wagtail/test/snippets/migrations/0012_alter_translatablesnippet_locale.py +20 -0
- wagtail/test/testapp/migrations/0038_sociallink.py +52 -0
- wagtail/test/testapp/migrations/0039_alter_eventcategory_locale_and_more.py +45 -0
- wagtail/test/testapp/models.py +24 -0
- wagtail/test/testapp/views.py +1 -0
- wagtail/test/testapp/wagtail_hooks.py +9 -0
- wagtail/test/urls_multilang.py +6 -1
- wagtail/test/urls_multilang_non_root.py +11 -0
- wagtail/tests/streamfield_migrations/test_migrations.py +53 -12
- wagtail/tests/test_audit_log.py +72 -2
- wagtail/tests/test_blocks.py +103 -0
- wagtail/tests/test_signals.py +49 -2
- wagtail/tests/test_streamfield.py +153 -0
- wagtail/tests/test_utils.py +14 -0
- wagtail/tests/tests.py +5 -0
- wagtail/users/apps.py +1 -0
- wagtail/users/forms.py +7 -0
- wagtail/users/locale/en/LC_MESSAGES/django.po +55 -50
- wagtail/users/models.py +1 -0
- wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +3 -2
- wagtail/users/templatetags/wagtailusers_tags.py +9 -0
- wagtail/users/tests/__init__.py +7 -1
- wagtail/users/tests/test_admin_views.py +117 -32
- wagtail/users/views/groups.py +4 -0
- wagtail/users/views/users.py +58 -14
- wagtail/users/wagtail_hooks.py +7 -123
- wagtail/utils/utils.py +1 -0
- wagtail/utils/version.py +5 -2
- {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/METADATA +3 -3
- {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/RECORD +248 -222
- wagtail/admin/templates/wagtailadmin/reports/aging_pages.html +0 -58
- wagtail/admin/templates/wagtailadmin/reports/page_types_usage.html +0 -18
- wagtail/admin/templates/wagtailadmin/reports/site_history.html +0 -57
- wagtail/admin/templates/wagtailadmin/reports/workflow.html +0 -81
- wagtail/admin/templates/wagtailadmin/reports/workflow_tasks.html +0 -63
- wagtail/contrib/frontend_cache/backends.py +0 -400
- wagtail/contrib/redirects/templates/wagtailredirects/list.html +0 -43
- wagtail/contrib/redirects/templates/wagtailredirects/reports/redirects_report.html +0 -14
- wagtail/contrib/redirects/tests/test_reports_view.py +0 -82
- {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/LICENSE +0 -0
- {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/WHEEL +0 -0
- {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/entry_points.txt +0 -0
- {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,1336 @@
|
|
|
1
|
+
import datetime
|
|
2
|
+
|
|
3
|
+
from django.conf import settings
|
|
4
|
+
from django.contrib.admin.utils import quote
|
|
5
|
+
from django.contrib.auth.models import Group, Permission
|
|
6
|
+
from django.contrib.contenttypes.models import ContentType
|
|
7
|
+
from django.test import TestCase, override_settings
|
|
8
|
+
from django.urls import reverse
|
|
9
|
+
from django.utils import timezone
|
|
10
|
+
from freezegun import freeze_time
|
|
11
|
+
|
|
12
|
+
from wagtail.admin.models import EditingSession
|
|
13
|
+
from wagtail.models import GroupPagePermission, Page
|
|
14
|
+
from wagtail.test.testapp.models import (
|
|
15
|
+
Advert,
|
|
16
|
+
AdvertWithCustomPrimaryKey,
|
|
17
|
+
FullFeaturedSnippet,
|
|
18
|
+
SimplePage,
|
|
19
|
+
)
|
|
20
|
+
from wagtail.test.utils import WagtailTestUtils
|
|
21
|
+
|
|
22
|
+
if settings.USE_TZ:
|
|
23
|
+
TIMESTAMP_ANCIENT = timezone.make_aware(
|
|
24
|
+
datetime.datetime(2019, 1, 1, 10, 30, 0), timezone=datetime.timezone.utc
|
|
25
|
+
)
|
|
26
|
+
TIMESTAMP_PAST = timezone.make_aware(
|
|
27
|
+
datetime.datetime(2020, 1, 1, 10, 30, 0), timezone=datetime.timezone.utc
|
|
28
|
+
)
|
|
29
|
+
TIMESTAMP_1 = timezone.make_aware(
|
|
30
|
+
datetime.datetime(2020, 1, 1, 11, 59, 51), timezone=datetime.timezone.utc
|
|
31
|
+
)
|
|
32
|
+
TIMESTAMP_2 = timezone.make_aware(
|
|
33
|
+
datetime.datetime(2020, 1, 1, 11, 59, 52), timezone=datetime.timezone.utc
|
|
34
|
+
)
|
|
35
|
+
TIMESTAMP_3 = timezone.make_aware(
|
|
36
|
+
datetime.datetime(2020, 1, 1, 11, 59, 53), timezone=datetime.timezone.utc
|
|
37
|
+
)
|
|
38
|
+
TIMESTAMP_4 = timezone.make_aware(
|
|
39
|
+
datetime.datetime(2020, 1, 1, 11, 59, 54), timezone=datetime.timezone.utc
|
|
40
|
+
)
|
|
41
|
+
TIMESTAMP_NOW = timezone.make_aware(
|
|
42
|
+
datetime.datetime(2020, 1, 1, 12, 0, 0), timezone=datetime.timezone.utc
|
|
43
|
+
)
|
|
44
|
+
else:
|
|
45
|
+
TIMESTAMP_ANCIENT = datetime.datetime(2019, 1, 1, 10, 30, 0)
|
|
46
|
+
TIMESTAMP_PAST = datetime.datetime(2020, 1, 1, 10, 30, 0)
|
|
47
|
+
TIMESTAMP_1 = datetime.datetime(2020, 1, 1, 11, 59, 51)
|
|
48
|
+
TIMESTAMP_2 = datetime.datetime(2020, 1, 1, 11, 59, 52)
|
|
49
|
+
TIMESTAMP_3 = datetime.datetime(2020, 1, 1, 11, 59, 53)
|
|
50
|
+
TIMESTAMP_4 = datetime.datetime(2020, 1, 1, 11, 59, 54)
|
|
51
|
+
TIMESTAMP_NOW = datetime.datetime(2020, 1, 1, 12, 0, 0)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class TestPingView(WagtailTestUtils, TestCase):
|
|
55
|
+
def setUp(self):
|
|
56
|
+
self.user = self.create_superuser(
|
|
57
|
+
"bob", password="password", first_name="Bob", last_name="Testuser"
|
|
58
|
+
)
|
|
59
|
+
self.other_user = self.create_user(
|
|
60
|
+
"vic", password="password", first_name="Vic", last_name="Otheruser"
|
|
61
|
+
)
|
|
62
|
+
self.third_user = self.create_user(
|
|
63
|
+
"gordon", password="password", first_name="Gordon", last_name="Thirduser"
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
self.login(user=self.user)
|
|
67
|
+
self.root_page = Page.get_first_root_node()
|
|
68
|
+
|
|
69
|
+
self.page = SimplePage(title="Test page", slug="test-page", content="test page")
|
|
70
|
+
self.root_page.add_child(instance=self.page)
|
|
71
|
+
|
|
72
|
+
with freeze_time(TIMESTAMP_ANCIENT):
|
|
73
|
+
self.original_revision = self.page.save_revision(user=self.other_user)
|
|
74
|
+
|
|
75
|
+
with freeze_time(TIMESTAMP_PAST):
|
|
76
|
+
self.original_revision = self.page.save_revision(user=self.user)
|
|
77
|
+
|
|
78
|
+
self.other_page = SimplePage(
|
|
79
|
+
title="Other page", slug="other-page", content="other page"
|
|
80
|
+
)
|
|
81
|
+
self.root_page.add_child(instance=self.other_page)
|
|
82
|
+
|
|
83
|
+
page_content_type = ContentType.objects.get_for_model(Page)
|
|
84
|
+
|
|
85
|
+
self.session = EditingSession.objects.create(
|
|
86
|
+
user=self.user,
|
|
87
|
+
content_type=page_content_type,
|
|
88
|
+
object_id=self.page.id,
|
|
89
|
+
last_seen_at=TIMESTAMP_1,
|
|
90
|
+
)
|
|
91
|
+
self.other_session = EditingSession.objects.create(
|
|
92
|
+
user=self.other_user,
|
|
93
|
+
content_type=page_content_type,
|
|
94
|
+
object_id=self.page.id,
|
|
95
|
+
last_seen_at=TIMESTAMP_2,
|
|
96
|
+
)
|
|
97
|
+
self.old_session = EditingSession.objects.create(
|
|
98
|
+
user=self.other_user,
|
|
99
|
+
content_type=page_content_type,
|
|
100
|
+
object_id=self.page.id,
|
|
101
|
+
last_seen_at=TIMESTAMP_PAST,
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
def test_ping_invalid_model(self):
|
|
105
|
+
response = self.client.post(
|
|
106
|
+
reverse(
|
|
107
|
+
"wagtailadmin_editing_sessions:ping",
|
|
108
|
+
args=("testapp", "invalidmodel", str(self.page.id), self.session.id),
|
|
109
|
+
)
|
|
110
|
+
)
|
|
111
|
+
self.assertEqual(response.status_code, 404)
|
|
112
|
+
|
|
113
|
+
def test_ping_non_page_non_snippet_model(self):
|
|
114
|
+
editors = Group.objects.get(name="Editors")
|
|
115
|
+
session = EditingSession.objects.create(
|
|
116
|
+
user=self.user,
|
|
117
|
+
content_type=ContentType.objects.get_for_model(Group),
|
|
118
|
+
object_id=editors.pk,
|
|
119
|
+
last_seen_at=TIMESTAMP_1,
|
|
120
|
+
)
|
|
121
|
+
response = self.client.post(
|
|
122
|
+
reverse(
|
|
123
|
+
"wagtailadmin_editing_sessions:ping",
|
|
124
|
+
args=("auth", "group", str(editors.pk), session.id),
|
|
125
|
+
)
|
|
126
|
+
)
|
|
127
|
+
self.assertEqual(response.status_code, 404)
|
|
128
|
+
|
|
129
|
+
def test_ping_non_existent_object(self):
|
|
130
|
+
response = self.client.post(
|
|
131
|
+
reverse(
|
|
132
|
+
"wagtailadmin_editing_sessions:ping",
|
|
133
|
+
args=("wagtailcore", "page", 999999, self.session.id),
|
|
134
|
+
)
|
|
135
|
+
)
|
|
136
|
+
self.assertEqual(response.status_code, 404)
|
|
137
|
+
|
|
138
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
139
|
+
def test_ping_existing_session(self):
|
|
140
|
+
response = self.client.post(
|
|
141
|
+
reverse(
|
|
142
|
+
"wagtailadmin_editing_sessions:ping",
|
|
143
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
144
|
+
)
|
|
145
|
+
)
|
|
146
|
+
self.assertEqual(response.status_code, 200)
|
|
147
|
+
response_json = response.json()
|
|
148
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
149
|
+
self.assertEqual(
|
|
150
|
+
response_json["other_sessions"],
|
|
151
|
+
[
|
|
152
|
+
{
|
|
153
|
+
"session_id": self.other_session.id,
|
|
154
|
+
"user": "Vic Otheruser",
|
|
155
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
156
|
+
"is_editing": False,
|
|
157
|
+
"revision_id": None,
|
|
158
|
+
},
|
|
159
|
+
],
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
soup = self.get_soup(response_json["html"])
|
|
163
|
+
rendered_sessions = soup.select("ol.w-editing-sessions__list li")
|
|
164
|
+
self.assertEqual(len(rendered_sessions), 1)
|
|
165
|
+
session_text = rendered_sessions[0].text
|
|
166
|
+
self.assertIn("Vic Otheruser", session_text)
|
|
167
|
+
self.assertIn("Currently viewing", session_text)
|
|
168
|
+
|
|
169
|
+
self.session.refresh_from_db()
|
|
170
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
171
|
+
self.assertFalse(self.session.is_editing)
|
|
172
|
+
|
|
173
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
174
|
+
def test_ping_existing_session_with_editing_flag(self):
|
|
175
|
+
response = self.client.post(
|
|
176
|
+
reverse(
|
|
177
|
+
"wagtailadmin_editing_sessions:ping",
|
|
178
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
179
|
+
),
|
|
180
|
+
{"is_editing": "1"},
|
|
181
|
+
)
|
|
182
|
+
self.assertEqual(response.status_code, 200)
|
|
183
|
+
response_json = response.json()
|
|
184
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
185
|
+
self.assertEqual(
|
|
186
|
+
response_json["other_sessions"],
|
|
187
|
+
[
|
|
188
|
+
# Should not cause any changes to the other sessions list,
|
|
189
|
+
# as the current session is the one that is editing
|
|
190
|
+
{
|
|
191
|
+
"session_id": self.other_session.id,
|
|
192
|
+
"user": "Vic Otheruser",
|
|
193
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
194
|
+
"is_editing": False,
|
|
195
|
+
"revision_id": None,
|
|
196
|
+
},
|
|
197
|
+
],
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
soup = self.get_soup(response_json["html"])
|
|
201
|
+
rendered_sessions = soup.select("ol.w-editing-sessions__list li")
|
|
202
|
+
self.assertEqual(len(rendered_sessions), 1)
|
|
203
|
+
session_text = rendered_sessions[0].text
|
|
204
|
+
self.assertIn("Vic Otheruser", session_text)
|
|
205
|
+
self.assertIn("Currently viewing", session_text)
|
|
206
|
+
|
|
207
|
+
self.session.refresh_from_db()
|
|
208
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
209
|
+
self.assertTrue(self.session.is_editing)
|
|
210
|
+
|
|
211
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
212
|
+
def test_ping_with_revision(self):
|
|
213
|
+
response = self.client.post(
|
|
214
|
+
reverse(
|
|
215
|
+
"wagtailadmin_editing_sessions:ping",
|
|
216
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
217
|
+
),
|
|
218
|
+
{"revision_id": self.original_revision.id},
|
|
219
|
+
)
|
|
220
|
+
self.assertEqual(response.status_code, 200)
|
|
221
|
+
response_json = response.json()
|
|
222
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
223
|
+
|
|
224
|
+
# no revisions have been saved since the original revision
|
|
225
|
+
self.assertEqual(
|
|
226
|
+
response_json["other_sessions"],
|
|
227
|
+
[
|
|
228
|
+
{
|
|
229
|
+
"session_id": self.other_session.id,
|
|
230
|
+
"user": "Vic Otheruser",
|
|
231
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
232
|
+
"is_editing": False,
|
|
233
|
+
"revision_id": None,
|
|
234
|
+
},
|
|
235
|
+
],
|
|
236
|
+
)
|
|
237
|
+
|
|
238
|
+
soup = self.get_soup(response_json["html"])
|
|
239
|
+
rendered_sessions = soup.select("ol.w-editing-sessions__list li")
|
|
240
|
+
self.assertEqual(len(rendered_sessions), 1)
|
|
241
|
+
session_text = rendered_sessions[0].text
|
|
242
|
+
self.assertIn("Vic Otheruser", session_text)
|
|
243
|
+
self.assertIn("Currently viewing", session_text)
|
|
244
|
+
self.assertNotIn("saved a new version", session_text)
|
|
245
|
+
|
|
246
|
+
self.session.refresh_from_db()
|
|
247
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
248
|
+
self.assertFalse(self.session.is_editing)
|
|
249
|
+
|
|
250
|
+
with freeze_time(TIMESTAMP_3):
|
|
251
|
+
new_revision = self.page.save_revision(user=self.other_user)
|
|
252
|
+
|
|
253
|
+
response = self.client.post(
|
|
254
|
+
reverse(
|
|
255
|
+
"wagtailadmin_editing_sessions:ping",
|
|
256
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
257
|
+
),
|
|
258
|
+
{"revision_id": self.original_revision.id},
|
|
259
|
+
)
|
|
260
|
+
self.assertEqual(response.status_code, 200)
|
|
261
|
+
response_json = response.json()
|
|
262
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
263
|
+
|
|
264
|
+
# the new revision should be indicated in the response (and last_seen_at should reflect it)
|
|
265
|
+
self.assertEqual(
|
|
266
|
+
response_json["other_sessions"],
|
|
267
|
+
[
|
|
268
|
+
{
|
|
269
|
+
"session_id": self.other_session.id,
|
|
270
|
+
"user": "Vic Otheruser",
|
|
271
|
+
"last_seen_at": TIMESTAMP_3.isoformat(),
|
|
272
|
+
"is_editing": False,
|
|
273
|
+
"revision_id": new_revision.id,
|
|
274
|
+
},
|
|
275
|
+
],
|
|
276
|
+
)
|
|
277
|
+
|
|
278
|
+
soup = self.get_soup(response_json["html"])
|
|
279
|
+
rendered_sessions = soup.select("ol.w-editing-sessions__list li")
|
|
280
|
+
self.assertEqual(len(rendered_sessions), 1)
|
|
281
|
+
session_text = rendered_sessions[0].text
|
|
282
|
+
self.assertIn("Vic Otheruser saved a new version", session_text)
|
|
283
|
+
self.assertNotIn("Currently viewing", session_text)
|
|
284
|
+
dialog_title = soup.select_one(
|
|
285
|
+
'template[data-w-teleport-target-value="#title-text-w-overwrite-changes-dialog"]'
|
|
286
|
+
)
|
|
287
|
+
self.assertIsNotNone(dialog_title)
|
|
288
|
+
self.assertIn(
|
|
289
|
+
"Vic Otheruser has saved a newer version of this page",
|
|
290
|
+
dialog_title.string,
|
|
291
|
+
)
|
|
292
|
+
dialog_subtitle = soup.select_one(
|
|
293
|
+
'template[data-w-teleport-target-value="#subtitle-w-overwrite-changes-dialog"]'
|
|
294
|
+
)
|
|
295
|
+
self.assertIsNotNone(dialog_subtitle)
|
|
296
|
+
self.assertIn(
|
|
297
|
+
"Proceeding will overwrite the changes made by Vic Otheruser. "
|
|
298
|
+
"Refreshing the page will lose any of your unsaved changes.",
|
|
299
|
+
dialog_subtitle.string,
|
|
300
|
+
)
|
|
301
|
+
|
|
302
|
+
self.session.refresh_from_db()
|
|
303
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
304
|
+
self.assertFalse(self.session.is_editing)
|
|
305
|
+
|
|
306
|
+
self.other_session.delete()
|
|
307
|
+
|
|
308
|
+
response = self.client.post(
|
|
309
|
+
reverse(
|
|
310
|
+
"wagtailadmin_editing_sessions:ping",
|
|
311
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
312
|
+
),
|
|
313
|
+
{"revision_id": self.original_revision.id},
|
|
314
|
+
)
|
|
315
|
+
self.assertEqual(response.status_code, 200)
|
|
316
|
+
response_json = response.json()
|
|
317
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
318
|
+
|
|
319
|
+
# the new revision should still appear as an "other session" in the response,
|
|
320
|
+
# even though the editing session record has been deleted
|
|
321
|
+
self.assertEqual(
|
|
322
|
+
response_json["other_sessions"],
|
|
323
|
+
[
|
|
324
|
+
{
|
|
325
|
+
"session_id": None,
|
|
326
|
+
"user": "Vic Otheruser",
|
|
327
|
+
"last_seen_at": TIMESTAMP_3.isoformat(),
|
|
328
|
+
"is_editing": False,
|
|
329
|
+
"revision_id": new_revision.id,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
)
|
|
333
|
+
|
|
334
|
+
soup = self.get_soup(response_json["html"])
|
|
335
|
+
rendered_sessions = soup.select("ol.w-editing-sessions__list li")
|
|
336
|
+
self.assertEqual(len(rendered_sessions), 1)
|
|
337
|
+
session_text = rendered_sessions[0].text
|
|
338
|
+
self.assertIn("Vic Otheruser saved a new version", session_text)
|
|
339
|
+
self.assertNotIn("Currently viewing", session_text)
|
|
340
|
+
dialog_title = soup.select_one(
|
|
341
|
+
'template[data-w-teleport-target-value="#title-text-w-overwrite-changes-dialog"]'
|
|
342
|
+
)
|
|
343
|
+
self.assertIsNotNone(dialog_title)
|
|
344
|
+
self.assertIn(
|
|
345
|
+
"Vic Otheruser has saved a newer version of this page",
|
|
346
|
+
dialog_title.string,
|
|
347
|
+
)
|
|
348
|
+
dialog_subtitle = soup.select_one(
|
|
349
|
+
'template[data-w-teleport-target-value="#subtitle-w-overwrite-changes-dialog"]'
|
|
350
|
+
)
|
|
351
|
+
self.assertIsNotNone(dialog_subtitle)
|
|
352
|
+
self.assertIn(
|
|
353
|
+
"Proceeding will overwrite the changes made by Vic Otheruser. "
|
|
354
|
+
"Refreshing the page will lose any of your unsaved changes.",
|
|
355
|
+
dialog_subtitle.string,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
self.session.refresh_from_db()
|
|
359
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
360
|
+
self.assertFalse(self.session.is_editing)
|
|
361
|
+
|
|
362
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
363
|
+
def test_ping_with_multiple_revisions_since_own_revision(self):
|
|
364
|
+
# Create a new revision with the other_user
|
|
365
|
+
with freeze_time(TIMESTAMP_3):
|
|
366
|
+
self.page.save_revision(user=self.other_user)
|
|
367
|
+
|
|
368
|
+
# Create a new session with the third_user, and save a revision too
|
|
369
|
+
third_session = EditingSession.objects.create(
|
|
370
|
+
user=self.third_user,
|
|
371
|
+
content_type=ContentType.objects.get_for_model(Page),
|
|
372
|
+
object_id=self.page.id,
|
|
373
|
+
last_seen_at=TIMESTAMP_3,
|
|
374
|
+
)
|
|
375
|
+
with freeze_time(TIMESTAMP_4):
|
|
376
|
+
latest_revision = self.page.save_revision(user=self.third_user)
|
|
377
|
+
|
|
378
|
+
response = self.client.post(
|
|
379
|
+
reverse(
|
|
380
|
+
"wagtailadmin_editing_sessions:ping",
|
|
381
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
382
|
+
),
|
|
383
|
+
{"revision_id": self.original_revision.id},
|
|
384
|
+
)
|
|
385
|
+
self.assertEqual(response.status_code, 200)
|
|
386
|
+
response_json = response.json()
|
|
387
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
388
|
+
|
|
389
|
+
# The revision_id should only be set for the session with the latest revision
|
|
390
|
+
self.assertEqual(
|
|
391
|
+
response_json["other_sessions"],
|
|
392
|
+
[
|
|
393
|
+
{
|
|
394
|
+
# The third_session has a newer ID, but is shown first
|
|
395
|
+
# because it has a revision_id set
|
|
396
|
+
"session_id": third_session.id,
|
|
397
|
+
"user": "Gordon Thirduser",
|
|
398
|
+
"last_seen_at": TIMESTAMP_4.isoformat(),
|
|
399
|
+
"is_editing": False,
|
|
400
|
+
"revision_id": latest_revision.id,
|
|
401
|
+
},
|
|
402
|
+
{
|
|
403
|
+
"session_id": self.other_session.id,
|
|
404
|
+
"user": "Vic Otheruser",
|
|
405
|
+
# The timestamp isn't updated for the other_session and it
|
|
406
|
+
# doesn't have a revision_id. This is because we don't care
|
|
407
|
+
# about the fact that this user created a new revision if
|
|
408
|
+
# it's not the latest one.
|
|
409
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
410
|
+
"is_editing": False,
|
|
411
|
+
"revision_id": None,
|
|
412
|
+
},
|
|
413
|
+
],
|
|
414
|
+
)
|
|
415
|
+
|
|
416
|
+
soup = self.get_soup(response_json["html"])
|
|
417
|
+
rendered_sessions = soup.select("ol.w-editing-sessions__list li")
|
|
418
|
+
self.assertEqual(len(rendered_sessions), 2)
|
|
419
|
+
session_text = rendered_sessions[0].text
|
|
420
|
+
self.assertIn("Gordon Thirduser saved a new version", session_text)
|
|
421
|
+
self.assertNotIn("Currently viewing", session_text)
|
|
422
|
+
dialog_title = soup.select_one(
|
|
423
|
+
'template[data-w-teleport-target-value="#title-text-w-overwrite-changes-dialog"]'
|
|
424
|
+
)
|
|
425
|
+
self.assertIsNotNone(dialog_title)
|
|
426
|
+
self.assertIn(
|
|
427
|
+
"Gordon Thirduser has saved a newer version of this page",
|
|
428
|
+
dialog_title.string,
|
|
429
|
+
)
|
|
430
|
+
dialog_subtitle = soup.select_one(
|
|
431
|
+
'template[data-w-teleport-target-value="#subtitle-w-overwrite-changes-dialog"]'
|
|
432
|
+
)
|
|
433
|
+
self.assertIsNotNone(dialog_subtitle)
|
|
434
|
+
self.assertIn(
|
|
435
|
+
"Proceeding will overwrite the changes made by Gordon Thirduser. "
|
|
436
|
+
"Refreshing the page will lose any of your unsaved changes.",
|
|
437
|
+
dialog_subtitle.string,
|
|
438
|
+
)
|
|
439
|
+
other_session_text = rendered_sessions[1].text
|
|
440
|
+
self.assertIn("Vic Otheruser", other_session_text)
|
|
441
|
+
self.assertIn("Currently viewing", other_session_text)
|
|
442
|
+
self.assertNotIn("saved a new version", other_session_text)
|
|
443
|
+
|
|
444
|
+
self.session.refresh_from_db()
|
|
445
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
446
|
+
self.assertFalse(self.session.is_editing)
|
|
447
|
+
|
|
448
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
449
|
+
def test_ping_with_new_revision_that_has_no_user(self):
|
|
450
|
+
# Create a new revision without any user
|
|
451
|
+
with freeze_time(TIMESTAMP_3):
|
|
452
|
+
latest_revision = self.page.save_revision()
|
|
453
|
+
|
|
454
|
+
response = self.client.post(
|
|
455
|
+
reverse(
|
|
456
|
+
"wagtailadmin_editing_sessions:ping",
|
|
457
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
458
|
+
),
|
|
459
|
+
{"revision_id": self.original_revision.id},
|
|
460
|
+
)
|
|
461
|
+
self.assertEqual(response.status_code, 200)
|
|
462
|
+
response_json = response.json()
|
|
463
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
464
|
+
|
|
465
|
+
self.assertEqual(
|
|
466
|
+
response_json["other_sessions"],
|
|
467
|
+
[
|
|
468
|
+
{
|
|
469
|
+
# Should work even if the revision has no associated user
|
|
470
|
+
"session_id": None,
|
|
471
|
+
"user": "",
|
|
472
|
+
"last_seen_at": TIMESTAMP_3.isoformat(),
|
|
473
|
+
"is_editing": False,
|
|
474
|
+
"revision_id": latest_revision.id,
|
|
475
|
+
},
|
|
476
|
+
{
|
|
477
|
+
"session_id": self.other_session.id,
|
|
478
|
+
"user": "Vic Otheruser",
|
|
479
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
480
|
+
"is_editing": False,
|
|
481
|
+
"revision_id": None,
|
|
482
|
+
},
|
|
483
|
+
],
|
|
484
|
+
)
|
|
485
|
+
|
|
486
|
+
soup = self.get_soup(response_json["html"])
|
|
487
|
+
rendered_sessions = soup.select("ol.w-editing-sessions__list li")
|
|
488
|
+
self.assertEqual(len(rendered_sessions), 2)
|
|
489
|
+
session_text = rendered_sessions[0].text
|
|
490
|
+
self.assertIn("System saved a new version", session_text)
|
|
491
|
+
self.assertNotIn("Currently viewing", session_text)
|
|
492
|
+
dialog_title = soup.select_one(
|
|
493
|
+
'template[data-w-teleport-target-value="#title-text-w-overwrite-changes-dialog"]'
|
|
494
|
+
)
|
|
495
|
+
self.assertIsNotNone(dialog_title)
|
|
496
|
+
self.assertIn(
|
|
497
|
+
"System has saved a newer version of this page",
|
|
498
|
+
dialog_title.string,
|
|
499
|
+
)
|
|
500
|
+
dialog_subtitle = soup.select_one(
|
|
501
|
+
'template[data-w-teleport-target-value="#subtitle-w-overwrite-changes-dialog"]'
|
|
502
|
+
)
|
|
503
|
+
self.assertIsNotNone(dialog_subtitle)
|
|
504
|
+
self.assertIn(
|
|
505
|
+
"Proceeding will overwrite the changes made by System. "
|
|
506
|
+
"Refreshing the page will lose any of your unsaved changes.",
|
|
507
|
+
dialog_subtitle.string,
|
|
508
|
+
)
|
|
509
|
+
other_session_text = rendered_sessions[1].text
|
|
510
|
+
self.assertIn("Vic Otheruser", other_session_text)
|
|
511
|
+
self.assertIn("Currently viewing", other_session_text)
|
|
512
|
+
self.assertNotIn("saved a new version", other_session_text)
|
|
513
|
+
|
|
514
|
+
self.session.refresh_from_db()
|
|
515
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
516
|
+
self.assertFalse(self.session.is_editing)
|
|
517
|
+
|
|
518
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
519
|
+
def test_ping_session_ordering(self):
|
|
520
|
+
fourth_user = self.create_user(
|
|
521
|
+
"alyx", password="password", first_name="Alyx", last_name="Fourthuser"
|
|
522
|
+
)
|
|
523
|
+
fifth_user = self.create_user(
|
|
524
|
+
"chell", password="password", first_name="Chell", last_name="Fifthuser"
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
third_session = EditingSession.objects.create(
|
|
528
|
+
user=self.third_user,
|
|
529
|
+
content_type=ContentType.objects.get_for_model(Page),
|
|
530
|
+
object_id=self.page.id,
|
|
531
|
+
last_seen_at=TIMESTAMP_2,
|
|
532
|
+
)
|
|
533
|
+
fourth_session = EditingSession.objects.create(
|
|
534
|
+
user=fourth_user,
|
|
535
|
+
content_type=ContentType.objects.get_for_model(Page),
|
|
536
|
+
object_id=self.page.id,
|
|
537
|
+
# newer ping but not the last one to be created
|
|
538
|
+
last_seen_at=TIMESTAMP_1,
|
|
539
|
+
)
|
|
540
|
+
fifth_session = EditingSession.objects.create(
|
|
541
|
+
user=fifth_user,
|
|
542
|
+
content_type=ContentType.objects.get_for_model(Page),
|
|
543
|
+
object_id=self.page.id,
|
|
544
|
+
last_seen_at=TIMESTAMP_4,
|
|
545
|
+
is_editing=True,
|
|
546
|
+
)
|
|
547
|
+
|
|
548
|
+
with freeze_time(TIMESTAMP_3):
|
|
549
|
+
new_revision = self.page.save_revision(user=self.third_user)
|
|
550
|
+
|
|
551
|
+
response = self.client.post(
|
|
552
|
+
reverse(
|
|
553
|
+
"wagtailadmin_editing_sessions:ping",
|
|
554
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
555
|
+
),
|
|
556
|
+
{"revision_id": self.original_revision.id},
|
|
557
|
+
)
|
|
558
|
+
self.assertEqual(response.status_code, 200)
|
|
559
|
+
response_json = response.json()
|
|
560
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
561
|
+
self.assertEqual(
|
|
562
|
+
response_json["other_sessions"],
|
|
563
|
+
[
|
|
564
|
+
# The session with the new revision should be shown first
|
|
565
|
+
{
|
|
566
|
+
"session_id": third_session.id,
|
|
567
|
+
"user": "Gordon Thirduser",
|
|
568
|
+
"last_seen_at": TIMESTAMP_3.isoformat(),
|
|
569
|
+
"is_editing": False,
|
|
570
|
+
"revision_id": new_revision.id,
|
|
571
|
+
},
|
|
572
|
+
# Then any sessions that are currently editing
|
|
573
|
+
{
|
|
574
|
+
"session_id": fifth_session.id,
|
|
575
|
+
"user": "Chell Fifthuser",
|
|
576
|
+
"last_seen_at": TIMESTAMP_4.isoformat(),
|
|
577
|
+
"is_editing": True,
|
|
578
|
+
"revision_id": None,
|
|
579
|
+
},
|
|
580
|
+
# Then any other sessions, sorted ascending by session_id
|
|
581
|
+
{
|
|
582
|
+
"session_id": self.other_session.id,
|
|
583
|
+
"user": "Vic Otheruser",
|
|
584
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
585
|
+
"is_editing": False,
|
|
586
|
+
"revision_id": None,
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
"session_id": fourth_session.id,
|
|
590
|
+
"user": "Alyx Fourthuser",
|
|
591
|
+
"last_seen_at": TIMESTAMP_1.isoformat(),
|
|
592
|
+
"is_editing": False,
|
|
593
|
+
"revision_id": None,
|
|
594
|
+
},
|
|
595
|
+
],
|
|
596
|
+
)
|
|
597
|
+
self.session.refresh_from_db()
|
|
598
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
599
|
+
self.assertFalse(self.session.is_editing)
|
|
600
|
+
|
|
601
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
602
|
+
def test_ping_new_session(self):
|
|
603
|
+
response = self.client.post(
|
|
604
|
+
reverse(
|
|
605
|
+
"wagtailadmin_editing_sessions:ping",
|
|
606
|
+
args=("wagtailcore", "page", self.page.id, 999999),
|
|
607
|
+
)
|
|
608
|
+
)
|
|
609
|
+
self.assertEqual(response.status_code, 200)
|
|
610
|
+
response_json = response.json()
|
|
611
|
+
new_session_id = response_json["session_id"]
|
|
612
|
+
session = EditingSession.objects.get(id=new_session_id)
|
|
613
|
+
self.assertEqual(session.user, self.user)
|
|
614
|
+
self.assertEqual(session.last_seen_at, TIMESTAMP_NOW)
|
|
615
|
+
self.assertFalse(session.is_editing)
|
|
616
|
+
|
|
617
|
+
self.assertEqual(
|
|
618
|
+
response_json["other_sessions"],
|
|
619
|
+
[
|
|
620
|
+
# The user's original session is not shown as it is not
|
|
621
|
+
# currently editing nor has it created the latest revision
|
|
622
|
+
{
|
|
623
|
+
"session_id": self.other_session.id,
|
|
624
|
+
"user": "Vic Otheruser",
|
|
625
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
626
|
+
"is_editing": False,
|
|
627
|
+
"revision_id": None,
|
|
628
|
+
},
|
|
629
|
+
],
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
# Should include the new URLs for the new session
|
|
633
|
+
self.assertEqual(
|
|
634
|
+
response_json["ping_url"],
|
|
635
|
+
reverse(
|
|
636
|
+
"wagtailadmin_editing_sessions:ping",
|
|
637
|
+
args=("wagtailcore", "page", self.page.id, session.id),
|
|
638
|
+
),
|
|
639
|
+
)
|
|
640
|
+
|
|
641
|
+
self.assertEqual(
|
|
642
|
+
response_json["release_url"],
|
|
643
|
+
reverse(
|
|
644
|
+
"wagtailadmin_editing_sessions:release",
|
|
645
|
+
args=(session.id,),
|
|
646
|
+
),
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
# content_object is a non-specific Page object
|
|
650
|
+
self.assertEqual(type(session.content_object), Page)
|
|
651
|
+
self.assertEqual(session.content_object.id, self.page.id)
|
|
652
|
+
|
|
653
|
+
self.assertEqual(session.last_seen_at, TIMESTAMP_NOW)
|
|
654
|
+
|
|
655
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
656
|
+
def test_ping_new_session_with_editing_flag(self):
|
|
657
|
+
response = self.client.post(
|
|
658
|
+
reverse(
|
|
659
|
+
"wagtailadmin_editing_sessions:ping",
|
|
660
|
+
args=("wagtailcore", "page", self.page.id, 999999),
|
|
661
|
+
),
|
|
662
|
+
{"is_editing": "1"},
|
|
663
|
+
)
|
|
664
|
+
self.assertEqual(response.status_code, 200)
|
|
665
|
+
response_json = response.json()
|
|
666
|
+
new_session_id = response_json["session_id"]
|
|
667
|
+
session = EditingSession.objects.get(id=new_session_id)
|
|
668
|
+
self.assertEqual(session.user, self.user)
|
|
669
|
+
self.assertEqual(session.last_seen_at, TIMESTAMP_NOW)
|
|
670
|
+
self.assertTrue(session.is_editing)
|
|
671
|
+
|
|
672
|
+
self.assertEqual(
|
|
673
|
+
response_json["other_sessions"],
|
|
674
|
+
[
|
|
675
|
+
# The user's original session is not shown as it is not
|
|
676
|
+
# currently editing nor has it created the latest revision
|
|
677
|
+
{
|
|
678
|
+
"session_id": self.other_session.id,
|
|
679
|
+
"user": "Vic Otheruser",
|
|
680
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
681
|
+
"is_editing": False,
|
|
682
|
+
"revision_id": None,
|
|
683
|
+
},
|
|
684
|
+
],
|
|
685
|
+
)
|
|
686
|
+
|
|
687
|
+
# content_object is a non-specific Page object
|
|
688
|
+
self.assertEqual(type(session.content_object), Page)
|
|
689
|
+
self.assertEqual(session.content_object.id, self.page.id)
|
|
690
|
+
|
|
691
|
+
self.assertEqual(session.last_seen_at, TIMESTAMP_NOW)
|
|
692
|
+
|
|
693
|
+
# The original session should not be changed
|
|
694
|
+
self.session.refresh_from_db()
|
|
695
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_1)
|
|
696
|
+
self.assertFalse(self.session.is_editing)
|
|
697
|
+
|
|
698
|
+
# Ping with the original session
|
|
699
|
+
response = self.client.post(
|
|
700
|
+
reverse(
|
|
701
|
+
"wagtailadmin_editing_sessions:ping",
|
|
702
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
703
|
+
)
|
|
704
|
+
)
|
|
705
|
+
self.assertEqual(response.status_code, 200)
|
|
706
|
+
response_json = response.json()
|
|
707
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
708
|
+
self.assertEqual(
|
|
709
|
+
response_json["other_sessions"],
|
|
710
|
+
[
|
|
711
|
+
# The new session should be shown as it is currently editing
|
|
712
|
+
{
|
|
713
|
+
"session_id": session.id,
|
|
714
|
+
"user": "Bob Testuser",
|
|
715
|
+
"last_seen_at": TIMESTAMP_NOW.isoformat(),
|
|
716
|
+
"is_editing": True,
|
|
717
|
+
"revision_id": None,
|
|
718
|
+
},
|
|
719
|
+
{
|
|
720
|
+
"session_id": self.other_session.id,
|
|
721
|
+
"user": "Vic Otheruser",
|
|
722
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
723
|
+
"is_editing": False,
|
|
724
|
+
"revision_id": None,
|
|
725
|
+
},
|
|
726
|
+
],
|
|
727
|
+
)
|
|
728
|
+
|
|
729
|
+
soup = self.get_soup(response_json["html"])
|
|
730
|
+
rendered_sessions = soup.select("ol.w-editing-sessions__list li")
|
|
731
|
+
self.assertEqual(len(rendered_sessions), 2)
|
|
732
|
+
session_text = rendered_sessions[0].text
|
|
733
|
+
self.assertIn("You have unsaved changes in another session", session_text)
|
|
734
|
+
self.assertNotIn("Currently viewing", session_text)
|
|
735
|
+
dialog_title = soup.select_one(
|
|
736
|
+
'template[data-w-teleport-target-value="#title-text-w-overwrite-changes-dialog"]'
|
|
737
|
+
)
|
|
738
|
+
self.assertIsNone(dialog_title)
|
|
739
|
+
dialog_subtitle = soup.select_one(
|
|
740
|
+
'template[data-w-teleport-target-value="#subtitle-w-overwrite-changes-dialog"]'
|
|
741
|
+
)
|
|
742
|
+
self.assertIsNone(dialog_subtitle)
|
|
743
|
+
other_session_text = rendered_sessions[1].text
|
|
744
|
+
self.assertIn("Vic Otheruser", other_session_text)
|
|
745
|
+
self.assertIn("Currently viewing", other_session_text)
|
|
746
|
+
self.assertNotIn("saved a new version", other_session_text)
|
|
747
|
+
|
|
748
|
+
self.session.refresh_from_db()
|
|
749
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
750
|
+
self.assertFalse(self.session.is_editing)
|
|
751
|
+
|
|
752
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
753
|
+
def test_ping_new_session_with_revision(self):
|
|
754
|
+
response = self.client.post(
|
|
755
|
+
reverse(
|
|
756
|
+
"wagtailadmin_editing_sessions:ping",
|
|
757
|
+
args=("wagtailcore", "page", self.page.id, 999999),
|
|
758
|
+
),
|
|
759
|
+
{"revision_id": self.original_revision.id},
|
|
760
|
+
)
|
|
761
|
+
self.assertEqual(response.status_code, 200)
|
|
762
|
+
response_json = response.json()
|
|
763
|
+
new_session_id = response_json["session_id"]
|
|
764
|
+
session = EditingSession.objects.get(id=new_session_id)
|
|
765
|
+
self.assertEqual(session.user, self.user)
|
|
766
|
+
self.assertEqual(session.last_seen_at, TIMESTAMP_NOW)
|
|
767
|
+
self.assertFalse(session.is_editing)
|
|
768
|
+
|
|
769
|
+
self.assertEqual(
|
|
770
|
+
response_json["other_sessions"],
|
|
771
|
+
[
|
|
772
|
+
# The user's original session is not shown as it is not
|
|
773
|
+
# currently editing nor has it created the latest revision
|
|
774
|
+
{
|
|
775
|
+
"session_id": self.other_session.id,
|
|
776
|
+
"user": "Vic Otheruser",
|
|
777
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
778
|
+
"is_editing": False,
|
|
779
|
+
"revision_id": None,
|
|
780
|
+
},
|
|
781
|
+
],
|
|
782
|
+
)
|
|
783
|
+
|
|
784
|
+
# content_object is a non-specific Page object
|
|
785
|
+
self.assertEqual(type(session.content_object), Page)
|
|
786
|
+
self.assertEqual(session.content_object.id, self.page.id)
|
|
787
|
+
|
|
788
|
+
self.assertEqual(session.last_seen_at, TIMESTAMP_NOW)
|
|
789
|
+
|
|
790
|
+
# The original session should not be changed
|
|
791
|
+
self.session.refresh_from_db()
|
|
792
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_1)
|
|
793
|
+
self.assertFalse(self.session.is_editing)
|
|
794
|
+
|
|
795
|
+
# Ping with the original session
|
|
796
|
+
response = self.client.post(
|
|
797
|
+
reverse(
|
|
798
|
+
"wagtailadmin_editing_sessions:ping",
|
|
799
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
800
|
+
)
|
|
801
|
+
)
|
|
802
|
+
self.assertEqual(response.status_code, 200)
|
|
803
|
+
response_json = response.json()
|
|
804
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
805
|
+
self.assertEqual(
|
|
806
|
+
response_json["other_sessions"],
|
|
807
|
+
[
|
|
808
|
+
# The new session is not shown as it is not
|
|
809
|
+
# currently editing nor has it created the latest revision
|
|
810
|
+
{
|
|
811
|
+
"session_id": self.other_session.id,
|
|
812
|
+
"user": "Vic Otheruser",
|
|
813
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
814
|
+
"is_editing": False,
|
|
815
|
+
"revision_id": None,
|
|
816
|
+
},
|
|
817
|
+
],
|
|
818
|
+
)
|
|
819
|
+
self.session.refresh_from_db()
|
|
820
|
+
self.assertEqual(self.session.last_seen_at, TIMESTAMP_NOW)
|
|
821
|
+
self.assertFalse(self.session.is_editing)
|
|
822
|
+
|
|
823
|
+
# Save a new revision as the current user
|
|
824
|
+
with freeze_time(TIMESTAMP_4):
|
|
825
|
+
new_revision = self.page.save_revision(user=self.user)
|
|
826
|
+
|
|
827
|
+
# Ping with the previously "new" session
|
|
828
|
+
response = self.client.post(
|
|
829
|
+
reverse(
|
|
830
|
+
"wagtailadmin_editing_sessions:ping",
|
|
831
|
+
args=("wagtailcore", "page", self.page.id, new_session_id),
|
|
832
|
+
),
|
|
833
|
+
{"revision_id": self.original_revision.id},
|
|
834
|
+
)
|
|
835
|
+
self.assertEqual(response.status_code, 200)
|
|
836
|
+
response_json = response.json()
|
|
837
|
+
self.assertEqual(response_json["session_id"], new_session_id)
|
|
838
|
+
|
|
839
|
+
self.assertEqual(
|
|
840
|
+
response_json["other_sessions"],
|
|
841
|
+
[
|
|
842
|
+
# The new revision should be indicated in the response.
|
|
843
|
+
# In this case, it's attached to the original session. This may
|
|
844
|
+
# not be exactly true, i.e. the new revision might not be created
|
|
845
|
+
# by the original session. However, we don't keep track of which
|
|
846
|
+
# session created which revision, and we don't really need to.
|
|
847
|
+
# All we need to know is that there is a new revision since the
|
|
848
|
+
# one the session has in hand. As the new revision happens to
|
|
849
|
+
# be created by self.user, and the only other session we have
|
|
850
|
+
# of that user is the original session (self.session), we attach
|
|
851
|
+
# the new revision to that session.
|
|
852
|
+
{
|
|
853
|
+
"session_id": self.session.id,
|
|
854
|
+
"user": "Bob Testuser",
|
|
855
|
+
"last_seen_at": TIMESTAMP_NOW.isoformat(),
|
|
856
|
+
"is_editing": False,
|
|
857
|
+
"revision_id": new_revision.id,
|
|
858
|
+
},
|
|
859
|
+
{
|
|
860
|
+
"session_id": self.other_session.id,
|
|
861
|
+
"user": "Vic Otheruser",
|
|
862
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
863
|
+
"is_editing": False,
|
|
864
|
+
"revision_id": None,
|
|
865
|
+
},
|
|
866
|
+
],
|
|
867
|
+
)
|
|
868
|
+
|
|
869
|
+
# Ping with the original self.session
|
|
870
|
+
response = self.client.post(
|
|
871
|
+
reverse(
|
|
872
|
+
"wagtailadmin_editing_sessions:ping",
|
|
873
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
874
|
+
),
|
|
875
|
+
{"revision_id": self.original_revision.id},
|
|
876
|
+
)
|
|
877
|
+
self.assertEqual(response.status_code, 200)
|
|
878
|
+
response_json = response.json()
|
|
879
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
880
|
+
|
|
881
|
+
self.assertEqual(
|
|
882
|
+
response_json["other_sessions"],
|
|
883
|
+
[
|
|
884
|
+
# In the eye of the original session, the new revision is
|
|
885
|
+
# attached to the new session (for the same reason as the
|
|
886
|
+
# previous explanation).
|
|
887
|
+
{
|
|
888
|
+
"session_id": new_session_id,
|
|
889
|
+
"user": "Bob Testuser",
|
|
890
|
+
"last_seen_at": TIMESTAMP_NOW.isoformat(),
|
|
891
|
+
"is_editing": False,
|
|
892
|
+
"revision_id": new_revision.id,
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
"session_id": self.other_session.id,
|
|
896
|
+
"user": "Vic Otheruser",
|
|
897
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
898
|
+
"is_editing": False,
|
|
899
|
+
"revision_id": None,
|
|
900
|
+
},
|
|
901
|
+
],
|
|
902
|
+
)
|
|
903
|
+
|
|
904
|
+
# Delete the new session
|
|
905
|
+
session.delete()
|
|
906
|
+
|
|
907
|
+
# Ping with the original self.session
|
|
908
|
+
response = self.client.post(
|
|
909
|
+
reverse(
|
|
910
|
+
"wagtailadmin_editing_sessions:ping",
|
|
911
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
912
|
+
),
|
|
913
|
+
{"revision_id": self.original_revision.id},
|
|
914
|
+
)
|
|
915
|
+
self.assertEqual(response.status_code, 200)
|
|
916
|
+
response_json = response.json()
|
|
917
|
+
self.assertEqual(response_json["session_id"], self.session.id)
|
|
918
|
+
|
|
919
|
+
self.assertEqual(
|
|
920
|
+
response_json["other_sessions"],
|
|
921
|
+
[
|
|
922
|
+
# The 'other' session of the same user is still shown
|
|
923
|
+
# as it has the latest revision, even though there are no
|
|
924
|
+
# other sessions to attach the revision to. The last_seen_at
|
|
925
|
+
# is set to the time of the revision's creation.
|
|
926
|
+
{
|
|
927
|
+
"session_id": None,
|
|
928
|
+
"user": "Bob Testuser",
|
|
929
|
+
"last_seen_at": TIMESTAMP_4.isoformat(),
|
|
930
|
+
"is_editing": False,
|
|
931
|
+
"revision_id": new_revision.id,
|
|
932
|
+
},
|
|
933
|
+
{
|
|
934
|
+
"session_id": self.other_session.id,
|
|
935
|
+
"user": "Vic Otheruser",
|
|
936
|
+
"last_seen_at": TIMESTAMP_2.isoformat(),
|
|
937
|
+
"is_editing": False,
|
|
938
|
+
"revision_id": None,
|
|
939
|
+
},
|
|
940
|
+
],
|
|
941
|
+
)
|
|
942
|
+
|
|
943
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
944
|
+
def test_user_must_have_edit_permission_on_page(self):
|
|
945
|
+
# make user a member of Editors
|
|
946
|
+
self.user.is_superuser = False
|
|
947
|
+
self.user.save()
|
|
948
|
+
editors = Group.objects.get(name="Editors")
|
|
949
|
+
self.user.groups.add(editors)
|
|
950
|
+
|
|
951
|
+
# give the Editors group edit permision on other_page only
|
|
952
|
+
GroupPagePermission.objects.filter(group=editors).delete()
|
|
953
|
+
GroupPagePermission.objects.create(
|
|
954
|
+
group=editors,
|
|
955
|
+
page=self.other_page,
|
|
956
|
+
permission=Permission.objects.get(codename="change_page"),
|
|
957
|
+
)
|
|
958
|
+
|
|
959
|
+
response = self.client.post(
|
|
960
|
+
reverse(
|
|
961
|
+
"wagtailadmin_editing_sessions:ping",
|
|
962
|
+
args=("wagtailcore", "page", self.page.id, 999999),
|
|
963
|
+
)
|
|
964
|
+
)
|
|
965
|
+
self.assertEqual(response.status_code, 404)
|
|
966
|
+
|
|
967
|
+
response = self.client.post(
|
|
968
|
+
reverse(
|
|
969
|
+
"wagtailadmin_editing_sessions:ping",
|
|
970
|
+
args=("wagtailcore", "page", self.other_page.id, 999999),
|
|
971
|
+
)
|
|
972
|
+
)
|
|
973
|
+
self.assertEqual(response.status_code, 200)
|
|
974
|
+
|
|
975
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
976
|
+
def test_ping_snippet_model(self):
|
|
977
|
+
snippet = Advert.objects.create(text="Test snippet")
|
|
978
|
+
|
|
979
|
+
# make user a member of Editors
|
|
980
|
+
self.user.is_superuser = False
|
|
981
|
+
self.user.save()
|
|
982
|
+
editors = Group.objects.get(name="Editors")
|
|
983
|
+
self.user.groups.add(editors)
|
|
984
|
+
|
|
985
|
+
editors.permissions.add(
|
|
986
|
+
Permission.objects.get(codename="change_advert"),
|
|
987
|
+
)
|
|
988
|
+
|
|
989
|
+
session = EditingSession.objects.create(
|
|
990
|
+
user=self.user,
|
|
991
|
+
content_type=ContentType.objects.get_for_model(Advert),
|
|
992
|
+
object_id=snippet.pk,
|
|
993
|
+
last_seen_at=TIMESTAMP_1,
|
|
994
|
+
)
|
|
995
|
+
# add two sessions from other_user to test that we correctly merge them into
|
|
996
|
+
# one record in the response
|
|
997
|
+
EditingSession.objects.create(
|
|
998
|
+
user=self.other_user,
|
|
999
|
+
content_type=ContentType.objects.get_for_model(Advert),
|
|
1000
|
+
object_id=snippet.pk,
|
|
1001
|
+
last_seen_at=TIMESTAMP_2,
|
|
1002
|
+
is_editing=True,
|
|
1003
|
+
)
|
|
1004
|
+
other_session_2 = EditingSession.objects.create(
|
|
1005
|
+
user=self.other_user,
|
|
1006
|
+
content_type=ContentType.objects.get_for_model(Advert),
|
|
1007
|
+
object_id=snippet.pk,
|
|
1008
|
+
last_seen_at=TIMESTAMP_3,
|
|
1009
|
+
is_editing=False,
|
|
1010
|
+
)
|
|
1011
|
+
|
|
1012
|
+
# session with last_seen_at too far in the past to be included in the response
|
|
1013
|
+
EditingSession.objects.create(
|
|
1014
|
+
user=self.other_user,
|
|
1015
|
+
content_type=ContentType.objects.get_for_model(Advert),
|
|
1016
|
+
object_id=snippet.pk,
|
|
1017
|
+
last_seen_at=TIMESTAMP_PAST,
|
|
1018
|
+
)
|
|
1019
|
+
response = self.client.post(
|
|
1020
|
+
reverse(
|
|
1021
|
+
"wagtailadmin_editing_sessions:ping",
|
|
1022
|
+
args=("tests", "advert", str(snippet.pk), session.id),
|
|
1023
|
+
)
|
|
1024
|
+
)
|
|
1025
|
+
self.assertEqual(response.status_code, 200)
|
|
1026
|
+
response_json = response.json()
|
|
1027
|
+
self.assertEqual(response_json["session_id"], session.id)
|
|
1028
|
+
self.assertEqual(
|
|
1029
|
+
response_json["other_sessions"],
|
|
1030
|
+
[
|
|
1031
|
+
{
|
|
1032
|
+
"session_id": other_session_2.id,
|
|
1033
|
+
"user": "Vic Otheruser",
|
|
1034
|
+
"last_seen_at": TIMESTAMP_3.isoformat(),
|
|
1035
|
+
"is_editing": True,
|
|
1036
|
+
"revision_id": None,
|
|
1037
|
+
},
|
|
1038
|
+
],
|
|
1039
|
+
)
|
|
1040
|
+
session.refresh_from_db()
|
|
1041
|
+
self.assertEqual(session.last_seen_at, TIMESTAMP_NOW)
|
|
1042
|
+
self.assertFalse(session.is_editing)
|
|
1043
|
+
|
|
1044
|
+
def test_ping_snippet_model_without_permission(self):
|
|
1045
|
+
snippet = Advert.objects.create(text="Test snippet")
|
|
1046
|
+
|
|
1047
|
+
# make user a member of Editors
|
|
1048
|
+
self.user.is_superuser = False
|
|
1049
|
+
self.user.save()
|
|
1050
|
+
editors = Group.objects.get(name="Editors")
|
|
1051
|
+
self.user.groups.add(editors)
|
|
1052
|
+
|
|
1053
|
+
session = EditingSession.objects.create(
|
|
1054
|
+
user=self.user,
|
|
1055
|
+
content_type=ContentType.objects.get_for_model(Advert),
|
|
1056
|
+
object_id=snippet.pk,
|
|
1057
|
+
last_seen_at=TIMESTAMP_1,
|
|
1058
|
+
)
|
|
1059
|
+
response = self.client.post(
|
|
1060
|
+
reverse(
|
|
1061
|
+
"wagtailadmin_editing_sessions:ping",
|
|
1062
|
+
args=("tests", "advert", str(snippet.pk), session.id),
|
|
1063
|
+
)
|
|
1064
|
+
)
|
|
1065
|
+
self.assertEqual(response.status_code, 404)
|
|
1066
|
+
|
|
1067
|
+
def test_must_post(self):
|
|
1068
|
+
response = self.client.get(
|
|
1069
|
+
reverse(
|
|
1070
|
+
"wagtailadmin_editing_sessions:ping",
|
|
1071
|
+
args=("wagtailcore", "page", self.page.id, 999999),
|
|
1072
|
+
)
|
|
1073
|
+
)
|
|
1074
|
+
self.assertEqual(response.status_code, 405)
|
|
1075
|
+
self.assertCountEqual(
|
|
1076
|
+
EditingSession.objects.all(),
|
|
1077
|
+
[self.session, self.other_session, self.old_session],
|
|
1078
|
+
)
|
|
1079
|
+
|
|
1080
|
+
def test_invalid_data(self):
|
|
1081
|
+
response = self.client.post(
|
|
1082
|
+
reverse(
|
|
1083
|
+
"wagtailadmin_editing_sessions:ping",
|
|
1084
|
+
args=("wagtailcore", "page", self.page.id, self.session.id),
|
|
1085
|
+
),
|
|
1086
|
+
{"is_editing": "invalid"},
|
|
1087
|
+
)
|
|
1088
|
+
self.assertEqual(response.status_code, 400)
|
|
1089
|
+
self.assertEqual(response.json(), {"error": "Invalid data"})
|
|
1090
|
+
self.assertCountEqual(
|
|
1091
|
+
EditingSession.objects.all(),
|
|
1092
|
+
[self.session, self.other_session, self.old_session],
|
|
1093
|
+
)
|
|
1094
|
+
|
|
1095
|
+
|
|
1096
|
+
class TestCleanup(WagtailTestUtils, TestCase):
|
|
1097
|
+
def setUp(self):
|
|
1098
|
+
self.user = self.create_superuser(
|
|
1099
|
+
"bob", password="password", first_name="Bob", last_name="Testuser"
|
|
1100
|
+
)
|
|
1101
|
+
self.root_page = Page.get_first_root_node()
|
|
1102
|
+
|
|
1103
|
+
self.page = SimplePage(title="Test page", slug="test-page", content="test page")
|
|
1104
|
+
self.root_page.add_child(instance=self.page)
|
|
1105
|
+
|
|
1106
|
+
page_content_type = ContentType.objects.get_for_model(Page)
|
|
1107
|
+
|
|
1108
|
+
self.session = EditingSession.objects.create(
|
|
1109
|
+
user=self.user,
|
|
1110
|
+
content_type=page_content_type,
|
|
1111
|
+
object_id=self.page.id,
|
|
1112
|
+
last_seen_at=TIMESTAMP_1,
|
|
1113
|
+
)
|
|
1114
|
+
self.old_session = EditingSession.objects.create(
|
|
1115
|
+
user=self.user,
|
|
1116
|
+
content_type=page_content_type,
|
|
1117
|
+
object_id=self.page.id,
|
|
1118
|
+
last_seen_at=TIMESTAMP_PAST,
|
|
1119
|
+
)
|
|
1120
|
+
|
|
1121
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
1122
|
+
def test_cleanup(self):
|
|
1123
|
+
EditingSession.cleanup()
|
|
1124
|
+
self.assertTrue(EditingSession.objects.filter(id=self.session.id).exists())
|
|
1125
|
+
self.assertFalse(EditingSession.objects.filter(id=self.old_session.id).exists())
|
|
1126
|
+
|
|
1127
|
+
|
|
1128
|
+
class TestReleaseView(WagtailTestUtils, TestCase):
|
|
1129
|
+
def setUp(self):
|
|
1130
|
+
self.user = self.create_superuser(
|
|
1131
|
+
"bob", password="password", first_name="Bob", last_name="Testuser"
|
|
1132
|
+
)
|
|
1133
|
+
self.login(user=self.user)
|
|
1134
|
+
self.root_page = Page.get_first_root_node()
|
|
1135
|
+
|
|
1136
|
+
self.page = SimplePage(title="Test page", slug="test-page", content="test page")
|
|
1137
|
+
self.root_page.add_child(instance=self.page)
|
|
1138
|
+
|
|
1139
|
+
self.other_user = self.create_user(
|
|
1140
|
+
"vic", password="password", first_name="Vic", last_name="Otheruser"
|
|
1141
|
+
)
|
|
1142
|
+
|
|
1143
|
+
page_content_type = ContentType.objects.get_for_model(Page)
|
|
1144
|
+
|
|
1145
|
+
self.session = EditingSession.objects.create(
|
|
1146
|
+
user=self.user,
|
|
1147
|
+
content_type=page_content_type,
|
|
1148
|
+
object_id=self.page.id,
|
|
1149
|
+
last_seen_at=TIMESTAMP_1,
|
|
1150
|
+
)
|
|
1151
|
+
self.other_session = EditingSession.objects.create(
|
|
1152
|
+
user=self.other_user,
|
|
1153
|
+
content_type=page_content_type,
|
|
1154
|
+
object_id=self.page.id,
|
|
1155
|
+
last_seen_at=TIMESTAMP_1,
|
|
1156
|
+
)
|
|
1157
|
+
|
|
1158
|
+
def test_release(self):
|
|
1159
|
+
response = self.client.post(
|
|
1160
|
+
reverse("wagtailadmin_editing_sessions:release", args=(self.session.id,))
|
|
1161
|
+
)
|
|
1162
|
+
self.assertEqual(response.status_code, 200)
|
|
1163
|
+
self.assertFalse(EditingSession.objects.filter(id=self.session.id).exists())
|
|
1164
|
+
self.assertTrue(
|
|
1165
|
+
EditingSession.objects.filter(id=self.other_session.id).exists()
|
|
1166
|
+
)
|
|
1167
|
+
|
|
1168
|
+
def test_must_post(self):
|
|
1169
|
+
response = self.client.get(
|
|
1170
|
+
reverse("wagtailadmin_editing_sessions:release", args=(self.session.id,))
|
|
1171
|
+
)
|
|
1172
|
+
self.assertEqual(response.status_code, 405)
|
|
1173
|
+
self.assertTrue(EditingSession.objects.filter(id=self.session.id).exists())
|
|
1174
|
+
self.assertTrue(
|
|
1175
|
+
EditingSession.objects.filter(id=self.other_session.id).exists()
|
|
1176
|
+
)
|
|
1177
|
+
|
|
1178
|
+
def test_cannot_release_other_users_session(self):
|
|
1179
|
+
response = self.client.post(
|
|
1180
|
+
reverse(
|
|
1181
|
+
"wagtailadmin_editing_sessions:release", args=(self.other_session.id,)
|
|
1182
|
+
)
|
|
1183
|
+
)
|
|
1184
|
+
self.assertEqual(response.status_code, 200)
|
|
1185
|
+
self.assertTrue(EditingSession.objects.filter(id=self.session.id).exists())
|
|
1186
|
+
self.assertTrue(
|
|
1187
|
+
EditingSession.objects.filter(id=self.other_session.id).exists()
|
|
1188
|
+
)
|
|
1189
|
+
|
|
1190
|
+
|
|
1191
|
+
class TestModuleInEditView(WagtailTestUtils, TestCase):
|
|
1192
|
+
url_name = "wagtailadmin_pages:edit"
|
|
1193
|
+
model = Page
|
|
1194
|
+
|
|
1195
|
+
def setUp(self):
|
|
1196
|
+
self.user = self.create_superuser(
|
|
1197
|
+
"bob", password="password", first_name="Bob", last_name="Testuser"
|
|
1198
|
+
)
|
|
1199
|
+
self.login(user=self.user)
|
|
1200
|
+
self.content_type = ContentType.objects.get_for_model(self.model)
|
|
1201
|
+
|
|
1202
|
+
self.object = self.create_object()
|
|
1203
|
+
|
|
1204
|
+
self.session = EditingSession.objects.create(
|
|
1205
|
+
user=self.user,
|
|
1206
|
+
content_type=self.content_type,
|
|
1207
|
+
object_id=self.object.pk,
|
|
1208
|
+
last_seen_at=TIMESTAMP_1,
|
|
1209
|
+
)
|
|
1210
|
+
self.old_session = EditingSession.objects.create(
|
|
1211
|
+
user=self.user,
|
|
1212
|
+
content_type=self.content_type,
|
|
1213
|
+
object_id=self.object.pk,
|
|
1214
|
+
last_seen_at=TIMESTAMP_PAST,
|
|
1215
|
+
)
|
|
1216
|
+
|
|
1217
|
+
def create_object(self):
|
|
1218
|
+
root_page = Page.get_first_root_node()
|
|
1219
|
+
page = SimplePage(title="Foo", slug="foo", content="bar")
|
|
1220
|
+
root_page.add_child(instance=page)
|
|
1221
|
+
page.save_revision()
|
|
1222
|
+
return page
|
|
1223
|
+
|
|
1224
|
+
def get(self):
|
|
1225
|
+
return self.client.get(reverse(self.url_name, args=(quote(self.object.pk),)))
|
|
1226
|
+
|
|
1227
|
+
def assertRevisionInput(self, soup):
|
|
1228
|
+
revision_input = soup.select_one('input[name="revision_id"]')
|
|
1229
|
+
self.assertIsNotNone(revision_input)
|
|
1230
|
+
self.assertEqual(revision_input.get("type"), "hidden")
|
|
1231
|
+
self.assertEqual(
|
|
1232
|
+
revision_input.get("value"),
|
|
1233
|
+
str(self.object.latest_revision.id),
|
|
1234
|
+
)
|
|
1235
|
+
|
|
1236
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
1237
|
+
def test_edit_view_with_default_interval(self):
|
|
1238
|
+
self.assertEqual(EditingSession.objects.all().count(), 2)
|
|
1239
|
+
response = self.get()
|
|
1240
|
+
self.assertEqual(response.status_code, 200)
|
|
1241
|
+
|
|
1242
|
+
# Should perform a cleanup of the EditingSessions
|
|
1243
|
+
self.assertTrue(EditingSession.objects.filter(id=self.session.id).exists())
|
|
1244
|
+
self.assertFalse(EditingSession.objects.filter(id=self.old_session.id).exists())
|
|
1245
|
+
|
|
1246
|
+
# Should create a new EditingSession for the current user
|
|
1247
|
+
self.assertEqual(EditingSession.objects.all().count(), 2)
|
|
1248
|
+
new_session = EditingSession.objects.exclude(id=self.session.id).get(
|
|
1249
|
+
content_type=self.content_type,
|
|
1250
|
+
object_id=self.object.pk,
|
|
1251
|
+
)
|
|
1252
|
+
self.assertEqual(new_session.user, self.user)
|
|
1253
|
+
|
|
1254
|
+
# Should load the EditingSessionsModule with the default interval (10s)
|
|
1255
|
+
soup = self.get_soup(response.content)
|
|
1256
|
+
module = soup.select_one('form[data-controller~="w-session"]')
|
|
1257
|
+
self.assertIsNotNone(module)
|
|
1258
|
+
self.assertEqual(module.get("data-w-session-interval-value"), "10000")
|
|
1259
|
+
|
|
1260
|
+
# Should show the revision_id input
|
|
1261
|
+
self.assertRevisionInput(module)
|
|
1262
|
+
|
|
1263
|
+
@freeze_time(TIMESTAMP_NOW)
|
|
1264
|
+
@override_settings(WAGTAIL_EDITING_SESSION_PING_INTERVAL=30000)
|
|
1265
|
+
def test_edit_view_with_custom_interval(self):
|
|
1266
|
+
self.assertEqual(EditingSession.objects.all().count(), 2)
|
|
1267
|
+
response = self.get()
|
|
1268
|
+
self.assertEqual(response.status_code, 200)
|
|
1269
|
+
|
|
1270
|
+
# Should perform a cleanup of the EditingSessions
|
|
1271
|
+
self.assertTrue(EditingSession.objects.filter(id=self.session.id).exists())
|
|
1272
|
+
self.assertFalse(EditingSession.objects.filter(id=self.old_session.id).exists())
|
|
1273
|
+
|
|
1274
|
+
# Should create a new EditingSession for the current user
|
|
1275
|
+
self.assertEqual(EditingSession.objects.all().count(), 2)
|
|
1276
|
+
new_session = EditingSession.objects.exclude(id=self.session.id).get(
|
|
1277
|
+
content_type=self.content_type,
|
|
1278
|
+
object_id=self.object.pk,
|
|
1279
|
+
)
|
|
1280
|
+
self.assertEqual(new_session.user, self.user)
|
|
1281
|
+
|
|
1282
|
+
# Should load the EditingSessionsModule
|
|
1283
|
+
soup = self.get_soup(response.content)
|
|
1284
|
+
module = soup.select_one('form[data-controller~="w-session"]')
|
|
1285
|
+
self.assertIsNotNone(module)
|
|
1286
|
+
self.assertEqual(
|
|
1287
|
+
module.get("data-w-swap-src-value"),
|
|
1288
|
+
reverse(
|
|
1289
|
+
"wagtailadmin_editing_sessions:ping",
|
|
1290
|
+
args=(
|
|
1291
|
+
self.content_type.app_label,
|
|
1292
|
+
self.content_type.model,
|
|
1293
|
+
quote(self.object.pk),
|
|
1294
|
+
new_session.id,
|
|
1295
|
+
),
|
|
1296
|
+
),
|
|
1297
|
+
)
|
|
1298
|
+
self.assertEqual(
|
|
1299
|
+
module.get("data-w-action-url-value"),
|
|
1300
|
+
reverse(
|
|
1301
|
+
"wagtailadmin_editing_sessions:release",
|
|
1302
|
+
args=(new_session.id,),
|
|
1303
|
+
),
|
|
1304
|
+
)
|
|
1305
|
+
|
|
1306
|
+
# Should use the custom interval (30s)
|
|
1307
|
+
self.assertEqual(module.get("data-w-session-interval-value"), "30000")
|
|
1308
|
+
self.assertRevisionInput(module)
|
|
1309
|
+
|
|
1310
|
+
|
|
1311
|
+
class TestModuleInEditViewWithRevisableSnippet(TestModuleInEditView):
|
|
1312
|
+
model = FullFeaturedSnippet
|
|
1313
|
+
|
|
1314
|
+
@property
|
|
1315
|
+
def url_name(self):
|
|
1316
|
+
return self.model.snippet_viewset.get_url_name("edit")
|
|
1317
|
+
|
|
1318
|
+
def create_object(self):
|
|
1319
|
+
obj = self.model.objects.create(text="Shodan")
|
|
1320
|
+
obj.save_revision()
|
|
1321
|
+
return obj
|
|
1322
|
+
|
|
1323
|
+
|
|
1324
|
+
class TestModuleInEditViewWithNonRevisableSnippet(TestModuleInEditView):
|
|
1325
|
+
model = AdvertWithCustomPrimaryKey
|
|
1326
|
+
|
|
1327
|
+
@property
|
|
1328
|
+
def url_name(self):
|
|
1329
|
+
return self.model.snippet_viewset.get_url_name("edit")
|
|
1330
|
+
|
|
1331
|
+
def create_object(self):
|
|
1332
|
+
return self.model.objects.create(text="GLaDOS", advert_id="m0n5t3r!/#")
|
|
1333
|
+
|
|
1334
|
+
def assertRevisionInput(self, soup):
|
|
1335
|
+
revision_input = soup.select_one('input[name="revision_id"]')
|
|
1336
|
+
self.assertIsNone(revision_input)
|