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.
Files changed (257) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_for_translation.py +15 -1
  3. wagtail/admin/checks.py +20 -30
  4. wagtail/admin/forms/pages.py +10 -0
  5. wagtail/admin/icons.py +43 -0
  6. wagtail/admin/locale/en/LC_MESSAGES/django.po +405 -295
  7. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +21 -3
  8. wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
  9. wagtail/admin/locale/sl/LC_MESSAGES/django.po +30 -0
  10. wagtail/admin/menu.py +2 -2
  11. wagtail/admin/migrations/0004_editingsession.py +57 -0
  12. wagtail/admin/migrations/0005_editingsession_is_editing.py +18 -0
  13. wagtail/admin/models.py +36 -3
  14. wagtail/admin/rich_text/editors/draftail/__init__.py +2 -20
  15. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  16. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  17. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  18. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  19. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  20. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  21. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  22. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  23. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  24. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +1 -1
  25. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  26. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  27. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  28. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  29. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  30. wagtail/admin/static/wagtailadmin/js/preview-panel.js +2 -1
  31. wagtail/admin/static/wagtailadmin/js/preview-panel.js.LICENSE.txt +11 -0
  32. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  33. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  34. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  37. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  38. wagtail/admin/static/wagtailadmin/js/userbar.js +2 -1
  39. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +11 -0
  40. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  41. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +0 -12
  42. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  43. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  44. wagtail/admin/templates/wagtailadmin/collection_privacy/ancestor_privacy.html +2 -6
  45. wagtail/admin/templates/wagtailadmin/generic/index_results.html +1 -17
  46. wagtail/admin/templates/wagtailadmin/generic/listing_results.html +20 -1
  47. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +2 -11
  48. wagtail/admin/templates/wagtailadmin/page_privacy/ancestor_privacy.html +2 -6
  49. wagtail/admin/templates/wagtailadmin/page_privacy/no_privacy.html +2 -0
  50. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -1
  51. wagtail/admin/templates/wagtailadmin/pages/action_menu/menu.html +1 -1
  52. wagtail/admin/templates/wagtailadmin/reports/aging_pages_results.html +54 -0
  53. wagtail/admin/templates/wagtailadmin/reports/base_page_report.html +1 -17
  54. wagtail/admin/templates/wagtailadmin/reports/base_page_report_results.html +10 -0
  55. wagtail/admin/templates/wagtailadmin/reports/base_report.html +1 -40
  56. wagtail/admin/templates/wagtailadmin/reports/base_report_results.html +1 -0
  57. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_report.html +21 -27
  58. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_types_usage.html +48 -54
  59. wagtail/admin/templates/wagtailadmin/reports/{locked_pages.html → locked_pages_results.html} +3 -3
  60. wagtail/admin/templates/wagtailadmin/reports/page_types_usage_results.html +10 -0
  61. wagtail/admin/templates/wagtailadmin/reports/site_history_results.html +53 -0
  62. wagtail/admin/templates/wagtailadmin/reports/workflow_results.html +74 -0
  63. wagtail/admin/templates/wagtailadmin/reports/workflow_tasks_results.html +56 -0
  64. wagtail/admin/templates/wagtailadmin/shared/_workflow_init.html +8 -44
  65. wagtail/admin/templates/wagtailadmin/shared/avatar.html +11 -1
  66. wagtail/admin/templates/wagtailadmin/shared/dialog/dialog.html +5 -4
  67. wagtail/admin/templates/wagtailadmin/shared/dropdown/dropdown_button.html +2 -1
  68. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/list.html +132 -0
  69. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/module.html +44 -0
  70. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +7 -1
  71. wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +1 -1
  72. wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html +32 -16
  73. wagtail/admin/templates/wagtailadmin/skeleton.html +1 -1
  74. wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +9 -11
  75. wagtail/admin/templatetags/wagtailadmin_tags.py +13 -2
  76. wagtail/admin/tests/formats/en/__init__.py +0 -0
  77. wagtail/admin/tests/formats/en/formats.py +1 -0
  78. wagtail/admin/tests/pages/test_create_page.py +47 -0
  79. wagtail/admin/tests/pages/test_edit_page.py +10 -8
  80. wagtail/admin/tests/pages/test_parent_page_chooser_view.py +45 -1
  81. wagtail/admin/tests/test_checks.py +53 -3
  82. wagtail/admin/tests/test_collections_views.py +62 -1
  83. wagtail/admin/tests/test_edit_handlers.py +37 -0
  84. wagtail/admin/tests/test_editing_sessions.py +1336 -0
  85. wagtail/admin/tests/test_icon_sprite.py +12 -21
  86. wagtail/admin/tests/test_page_chooser.py +309 -7
  87. wagtail/admin/tests/test_privacy.py +82 -0
  88. wagtail/admin/tests/test_reports_views.py +464 -70
  89. wagtail/admin/tests/test_userbar.py +93 -6
  90. wagtail/admin/tests/test_workflows.py +223 -33
  91. wagtail/admin/tests/viewsets/test_model_viewset.py +151 -2
  92. wagtail/admin/ui/editing_sessions.py +57 -0
  93. wagtail/admin/urls/__init__.py +9 -15
  94. wagtail/admin/urls/editing_sessions.py +17 -0
  95. wagtail/admin/urls/reports.py +33 -1
  96. wagtail/admin/userbar.py +77 -20
  97. wagtail/admin/views/chooser.py +49 -22
  98. wagtail/admin/views/collections.py +0 -11
  99. wagtail/admin/views/editing_sessions.py +193 -0
  100. wagtail/admin/views/generic/__init__.py +1 -0
  101. wagtail/admin/views/generic/history.py +9 -3
  102. wagtail/admin/views/generic/mixins.py +44 -3
  103. wagtail/admin/views/generic/models.py +46 -72
  104. wagtail/admin/views/generic/permissions.py +20 -10
  105. wagtail/admin/views/home.py +2 -31
  106. wagtail/admin/views/page_privacy.py +20 -5
  107. wagtail/admin/views/pages/choose_parent.py +62 -0
  108. wagtail/admin/views/pages/edit.py +28 -0
  109. wagtail/admin/views/reports/aging_pages.py +6 -10
  110. wagtail/admin/views/reports/audit_logging.py +13 -42
  111. wagtail/admin/views/reports/base.py +31 -4
  112. wagtail/admin/views/reports/locked_pages.py +5 -8
  113. wagtail/admin/views/reports/page_types_usage.py +6 -10
  114. wagtail/admin/views/reports/workflows.py +36 -12
  115. wagtail/admin/viewsets/base.py +8 -3
  116. wagtail/admin/viewsets/chooser.py +1 -1
  117. wagtail/admin/viewsets/model.py +26 -1
  118. wagtail/admin/wagtail_hooks.py +2 -1
  119. wagtail/api/v2/filters.py +6 -0
  120. wagtail/api/v2/tests/test_documents.py +1 -1
  121. wagtail/api/v2/tests/test_images.py +1 -1
  122. wagtail/api/v2/tests/test_pages.py +11 -1
  123. wagtail/api/v2/utils.py +2 -2
  124. wagtail/blocks/base.py +35 -12
  125. wagtail/blocks/definition_lookup.py +85 -0
  126. wagtail/blocks/list_block.py +12 -0
  127. wagtail/blocks/migrations/migrate_operation.py +2 -0
  128. wagtail/blocks/stream_block.py +19 -0
  129. wagtail/blocks/struct_block.py +19 -0
  130. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +1 -1
  131. wagtail/contrib/frontend_cache/backends/__init__.py +5 -0
  132. wagtail/contrib/frontend_cache/backends/azure.py +179 -0
  133. wagtail/contrib/frontend_cache/backends/base.py +28 -0
  134. wagtail/contrib/frontend_cache/backends/cloudflare.py +114 -0
  135. wagtail/contrib/frontend_cache/backends/cloudfront.py +99 -0
  136. wagtail/contrib/frontend_cache/backends/http.py +64 -0
  137. wagtail/contrib/frontend_cache/tests.py +59 -17
  138. wagtail/contrib/frontend_cache/utils.py +26 -8
  139. wagtail/contrib/redirects/filters.py +15 -1
  140. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +37 -72
  141. wagtail/contrib/redirects/models.py +6 -5
  142. wagtail/contrib/redirects/templates/wagtailredirects/edit.html +1 -38
  143. wagtail/contrib/redirects/tests/test_redirects.py +141 -1
  144. wagtail/contrib/redirects/urls.py +1 -2
  145. wagtail/contrib/redirects/views.py +39 -80
  146. wagtail/contrib/routable_page/models.py +6 -4
  147. wagtail/contrib/routable_page/tests.py +11 -0
  148. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +1 -1
  149. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +4 -4
  150. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +5 -1
  151. wagtail/contrib/simple_translation/models.py +2 -1
  152. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  153. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  154. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  155. wagtail/contrib/typed_table_block/blocks.py +19 -0
  156. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  157. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  158. wagtail/contrib/typed_table_block/tests.py +38 -0
  159. wagtail/coreutils.py +1 -1
  160. wagtail/documents/__init__.py +1 -1
  161. wagtail/documents/locale/en/LC_MESSAGES/django.po +8 -8
  162. wagtail/documents/locale/sl/LC_MESSAGES/django.mo +0 -0
  163. wagtail/documents/locale/sl/LC_MESSAGES/django.po +20 -0
  164. wagtail/documents/models.py +5 -1
  165. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  166. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  167. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  168. wagtail/documents/tests/test_models.py +5 -1
  169. wagtail/embeds/apps.py +2 -0
  170. wagtail/embeds/embeds.py +12 -14
  171. wagtail/embeds/finders/__init__.py +2 -0
  172. wagtail/embeds/finders/facebook.py +17 -33
  173. wagtail/embeds/finders/instagram.py +19 -16
  174. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  175. wagtail/embeds/signal_handlers.py +13 -0
  176. wagtail/embeds/tests/test_embeds.py +7 -7
  177. wagtail/fields.py +58 -14
  178. wagtail/images/__init__.py +1 -1
  179. wagtail/images/locale/en/LC_MESSAGES/django.po +34 -34
  180. wagtail/images/locale/sl/LC_MESSAGES/django.mo +0 -0
  181. wagtail/images/locale/sl/LC_MESSAGES/django.po +20 -0
  182. wagtail/images/models.py +2 -0
  183. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  184. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  185. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  186. wagtail/images/templates/wagtailimages/images/edit.html +4 -4
  187. wagtail/images/tests/test_admin_views.py +26 -2
  188. wagtail/images/views/chooser.py +6 -1
  189. wagtail/locale/en/LC_MESSAGES/django.po +84 -80
  190. wagtail/locales/locale/en/LC_MESSAGES/django.po +2 -2
  191. wagtail/locales/tests.py +16 -0
  192. wagtail/locales/wagtail_hooks.py +0 -9
  193. wagtail/migrations/0094_alter_page_locale.py +19 -0
  194. wagtail/models/__init__.py +11 -5
  195. wagtail/models/i18n.py +6 -1
  196. wagtail/project_template/requirements.txt +1 -1
  197. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  198. wagtail/signals.py +4 -0
  199. wagtail/sites/locale/en/LC_MESSAGES/django.po +2 -2
  200. wagtail/sites/tests.py +15 -0
  201. wagtail/sites/wagtail_hooks.py +0 -9
  202. wagtail/snippets/locale/en/LC_MESSAGES/django.po +9 -9
  203. wagtail/snippets/permissions.py +5 -3
  204. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  205. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  206. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu.html +1 -1
  207. wagtail/snippets/tests/test_snippets.py +78 -12
  208. wagtail/snippets/tests/test_viewset.py +22 -0
  209. wagtail/snippets/views/snippets.py +19 -14
  210. wagtail/snippets/wagtail_hooks.py +2 -10
  211. wagtail/templatetags/wagtailcore_tags.py +3 -0
  212. wagtail/test/dummy_external_storage.py +1 -1
  213. wagtail/test/i18n/migrations/0003_alter_clusterabletestmodel_locale_and_more.py +40 -0
  214. wagtail/test/routablepage/models.py +4 -0
  215. wagtail/test/snippets/migrations/0012_alter_translatablesnippet_locale.py +20 -0
  216. wagtail/test/testapp/migrations/0038_sociallink.py +52 -0
  217. wagtail/test/testapp/migrations/0039_alter_eventcategory_locale_and_more.py +45 -0
  218. wagtail/test/testapp/models.py +24 -0
  219. wagtail/test/testapp/views.py +1 -0
  220. wagtail/test/testapp/wagtail_hooks.py +9 -0
  221. wagtail/test/urls_multilang.py +6 -1
  222. wagtail/test/urls_multilang_non_root.py +11 -0
  223. wagtail/tests/streamfield_migrations/test_migrations.py +53 -12
  224. wagtail/tests/test_audit_log.py +72 -2
  225. wagtail/tests/test_blocks.py +103 -0
  226. wagtail/tests/test_signals.py +49 -2
  227. wagtail/tests/test_streamfield.py +153 -0
  228. wagtail/tests/test_utils.py +14 -0
  229. wagtail/tests/tests.py +5 -0
  230. wagtail/users/apps.py +1 -0
  231. wagtail/users/forms.py +7 -0
  232. wagtail/users/locale/en/LC_MESSAGES/django.po +55 -50
  233. wagtail/users/models.py +1 -0
  234. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +3 -2
  235. wagtail/users/templatetags/wagtailusers_tags.py +9 -0
  236. wagtail/users/tests/__init__.py +7 -1
  237. wagtail/users/tests/test_admin_views.py +117 -32
  238. wagtail/users/views/groups.py +4 -0
  239. wagtail/users/views/users.py +58 -14
  240. wagtail/users/wagtail_hooks.py +7 -123
  241. wagtail/utils/utils.py +1 -0
  242. wagtail/utils/version.py +5 -2
  243. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/METADATA +3 -3
  244. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/RECORD +248 -222
  245. wagtail/admin/templates/wagtailadmin/reports/aging_pages.html +0 -58
  246. wagtail/admin/templates/wagtailadmin/reports/page_types_usage.html +0 -18
  247. wagtail/admin/templates/wagtailadmin/reports/site_history.html +0 -57
  248. wagtail/admin/templates/wagtailadmin/reports/workflow.html +0 -81
  249. wagtail/admin/templates/wagtailadmin/reports/workflow_tasks.html +0 -63
  250. wagtail/contrib/frontend_cache/backends.py +0 -400
  251. wagtail/contrib/redirects/templates/wagtailredirects/list.html +0 -43
  252. wagtail/contrib/redirects/templates/wagtailredirects/reports/redirects_report.html +0 -14
  253. wagtail/contrib/redirects/tests/test_reports_view.py +0 -82
  254. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/LICENSE +0 -0
  255. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/WHEEL +0 -0
  256. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/entry_points.txt +0 -0
  257. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/top_level.txt +0 -0
@@ -1563,6 +1563,10 @@ class TestInlinePanelWithTags(WagtailTestUtils, TestCase):
1563
1563
  "comments-INITIAL_FORMS": 0,
1564
1564
  "comments-MIN_NUM_FORMS": 0,
1565
1565
  "comments-MAX_NUM_FORMS": 1000,
1566
+ "social_links-TOTAL_FORMS": 0,
1567
+ "social_links-INITIAL_FORMS": 0,
1568
+ "social_links-MIN_NUM_FORMS": 0,
1569
+ "social_links-MAX_NUM_FORMS": 1000,
1566
1570
  }
1567
1571
  response = self.client.post(
1568
1572
  reverse(
@@ -1578,6 +1582,49 @@ class TestInlinePanelWithTags(WagtailTestUtils, TestCase):
1578
1582
  self.assertEqual(new_page.addresses.first().tags.count(), 2)
1579
1583
 
1580
1584
 
1585
+ class TestNonOrderableInlinePanel(WagtailTestUtils, TestCase):
1586
+ # https://github.com/wagtail/wagtail/issues/11887
1587
+
1588
+ def setUp(self):
1589
+ self.root_page = Page.objects.get(id=2)
1590
+ self.user = self.login()
1591
+
1592
+ def test_create(self):
1593
+ post_data = {
1594
+ "title": "Mr Benn",
1595
+ "slug": "mr-benn",
1596
+ "first_name": "William",
1597
+ "last_name": "Benn",
1598
+ "addresses-TOTAL_FORMS": 0,
1599
+ "addresses-INITIAL_FORMS": 0,
1600
+ "addresses-MIN_NUM_FORMS": 0,
1601
+ "addresses-MAX_NUM_FORMS": 1000,
1602
+ "action-publish": "Publish",
1603
+ "comments-TOTAL_FORMS": 0,
1604
+ "comments-INITIAL_FORMS": 0,
1605
+ "comments-MIN_NUM_FORMS": 0,
1606
+ "comments-MAX_NUM_FORMS": 1000,
1607
+ "social_links-TOTAL_FORMS": 1,
1608
+ "social_links-INITIAL_FORMS": 0,
1609
+ "social_links-MIN_NUM_FORMS": 0,
1610
+ "social_links-MAX_NUM_FORMS": 1000,
1611
+ "social_links-0-url": "https://twitter.com/mrbenn",
1612
+ "social_links-0-kind": "twitter",
1613
+ }
1614
+ response = self.client.post(
1615
+ reverse(
1616
+ "wagtailadmin_pages:add",
1617
+ args=("tests", "personpage", self.root_page.id),
1618
+ ),
1619
+ post_data,
1620
+ )
1621
+ self.assertRedirects(
1622
+ response, reverse("wagtailadmin_explore", args=(self.root_page.id,))
1623
+ )
1624
+ new_page = PersonPage.objects.get(slug="mr-benn")
1625
+ self.assertEqual(new_page.social_links.count(), 1)
1626
+
1627
+
1581
1628
  class TestInlinePanelNonFieldErrors(WagtailTestUtils, TestCase):
1582
1629
  """
1583
1630
  Test that non field errors will render for InlinePanels
@@ -127,12 +127,14 @@ class TestPageEdit(WagtailTestUtils, TestCase):
127
127
  allow_extra_attrs=True,
128
128
  )
129
129
  # Should show the dialog template pointing to the [data-edit-form] selector as the root
130
- self.assertTagInHTML(
131
- '<template data-controller="w-teleport" data-w-teleport-target-value="[data-edit-form]">',
132
- html,
133
- count=1,
134
- allow_extra_attrs=True,
135
- )
130
+ soup = self.get_soup(html)
131
+ dialog = soup.select_one(
132
+ """
133
+ template[data-controller="w-teleport"][data-w-teleport-target-value="[data-edit-form]"]
134
+ #schedule-publishing-dialog
135
+ """
136
+ )
137
+ self.assertIsNotNone(dialog)
136
138
  # Should render the main form with data-edit-form attribute
137
139
  self.assertTagInHTML(
138
140
  f'<form action="{edit_url}" method="POST" data-edit-form>',
@@ -2067,7 +2069,7 @@ class TestPageEdit(WagtailTestUtils, TestCase):
2067
2069
  # as when running it within the full test suite
2068
2070
  self.client.get(reverse("wagtailadmin_pages:edit", args=(self.event_page.id,)))
2069
2071
 
2070
- with self.assertNumQueries(33):
2072
+ with self.assertNumQueries(35):
2071
2073
  self.client.get(
2072
2074
  reverse("wagtailadmin_pages:edit", args=(self.event_page.id,))
2073
2075
  )
@@ -2080,7 +2082,7 @@ class TestPageEdit(WagtailTestUtils, TestCase):
2080
2082
  # Warm up the cache as above.
2081
2083
  self.client.get(reverse("wagtailadmin_pages:edit", args=(self.event_page.id,)))
2082
2084
 
2083
- with self.assertNumQueries(37):
2085
+ with self.assertNumQueries(39):
2084
2086
  self.client.get(
2085
2087
  reverse("wagtailadmin_pages:edit", args=(self.event_page.id,))
2086
2088
  )
@@ -1,9 +1,11 @@
1
+ from unittest import mock
2
+
1
3
  from django.contrib.auth.models import Group, Permission
2
4
  from django.test import TestCase
3
5
  from django.urls import reverse
4
6
 
5
7
  from wagtail.models import GroupPagePermission, Page
6
- from wagtail.test.testapp.models import BusinessIndex
8
+ from wagtail.test.testapp.models import BusinessIndex, EventIndex, EventPage, SimplePage
7
9
  from wagtail.test.utils import WagtailTestUtils
8
10
  from wagtail.test.utils.template_tests import AdminTemplateTestUtils
9
11
 
@@ -117,3 +119,45 @@ class TestParentPageChooserView(AdminTemplateTestUtils, WagtailTestUtils, TestCa
117
119
  "You cannot create a page of type &quot;Event page&quot; under &quot;%s&quot;."
118
120
  % page_with_limited_subtypes.get_admin_display_title(),
119
121
  )
122
+
123
+ def test_skip_if_only_one_valid_parent(self):
124
+ self.assertEqual(EventIndex.objects.count(), 1)
125
+
126
+ with mock.patch.object(EventPage, "allowed_parent_page_models") as mock_method:
127
+ mock_method.return_value = [EventIndex]
128
+ self.assertEqual(EventPage.allowed_parent_page_models(), [EventIndex])
129
+ response = self.client.get(self.view_url)
130
+
131
+ self.assertRedirects(
132
+ response,
133
+ reverse(
134
+ "wagtailadmin_pages:add",
135
+ args=("tests", "eventpage", EventIndex.objects.first().pk),
136
+ ),
137
+ )
138
+
139
+ def test_skip_if_user_only_has_permission_for_one_parent(self):
140
+ test_group = Group.objects.create(name="test_group")
141
+ test_group.permissions.add(Permission.objects.get(codename="access_admin"))
142
+
143
+ self.user.is_superuser = False
144
+ self.user.groups.add(test_group)
145
+ self.user.save()
146
+
147
+ # Only grant the user permission to add pages under one leaf page
148
+ parent_page = SimplePage.objects.filter(numchild=0).first()
149
+ GroupPagePermission.objects.create(
150
+ group=test_group,
151
+ page=parent_page,
152
+ permission_type="add",
153
+ )
154
+
155
+ # Should redirect to the add view with the only valid parent page
156
+ response = self.client.get(self.view_url)
157
+ self.assertRedirects(
158
+ response,
159
+ reverse(
160
+ "wagtailadmin_pages:add",
161
+ args=("tests", "eventpage", parent_page.pk),
162
+ ),
163
+ )
@@ -1,5 +1,6 @@
1
1
  from django.core.checks import Error
2
2
  from django.test import TestCase, override_settings
3
+ from django.utils.formats import reset_format_cache
3
4
 
4
5
  from wagtail.admin.checks import datetime_format_check
5
6
  from wagtail.test.utils import WagtailTestUtils
@@ -8,6 +9,9 @@ from wagtail.test.utils import WagtailTestUtils
8
9
  class TestDateTimeChecks(WagtailTestUtils, TestCase):
9
10
  fixtures = ["test.json"]
10
11
 
12
+ def setUp(self):
13
+ reset_format_cache()
14
+
11
15
  def test_datetime_format(self):
12
16
  with override_settings(
13
17
  WAGTAIL_CONTENT_LANGUAGES=[
@@ -41,7 +45,9 @@ class TestDateTimeChecks(WagtailTestUtils, TestCase):
41
45
  expected_errors = [
42
46
  Error(
43
47
  "Configuration error",
44
- hint="WAGTAIL_DATE_FORMAT %d.%m.%Y. must be in DATE_INPUT_FORMATS for language English (en).",
48
+ hint="'%d.%m.%Y.' must be in DATE_INPUT_FORMATS for language English (en).",
49
+ obj="WAGTAIL_DATE_FORMAT",
50
+ id="wagtailadmin.E003",
45
51
  )
46
52
  ]
47
53
  self.assertEqual(errors, expected_errors)
@@ -82,11 +88,55 @@ class TestDateTimeChecks(WagtailTestUtils, TestCase):
82
88
  expected_errors = [
83
89
  Error(
84
90
  "Configuration error",
85
- hint="WAGTAIL_DATETIME_FORMAT %d.%m.%Y. %H:%M must be in DATETIME_INPUT_FORMATS for language English (en).",
91
+ hint="'%d.%m.%Y. %H:%M' must be in DATETIME_INPUT_FORMATS for language English (en).",
92
+ obj="WAGTAIL_DATETIME_FORMAT",
93
+ id="wagtailadmin.E003",
86
94
  ),
87
95
  Error(
88
96
  "Configuration error",
89
- hint="WAGTAIL_TIME_FORMAT %I:%M %p must be in TIME_INPUT_FORMATS for language English (en).",
97
+ hint="'%I:%M %p' must be in TIME_INPUT_FORMATS for language English (en).",
98
+ obj="WAGTAIL_TIME_FORMAT",
99
+ id="wagtailadmin.E003",
100
+ ),
101
+ ]
102
+ self.assertEqual(errors, expected_errors)
103
+
104
+ def test_datetime_format_with_overriden_format(self):
105
+ with override_settings(
106
+ WAGTAIL_CONTENT_LANGUAGES=[
107
+ ("en", "English"),
108
+ ],
109
+ LANGUAGES=[
110
+ ("en", "English"),
111
+ ],
112
+ WAGTAIL_DATETIME_FORMAT="%d.%m.%Y. %H:%M",
113
+ FORMAT_MODULE_PATH=["wagtail.admin.tests.formats"],
114
+ USE_L10N=True,
115
+ ):
116
+ errors = datetime_format_check(None)
117
+
118
+ self.assertEqual(errors, [])
119
+
120
+ def test_datetime_format_with_incorrect_overriden_format(self):
121
+ with override_settings(
122
+ WAGTAIL_CONTENT_LANGUAGES=[
123
+ ("en", "English"),
124
+ ],
125
+ LANGUAGES=[
126
+ ("en", "English"),
127
+ ],
128
+ WAGTAIL_DATETIME_FORMAT="%m.%d.%Y. %H:%M",
129
+ FORMAT_MODULE_PATH=["wagtail.admin.tests.formats"],
130
+ USE_L10N=True,
131
+ ):
132
+ errors = datetime_format_check(None)
133
+
134
+ expected_errors = [
135
+ Error(
136
+ "Configuration error",
137
+ hint="'%m.%d.%Y. %H:%M' must be in DATETIME_INPUT_FORMATS for language English (en).",
138
+ obj="WAGTAIL_DATETIME_FORMAT",
139
+ id="wagtailadmin.E003",
90
140
  ),
91
141
  ]
92
142
  self.assertEqual(errors, expected_errors)
@@ -1,3 +1,5 @@
1
+ import json
2
+
1
3
  from django.contrib.auth.models import Group, Permission
2
4
  from django.contrib.contenttypes.models import ContentType
3
5
  from django.test import TestCase
@@ -5,7 +7,11 @@ from django.urls import reverse
5
7
 
6
8
  from wagtail.admin.admin_url_finder import AdminURLFinder
7
9
  from wagtail.documents.models import Document
8
- from wagtail.models import Collection, GroupCollectionPermission
10
+ from wagtail.models import (
11
+ Collection,
12
+ CollectionViewRestriction,
13
+ GroupCollectionPermission,
14
+ )
9
15
  from wagtail.test.utils import WagtailTestUtils
10
16
  from wagtail.test.utils.template_tests import AdminTemplateTestUtils
11
17
 
@@ -549,6 +555,19 @@ class TestEditCollection(CollectionInstanceTestUtils, WagtailTestUtils, TestCase
549
555
  # Retrieve edit form and check fields
550
556
  response = self.get(collection_id=self.marketing_sub_collection.id)
551
557
  self.assertNotContains(response, "Delete collection")
558
+
559
+ # Add delete permission to a different collection and try again,
560
+ # ensure that it checks against the tree structure, and not just a
561
+ # "delete collection" permission on any collection
562
+ # See https://github.com/wagtail/wagtail/issues/10084
563
+ GroupCollectionPermission.objects.create(
564
+ group=self.marketing_group,
565
+ collection=self.marketing_sub_collection_2,
566
+ permission=self.delete_permission,
567
+ )
568
+ response = self.get(collection_id=self.marketing_sub_collection.id)
569
+ self.assertNotContains(response, "Delete collection")
570
+
552
571
  # Add delete permission to parent collection and try again
553
572
  GroupCollectionPermission.objects.create(
554
573
  group=self.marketing_group,
@@ -737,3 +756,45 @@ class TestDeleteCollection(CollectionInstanceTestUtils, WagtailTestUtils, TestCa
737
756
 
738
757
  # Check that the collection was not deleted
739
758
  self.assertTrue(Collection.objects.get(id=self.marketing_sub_collection.id))
759
+
760
+
761
+ class TestSetCollectionPrivacy(CollectionInstanceTestUtils, WagtailTestUtils, TestCase):
762
+ def setUp(self):
763
+ super().setUp()
764
+ self.login()
765
+
766
+ def get(self, collection_id, params={}):
767
+ return self.client.get(
768
+ reverse("wagtailadmin_collections:set_privacy", args=(collection_id,)),
769
+ params,
770
+ )
771
+
772
+ def test_get_private_child(self):
773
+ CollectionViewRestriction.objects.create(
774
+ collection=self.root_collection,
775
+ restriction_type="password",
776
+ password="password123",
777
+ )
778
+ response = self.get(self.marketing_sub_collection.pk)
779
+ # Check response
780
+ self.assertEqual(response.status_code, 200)
781
+ self.assertTemplateUsed(
782
+ response, "wagtailadmin/collection_privacy/ancestor_privacy.html"
783
+ )
784
+ self.assertContains(
785
+ response,
786
+ "This collection has been made private by a parent collection.",
787
+ )
788
+
789
+ # Should render without any heading, as the dialog already has a heading
790
+ soup = self.get_soup(json.loads(response.content)["html"])
791
+ self.assertIsNone(soup.select_one("header"))
792
+ self.assertIsNone(soup.select_one("h1"))
793
+
794
+ # Should link to the edit page for the collection with the restriction
795
+ link = soup.select_one("a")
796
+ parent_edit_url = reverse(
797
+ "wagtailadmin_collections:edit",
798
+ args=(self.root_collection.pk,),
799
+ )
800
+ self.assertEqual(link.get("href"), parent_edit_url)
@@ -52,6 +52,7 @@ from wagtail.test.testapp.models import (
52
52
  FormPageWithRedirect,
53
53
  GalleryPage,
54
54
  PageChooserModel,
55
+ PersonPage,
55
56
  RestaurantPage,
56
57
  RestaurantTag,
57
58
  SimplePage,
@@ -1522,6 +1523,42 @@ class TestInlinePanel(WagtailTestUtils, TestCase):
1522
1523
  )
1523
1524
 
1524
1525
 
1526
+ class TestNonOrderableInlinePanel(WagtailTestUtils, TestCase):
1527
+ fixtures = ["test.json"]
1528
+
1529
+ def setUp(self):
1530
+ self.request = get_dummy_request()
1531
+ user = AnonymousUser() # technically, Anonymous users cannot access the admin
1532
+ self.request.user = user
1533
+
1534
+ def test_render(self):
1535
+ """
1536
+ Check that the inline panel renders the panels set on the model
1537
+ when no 'panels' parameter is passed in the InlinePanel definition
1538
+ """
1539
+ social_link_object_list = ObjectList(
1540
+ [
1541
+ InlinePanel(
1542
+ "social_links",
1543
+ label="Social Links",
1544
+ )
1545
+ ]
1546
+ ).bind_to_model(PersonPage)
1547
+ PersonPageForm = social_link_object_list.get_form_class()
1548
+
1549
+ person_page = PersonPage()
1550
+ form = PersonPageForm(instance=person_page)
1551
+ panel = social_link_object_list.get_bound_panel(
1552
+ instance=person_page, form=form, request=self.request
1553
+ )
1554
+ result = panel.render_html()
1555
+ # rendered panel must not contain hidden fields for ORDER
1556
+ self.assertNotInHTML(
1557
+ 'id="id_social_links-__prefix__-ORDER"',
1558
+ result,
1559
+ )
1560
+
1561
+
1525
1562
  class TestInlinePanelGetComparison(TestCase):
1526
1563
  fixtures = ["test.json"]
1527
1564