wagtail 6.0.2__py3-none-any.whl → 6.1rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (355) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/admin/checks.py +51 -0
  3. wagtail/admin/compare.py +1 -1
  4. wagtail/admin/filters.py +70 -1
  5. wagtail/admin/forms/account.py +1 -1
  6. wagtail/admin/forms/collections.py +15 -0
  7. wagtail/admin/forms/pages.py +49 -0
  8. wagtail/admin/locale/de/LC_MESSAGES/django.mo +0 -0
  9. wagtail/admin/locale/de/LC_MESSAGES/django.po +5 -5
  10. wagtail/admin/locale/en/LC_MESSAGES/django.po +474 -385
  11. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +3 -3
  12. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  13. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.po +73 -2
  14. wagtail/admin/locale/ro/LC_MESSAGES/django.mo +0 -0
  15. wagtail/admin/locale/ro/LC_MESSAGES/django.po +3 -3
  16. wagtail/admin/panels/comment_panel.py +1 -1
  17. wagtail/admin/panels/field_panel.py +1 -1
  18. wagtail/admin/rich_text/converters/editor_html.py +3 -1
  19. wagtail/admin/rich_text/editors/draftail/__init__.py +28 -2
  20. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  21. wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
  22. wagtail/admin/static/wagtailadmin/images/favicon.ico +0 -0
  23. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  24. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  25. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  26. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  27. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  28. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  29. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -1
  30. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  31. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  32. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +1 -1
  33. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  34. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  37. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  38. wagtail/admin/static/wagtailadmin/js/preview-panel.js +1 -1
  39. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  40. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  41. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  42. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  43. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  44. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  45. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  46. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  47. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  48. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +4 -4
  49. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  50. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  51. wagtail/admin/staticfiles.py +1 -0
  52. wagtail/admin/templates/wagtailadmin/base.html +1 -0
  53. wagtail/admin/templates/wagtailadmin/collection_privacy/set_privacy.html +3 -1
  54. wagtail/admin/templates/wagtailadmin/collections/index_results.html +10 -0
  55. wagtail/admin/templates/wagtailadmin/generic/base.html +1 -9
  56. wagtail/admin/templates/wagtailadmin/generic/form.html +3 -2
  57. wagtail/admin/templates/wagtailadmin/generic/history/action_cell.html +27 -0
  58. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +3 -3
  59. wagtail/admin/templates/wagtailadmin/icons/keyboard.svg +1 -0
  60. wagtail/admin/templates/wagtailadmin/page_privacy/set_privacy.html +3 -1
  61. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -14
  62. wagtail/admin/templates/wagtailadmin/pages/action_menu/save_draft.html +3 -1
  63. wagtail/admin/templates/wagtailadmin/pages/choose_parent.html +17 -0
  64. wagtail/admin/templates/wagtailadmin/pages/explorable_index.html +8 -0
  65. wagtail/admin/templates/wagtailadmin/pages/history.html +1 -61
  66. wagtail/admin/templates/wagtailadmin/pages/index.html +1 -3
  67. wagtail/admin/templates/wagtailadmin/pages/listing/_locked_indicator.html +2 -2
  68. wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_column_header.html +25 -27
  69. wagtail/admin/templates/wagtailadmin/pages/page_listing_header.html +2 -1
  70. wagtail/admin/templates/wagtailadmin/panels/multi_field_panel_child.html +1 -1
  71. wagtail/admin/templates/wagtailadmin/panels/publishing/schedule_publishing_panel.html +3 -1
  72. wagtail/admin/templates/wagtailadmin/panels/tabbed_interface.html +1 -1
  73. wagtail/admin/templates/wagtailadmin/shared/active_filters.html +2 -1
  74. wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +8 -0
  75. wagtail/admin/templates/wagtailadmin/shared/forms/single_checkbox.html +1 -1
  76. wagtail/admin/templates/wagtailadmin/shared/headers/page_edit_header.html +1 -1
  77. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +21 -9
  78. wagtail/admin/templates/wagtailadmin/shared/human_readable_date.html +1 -1
  79. wagtail/admin/templates/wagtailadmin/shared/keyboard_shortcuts_dialog.html +29 -0
  80. wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +2 -1
  81. wagtail/admin/templates/wagtailadmin/skeleton.html +2 -1
  82. wagtail/admin/templates/wagtailadmin/tables/related_objects_cell.html +9 -0
  83. wagtail/admin/templates/wagtailadmin/tables/title_cell.html +9 -7
  84. wagtail/admin/templates/wagtailadmin/widgets/draftail_rich_text_area.html +1 -1
  85. wagtail/admin/templates/wagtailadmin/workflows/create.html +6 -23
  86. wagtail/admin/templates/wagtailadmin/workflows/create_task.html +6 -15
  87. wagtail/admin/templates/wagtailadmin/workflows/edit.html +6 -23
  88. wagtail/admin/templates/wagtailadmin/workflows/edit_task.html +6 -13
  89. wagtail/admin/templates/wagtailadmin/workflows/includes/task_usage_cell.html +4 -4
  90. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_tasks_cell.html +18 -0
  91. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_title_cell.html +7 -0
  92. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_used_by_cell.html +25 -0
  93. wagtail/admin/templates/wagtailadmin/workflows/index.html +0 -99
  94. wagtail/admin/templates/wagtailadmin/workflows/index_results.html +10 -0
  95. wagtail/admin/templates/wagtailadmin/workflows/task_index.html +0 -30
  96. wagtail/admin/templates/wagtailadmin/workflows/task_index_results.html +10 -0
  97. wagtail/admin/templates/wagtailadmin/workflows/usage.html +1 -1
  98. wagtail/admin/templatetags/wagtailadmin_tags.py +116 -39
  99. wagtail/admin/tests/pages/test_create_page.py +10 -4
  100. wagtail/admin/tests/pages/test_custom_listing.py +37 -0
  101. wagtail/admin/tests/pages/test_edit_page.py +6 -6
  102. wagtail/admin/tests/pages/test_explorer_view.py +19 -18
  103. wagtail/admin/tests/pages/test_move_page.py +1 -1
  104. wagtail/admin/tests/pages/test_page_usage.py +50 -2
  105. wagtail/admin/tests/pages/test_parent_page_chooser_view.py +119 -0
  106. wagtail/admin/tests/pages/test_preview.py +18 -4
  107. wagtail/admin/tests/test_account_management.py +20 -1
  108. wagtail/admin/tests/test_audit_log.py +172 -5
  109. wagtail/admin/tests/test_checks.py +92 -0
  110. wagtail/admin/tests/test_collections_views.py +19 -5
  111. wagtail/admin/tests/test_compare.py +6 -6
  112. wagtail/admin/tests/test_dashboard.py +404 -0
  113. wagtail/admin/tests/test_dbwhitelister.py +4 -5
  114. wagtail/admin/tests/test_edit_handlers.py +2 -2
  115. wagtail/admin/tests/test_keyboard_shortcuts.py +84 -0
  116. wagtail/admin/tests/test_page_chooser.py +31 -18
  117. wagtail/admin/tests/test_privacy.py +36 -2
  118. wagtail/admin/tests/test_rich_text.py +168 -23
  119. wagtail/admin/tests/test_templatetags.py +411 -43
  120. wagtail/admin/tests/test_views.py +4 -2
  121. wagtail/admin/tests/test_workflows.py +531 -9
  122. wagtail/admin/tests/tests.py +3 -1
  123. wagtail/admin/tests/ui/test_tables.py +48 -1
  124. wagtail/admin/tests/viewsets/test_model_viewset.py +126 -29
  125. wagtail/admin/ui/side_panels.py +3 -1
  126. wagtail/admin/ui/tables/__init__.py +13 -1
  127. wagtail/admin/ui/tables/pages.py +17 -6
  128. wagtail/admin/urls/__init__.py +8 -3
  129. wagtail/admin/urls/pages.py +5 -0
  130. wagtail/admin/urls/workflows.py +10 -0
  131. wagtail/admin/views/chooser.py +20 -24
  132. wagtail/admin/views/collections.py +17 -1
  133. wagtail/admin/views/generic/base.py +31 -4
  134. wagtail/admin/views/generic/history.py +220 -51
  135. wagtail/admin/views/generic/mixins.py +7 -4
  136. wagtail/admin/views/generic/models.py +54 -38
  137. wagtail/admin/views/generic/multiple_upload.py +17 -8
  138. wagtail/admin/views/generic/usage.py +17 -11
  139. wagtail/admin/views/home.py +15 -12
  140. wagtail/admin/views/mixins.py +30 -0
  141. wagtail/admin/views/pages/choose_parent.py +73 -0
  142. wagtail/admin/views/pages/history.py +54 -66
  143. wagtail/admin/views/pages/listing.py +187 -106
  144. wagtail/admin/views/pages/usage.py +6 -1
  145. wagtail/admin/views/pages/utils.py +70 -1
  146. wagtail/admin/views/workflows.py +150 -21
  147. wagtail/admin/viewsets/model.py +2 -2
  148. wagtail/admin/viewsets/pages.py +77 -0
  149. wagtail/admin/wagtail_hooks.py +40 -2
  150. wagtail/admin/widgets/button.py +10 -9
  151. wagtail/api/v2/filters.py +1 -1
  152. wagtail/api/v2/tests/test_pages.py +1 -1
  153. wagtail/blocks/base.py +18 -9
  154. wagtail/blocks/field_block.py +9 -7
  155. wagtail/blocks/list_block.py +16 -6
  156. wagtail/blocks/static_block.py +3 -0
  157. wagtail/blocks/stream_block.py +58 -23
  158. wagtail/blocks/struct_block.py +15 -9
  159. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +39 -47
  160. wagtail/contrib/forms/models.py +5 -5
  161. wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +44 -33
  162. wagtail/contrib/forms/templates/wagtailforms/submissions_index.html +2 -63
  163. wagtail/contrib/forms/tests/test_models.py +26 -0
  164. wagtail/contrib/forms/urls.py +6 -0
  165. wagtail/contrib/forms/views.py +52 -49
  166. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.mo +0 -0
  167. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.po +3 -3
  168. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +34 -42
  169. wagtail/contrib/redirects/signal_handlers.py +1 -1
  170. wagtail/contrib/redirects/templates/wagtailredirects/index.html +1 -36
  171. wagtail/contrib/redirects/templates/wagtailredirects/index_results.html +18 -0
  172. wagtail/contrib/redirects/templates/wagtailredirects/redirect_target_cell.html +8 -0
  173. wagtail/contrib/redirects/tests/test_import_command.py +1 -1
  174. wagtail/contrib/redirects/tests/test_redirects.py +79 -8
  175. wagtail/contrib/redirects/urls.py +2 -1
  176. wagtail/contrib/redirects/views.py +85 -55
  177. wagtail/contrib/search_promotions/admin_urls.py +2 -1
  178. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +41 -64
  179. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index.html +1 -16
  180. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index_results.html +11 -0
  181. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/list.html +0 -51
  182. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/results.html +3 -16
  183. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/search_promotion_column.html +15 -0
  184. wagtail/contrib/search_promotions/tests.py +122 -9
  185. wagtail/contrib/search_promotions/views.py +66 -65
  186. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +3 -3
  187. wagtail/contrib/settings/registry.py +10 -5
  188. wagtail/contrib/settings/tests/generic/test_admin.py +9 -0
  189. wagtail/contrib/settings/tests/site_specific/test_admin.py +10 -1
  190. wagtail/contrib/settings/tests/site_specific/test_model.py +3 -3
  191. wagtail/contrib/settings/tests/site_specific/test_templates.py +1 -1
  192. wagtail/contrib/settings/views.py +3 -1
  193. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  194. wagtail/contrib/simple_translation/tests/test_wagtail_hooks.py +2 -2
  195. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  196. wagtail/contrib/table_block/blocks.py +1 -1
  197. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  198. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  199. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +1 -1
  200. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  201. wagtail/coreutils.py +3 -2
  202. wagtail/documents/admin_urls.py +2 -2
  203. wagtail/documents/locale/en/LC_MESSAGES/django.po +22 -22
  204. wagtail/documents/migrations/0013_delete_uploadeddocument.py +16 -0
  205. wagtail/documents/models.py +1 -20
  206. wagtail/documents/rich_text/__init__.py +11 -7
  207. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  208. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  209. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  210. wagtail/documents/templates/wagtaildocs/documents/index.html +0 -16
  211. wagtail/documents/tests/test_admin_views.py +155 -23
  212. wagtail/documents/tests/test_collection_privacy.py +55 -1
  213. wagtail/documents/tests/test_rich_text.py +14 -0
  214. wagtail/documents/views/documents.py +25 -22
  215. wagtail/documents/views/multiple.py +6 -7
  216. wagtail/documents/views/serve.py +16 -1
  217. wagtail/documents/wagtail_hooks.py +20 -15
  218. wagtail/embeds/blocks.py +5 -0
  219. wagtail/embeds/locale/en/LC_MESSAGES/django.po +2 -2
  220. wagtail/embeds/rich_text/__init__.py +1 -1
  221. wagtail/embeds/tests/test_rich_text.py +14 -0
  222. wagtail/embeds/wagtail_hooks.py +4 -14
  223. wagtail/fields.py +3 -48
  224. wagtail/images/admin_urls.py +2 -2
  225. wagtail/images/check_files/wagtail.jpg +0 -0
  226. wagtail/images/check_files/wagtail.png +0 -0
  227. wagtail/images/fields.py +2 -0
  228. wagtail/images/image_operations.py +1 -1
  229. wagtail/images/locale/en/LC_MESSAGES/django.po +33 -45
  230. wagtail/images/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  231. wagtail/images/locale/pt_PT/LC_MESSAGES/django.po +4 -0
  232. wagtail/images/migrations/0026_delete_uploadedimage.py +16 -0
  233. wagtail/images/models.py +49 -43
  234. wagtail/images/rich_text/__init__.py +18 -8
  235. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  236. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  237. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  238. wagtail/images/templates/wagtailimages/images/image_listing_header.html +6 -0
  239. wagtail/images/templates/wagtailimages/images/index.html +11 -51
  240. wagtail/images/tests/test_admin_views.py +119 -62
  241. wagtail/images/tests/test_image_operations.py +10 -0
  242. wagtail/images/tests/test_models.py +35 -33
  243. wagtail/images/tests/test_rich_text.py +14 -0
  244. wagtail/images/tests/utils.py +1 -1
  245. wagtail/images/views/images.py +35 -64
  246. wagtail/images/views/multiple.py +6 -7
  247. wagtail/images/wagtail_hooks.py +4 -14
  248. wagtail/locale/en/LC_MESSAGES/django.po +150 -136
  249. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  250. wagtail/locales/tests.py +18 -3
  251. wagtail/locales/views.py +0 -1
  252. wagtail/management/commands/rebuild_references_index.py +3 -1
  253. wagtail/migrations/0092_alter_collectionviewrestriction_password_and_more.py +33 -0
  254. wagtail/migrations/0093_uploadedfile.py +53 -0
  255. wagtail/models/__init__.py +147 -32
  256. wagtail/models/i18n.py +1 -1
  257. wagtail/models/{collections.py → media.py} +33 -2
  258. wagtail/models/reference_index.py +1 -1
  259. wagtail/models/view_restrictions.py +10 -3
  260. wagtail/project_template/project_name/settings/base.py +6 -0
  261. wagtail/project_template/requirements.txt +1 -1
  262. wagtail/rich_text/__init__.py +25 -8
  263. wagtail/rich_text/pages.py +19 -8
  264. wagtail/rich_text/rewriters.py +140 -68
  265. wagtail/search/backends/database/mysql/mysql.py +3 -3
  266. wagtail/search/backends/database/postgres/postgres.py +3 -3
  267. wagtail/search/backends/database/sqlite/sqlite.py +2 -2
  268. wagtail/search/backends/elasticsearch7.py +4 -0
  269. wagtail/search/locale/en/LC_MESSAGES/django.po +3 -3
  270. wagtail/search/tests/test_postgres_backend.py +50 -0
  271. wagtail/sites/locale/en/LC_MESSAGES/django.po +8 -8
  272. wagtail/sites/tests.py +35 -9
  273. wagtail/sites/views.py +3 -1
  274. wagtail/snippets/locale/de/LC_MESSAGES/django.mo +0 -0
  275. wagtail/snippets/locale/de/LC_MESSAGES/django.po +5 -6
  276. wagtail/snippets/locale/en/LC_MESSAGES/django.po +16 -56
  277. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  278. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  279. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/publish.html +3 -1
  280. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/save.html +3 -1
  281. wagtail/snippets/templates/wagtailsnippets/snippets/create.html +1 -1
  282. wagtail/snippets/templates/wagtailsnippets/snippets/edit.html +1 -1
  283. wagtail/snippets/tests/test_preview.py +13 -2
  284. wagtail/snippets/tests/test_snippets.py +41 -16
  285. wagtail/snippets/tests/test_viewset.py +63 -18
  286. wagtail/snippets/tests/test_workflows.py +12 -0
  287. wagtail/snippets/views/snippets.py +1 -40
  288. wagtail/templatetags/wagtailcore_tags.py +1 -1
  289. wagtail/test/demosite/models.py +1 -1
  290. wagtail/test/middleware.py +14 -1
  291. wagtail/test/testapp/fixtures/test.json +20 -0
  292. wagtail/test/testapp/migrations/0001_squashed_0073_revisablechildmodel_secret_text.py +8 -8
  293. wagtail/test/testapp/migrations/0023_snippetchoosermodel_full_featured.py +1 -0
  294. wagtail/test/testapp/migrations/0034_custompermissionmodel.py +44 -0
  295. wagtail/test/testapp/migrations/0035_modelwithcustommanager.py +30 -0
  296. wagtail/test/testapp/migrations/0036_complexdefaultstreampage.py +28 -0
  297. wagtail/test/testapp/models.py +79 -2
  298. wagtail/test/testapp/templates/tests/custom_docs_password_required.html +10 -0
  299. wagtail/test/testapp/templates/tests/custom_page_password_required.html +10 -0
  300. wagtail/test/testapp/views.py +24 -2
  301. wagtail/test/testapp/wagtail_hooks.py +19 -0
  302. wagtail/test/utils/wagtail_tests.py +2 -2
  303. wagtail/tests/test_blocks.py +262 -1
  304. wagtail/tests/test_migrations.py +1 -1
  305. wagtail/tests/test_page_model.py +77 -0
  306. wagtail/tests/test_page_privacy.py +18 -1
  307. wagtail/tests/test_rich_text.py +95 -5
  308. wagtail/tests/test_streamfield.py +43 -0
  309. wagtail/tests/test_utils.py +8 -2
  310. wagtail/tests/test_views.py +52 -1
  311. wagtail/tests/test_whitelist.py +7 -7
  312. wagtail/users/forms.py +3 -1
  313. wagtail/users/locale/en/LC_MESSAGES/django.po +124 -96
  314. wagtail/users/migrations/0013_userprofile_density.py +23 -0
  315. wagtail/users/models.py +14 -3
  316. wagtail/users/templates/wagtailusers/groups/create.html +1 -7
  317. wagtail/users/templates/wagtailusers/groups/edit.html +1 -13
  318. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +46 -2
  319. wagtail/users/templates/wagtailusers/groups/includes/group_form_js.html +0 -2
  320. wagtail/users/templates/wagtailusers/users/create.html +1 -9
  321. wagtail/users/templates/wagtailusers/users/edit.html +1 -9
  322. wagtail/users/templates/wagtailusers/users/index.html +2 -5
  323. wagtail/users/templates/wagtailusers/users/index_results.html +3 -13
  324. wagtail/users/templates/wagtailusers/users/user_cell.html +9 -0
  325. wagtail/users/templatetags/wagtailusers_tags.py +107 -20
  326. wagtail/users/tests/test_admin_views.py +669 -90
  327. wagtail/users/views/groups.py +58 -61
  328. wagtail/users/views/users.py +211 -92
  329. wagtail/users/wagtail_hooks.py +6 -38
  330. wagtail/users/widgets.py +3 -5
  331. wagtail/utils/text.py +1 -1
  332. wagtail/views.py +5 -9
  333. wagtail/whitelist.py +1 -1
  334. {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/METADATA +4 -5
  335. {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/RECORD +339 -320
  336. wagtail/admin/static/wagtailadmin/js/page-editor.js +0 -1
  337. wagtail/admin/static/wagtailadmin/js/vendor/mousetrap.min.js +0 -1
  338. wagtail/admin/static/wagtailadmin/js/vendor/urlify.js +0 -1
  339. wagtail/admin/static/wagtailadmin/js/vendor/xregexp.min.js +0 -1
  340. wagtail/admin/templates/wagtailadmin/collections/index.html +0 -34
  341. wagtail/admin/templates/wagtailadmin/pages/revisions/_actions.html +0 -22
  342. wagtail/admin/templates/wagtailadmin/shared/page_breadcrumbs.html +0 -55
  343. wagtail/admin/tests/pages/test_dashboard.py +0 -172
  344. wagtail/contrib/redirects/templates/wagtailredirects/results.html +0 -23
  345. wagtail/documents/templates/wagtaildocs/documents/list.html +0 -2
  346. wagtail/search/tests/test_postgres_stemming.py +0 -40
  347. wagtail/sites/templates/wagtailsites/create.html +0 -6
  348. wagtail/sites/templates/wagtailsites/edit.html +0 -6
  349. wagtail/snippets/templates/wagtailsnippets/snippets/revisions/_actions.html +0 -36
  350. wagtail/users/templates/wagtailusers/users/list.html +0 -62
  351. wagtail/users/urls/users.py +0 -12
  352. {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/LICENSE +0 -0
  353. {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/WHEEL +0 -0
  354. {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/entry_points.txt +0 -0
  355. {wagtail-6.0.2.dist-info → wagtail-6.1rc1.dist-info}/top_level.txt +0 -0
@@ -1,5 +1,6 @@
1
+ import datetime
1
2
  import json
2
- from datetime import datetime
3
+ import re
3
4
  from urllib.parse import urljoin
4
5
  from warnings import warn
5
6
 
@@ -40,6 +41,7 @@ from wagtail.admin.utils import (
40
41
  get_valid_next_url_from_request,
41
42
  )
42
43
  from wagtail.admin.views.bulk_action.registry import bulk_action_registry
44
+ from wagtail.admin.views.pages.utils import get_breadcrumbs_items_for_page
43
45
  from wagtail.admin.widgets import Button, ButtonWithDropdown, PageListingButton
44
46
  from wagtail.coreutils import (
45
47
  accepts_kwarg,
@@ -55,7 +57,6 @@ from wagtail.models import (
55
57
  Page,
56
58
  PageViewRestriction,
57
59
  )
58
- from wagtail.permissions import page_permission_policy
59
60
  from wagtail.telepath import JSContext
60
61
  from wagtail.users.utils import get_gravatar_url
61
62
  from wagtail.utils.deprecation import RemovedInWagtail70Warning
@@ -67,11 +68,16 @@ register.filter("naturaltime", naturaltime)
67
68
 
68
69
 
69
70
  @register.inclusion_tag("wagtailadmin/shared/breadcrumbs.html")
70
- def breadcrumbs(items, is_expanded=False, classname=None):
71
- return {"items": items, "is_expanded": is_expanded, "classname": classname}
71
+ def breadcrumbs(items, is_expanded=False, classname=None, icon_name=None):
72
+ return {
73
+ "items": items,
74
+ "is_expanded": is_expanded or len(items) == 1,
75
+ "classname": classname,
76
+ "icon_name": icon_name,
77
+ }
72
78
 
73
79
 
74
- @register.inclusion_tag("wagtailadmin/shared/page_breadcrumbs.html", takes_context=True)
80
+ @register.inclusion_tag("wagtailadmin/shared/breadcrumbs.html", takes_context=True)
75
81
  def page_breadcrumbs(
76
82
  context,
77
83
  page,
@@ -79,38 +85,33 @@ def page_breadcrumbs(
79
85
  url_root_name=None,
80
86
  include_self=True,
81
87
  is_expanded=False,
82
- page_perms=None,
83
- querystring_value=None,
88
+ querystring_value="",
84
89
  trailing_breadcrumb_title=None,
85
90
  classname=None,
91
+ icon_name=None,
86
92
  ):
87
93
  user = context["request"].user
88
94
 
89
- # find the closest common ancestor of the pages that this user has direct explore permission
90
- # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb
91
- cca = page_permission_policy.explorable_root_instance(user)
92
- if not cca:
93
- return {"items": Page.objects.none()}
94
-
95
- items = (
96
- page.get_ancestors(inclusive=include_self)
97
- .descendant_of(cca, inclusive=True)
98
- .specific()
95
+ items = get_breadcrumbs_items_for_page(
96
+ page,
97
+ user,
98
+ url_name,
99
+ url_root_name,
100
+ include_self,
101
+ querystring_value,
99
102
  )
100
103
 
104
+ if trailing_breadcrumb_title:
105
+ items.append({"label": trailing_breadcrumb_title})
106
+
101
107
  if len(items) == 1:
102
108
  is_expanded = True
103
109
 
104
110
  return {
105
111
  "items": items,
106
- "current_page": page,
107
112
  "is_expanded": is_expanded,
108
- "page_perms": page_perms,
109
- "querystring_value": querystring_value or "",
110
- "trailing_breadcrumb_title": trailing_breadcrumb_title, # Only used in collapsible breadcrumb templates
111
- "url_name": url_name,
112
- "url_root_name": url_root_name,
113
113
  "classname": classname,
114
+ "icon_name": icon_name,
114
115
  }
115
116
 
116
117
 
@@ -223,7 +224,15 @@ def classnames(*classes):
223
224
  Usage <div class="{% classnames "w-base" classname active|yesno:"w-base--active," any_other_var %}"></div>
224
225
  Returns any args as a space-separated joined string for using in HTML class names.
225
226
  """
226
- return " ".join([classname.strip() for classname in classes if classname])
227
+
228
+ flattened = []
229
+ for classname in classes:
230
+ if isinstance(classname, str):
231
+ flattened.append(classname)
232
+ elif hasattr(classname, "__iter__"):
233
+ flattened.extend(classname)
234
+
235
+ return " ".join([classname.strip() for classname in flattened if classname])
227
236
 
228
237
 
229
238
  @register.simple_tag(takes_context=True)
@@ -657,7 +666,12 @@ def admin_theme_classname(context):
657
666
  if hasattr(user, "wagtail_userprofile")
658
667
  else "system"
659
668
  )
660
- return f"w-theme-{theme_name}"
669
+ density_name = (
670
+ user.wagtail_userprofile.density
671
+ if hasattr(user, "wagtail_userprofile")
672
+ else "default"
673
+ )
674
+ return f"w-theme-{theme_name} w-density-{density_name}"
661
675
 
662
676
 
663
677
  @register.simple_tag
@@ -752,7 +766,6 @@ def timesince_simple(d):
752
766
  1 week, 1 day ago -> 1 week ago
753
767
  0 minutes ago -> just now
754
768
  """
755
- # Note: Duplicate code in timesince_last_update()
756
769
  time_period = timesince(d).split(",")[0]
757
770
  if time_period == avoid_wrapping(_("0 minutes")):
758
771
  return _("just now")
@@ -771,11 +784,25 @@ def timesince_last_update(
771
784
  """
772
785
  # translation usage below is intentionally verbose to be easier to work with translations
773
786
 
774
- if last_update.date() == datetime.today().date():
787
+ current_datetime = timezone.now()
788
+ if timezone.is_aware(current_datetime):
789
+ # timezone support is enabled - make last_update timezone-aware and set to the user's
790
+ # timezone
791
+ current_datetime = timezone.localtime(current_datetime)
775
792
  if timezone.is_aware(last_update):
776
- time_str = timezone.localtime(last_update).strftime("%H:%M")
793
+ local_datetime = timezone.localtime(last_update)
777
794
  else:
778
- time_str = last_update.strftime("%H:%M")
795
+ local_datetime = timezone.make_aware(last_update)
796
+ else:
797
+ # timezone support is disabled - use naive datetimes
798
+ if timezone.is_aware(last_update):
799
+ local_datetime = timezone.make_naive(last_update)
800
+ else:
801
+ local_datetime = last_update
802
+
803
+ # Use an explicit timestamp if last_update is today as seen in the current user's time zone
804
+ if local_datetime.date() == current_datetime.date():
805
+ time_str = local_datetime.strftime("%H:%M")
779
806
 
780
807
  if show_time_prefix:
781
808
  if user_display_name:
@@ -795,17 +822,9 @@ def timesince_last_update(
795
822
  return time_str
796
823
  else:
797
824
  if use_shorthand:
798
- # Note: Duplicate code in timesince_simple()
799
- time_period = timesince(last_update).split(",")[0]
800
- if time_period == avoid_wrapping(_("0 minutes")):
801
- if user_display_name:
802
- return _("just now by %(user_display_name)s") % {
803
- "user_display_name": user_display_name
804
- }
805
- else:
806
- return _("just now")
825
+ time_period = timesince(local_datetime, now=current_datetime).split(",")[0]
807
826
  else:
808
- time_period = timesince(last_update)
827
+ time_period = timesince(local_datetime, now=current_datetime)
809
828
 
810
829
  if user_display_name:
811
830
  return _("%(time_period)s ago by %(user_display_name)s") % {
@@ -1288,12 +1307,70 @@ def workflow_status_with_date(workflow_state):
1288
1307
  return _("%(status_display)s %(task_name)s %(started_at)s") % translation_context
1289
1308
 
1290
1309
 
1310
+ @register.inclusion_tag(
1311
+ "wagtailadmin/shared/keyboard_shortcuts_dialog.html",
1312
+ takes_context=True,
1313
+ )
1314
+ def keyboard_shortcuts_dialog(context):
1315
+ """
1316
+ Renders the keyboard shortcuts dialog content with the
1317
+ appropriate shortcuts for the user's platform.
1318
+ Note: Shortcut keys are intentionally not translated.
1319
+ """
1320
+
1321
+ user_agent = context["request"].headers.get("User-Agent", "")
1322
+ is_mac = re.search(r"Mac|iPod|iPhone|iPad", user_agent)
1323
+ modifier = "⌘" if is_mac else "Ctrl"
1324
+
1325
+ return {
1326
+ "shortcuts": {
1327
+ ("actions-common", _("Common actions")): [
1328
+ (_("Copy"), f"{modifier} + c"),
1329
+ (_("Cut"), f"{modifier} + x"),
1330
+ (_("Paste"), f"{modifier} + v"),
1331
+ (
1332
+ _("Paste and match style")
1333
+ if is_mac
1334
+ else _("Paste without formatting"),
1335
+ f"{modifier} + Shift + v",
1336
+ ),
1337
+ (_("Undo"), f"{modifier} + z"),
1338
+ (
1339
+ _("Redo"),
1340
+ f"{modifier} + Shift + z" if is_mac else f"{modifier} + y",
1341
+ ),
1342
+ ],
1343
+ ("actions-model", _("Actions")): [
1344
+ (_("Save changes"), f"{modifier} + s"),
1345
+ (_("Preview"), f"{modifier} + p"),
1346
+ ],
1347
+ ("rich-text-content", _("Text content")): [
1348
+ (_("Insert or edit a link"), f"{modifier} + k")
1349
+ ],
1350
+ ("rich-text-formatting", _("Text formatting")): [
1351
+ (_("Bold"), f"{modifier} + b"),
1352
+ (_("Italic"), f"{modifier} + i"),
1353
+ (_("Underline"), f"{modifier} + u"),
1354
+ (_("Monospace (code)"), f"{modifier} + j"),
1355
+ (_("Strike-through"), f"{modifier} + x"),
1356
+ (_("Superscript"), f"{modifier} + ."),
1357
+ (_("Subscript"), f"{modifier} + ,"),
1358
+ ],
1359
+ }
1360
+ }
1361
+
1362
+
1291
1363
  @register.inclusion_tag("wagtailadmin/shared/human_readable_date.html")
1292
1364
  def human_readable_date(date, description=None, placement="top"):
1365
+ if isinstance(date, datetime.datetime):
1366
+ tooltip_format = getattr(settings, "DATETIME_FORMAT", "N j, Y, P")
1367
+ elif isinstance(date, datetime.date):
1368
+ tooltip_format = getattr(settings, "DATE_FORMAT", "N j, Y")
1293
1369
  return {
1294
1370
  "date": date,
1295
1371
  "description": description,
1296
1372
  "placement": placement,
1373
+ "tooltip_format": tooltip_format,
1297
1374
  }
1298
1375
 
1299
1376
 
@@ -1494,7 +1494,13 @@ class TestInlineStreamField(WagtailTestUtils, TestCase):
1494
1494
  self.assertEqual(response.status_code, 200)
1495
1495
 
1496
1496
  # response should include HTML declarations for streamfield child blocks
1497
- self.assertContains(response, '<div id="sections-__prefix__-body" data-block="')
1497
+ self.assertContains(response, '<div id="sections-__prefix__-body" data-block')
1498
+ soup = self.get_soup(response.content)
1499
+ blockDiv = soup.find("div", {"data-controller": "w-block"})
1500
+ self.assertIsNotNone(blockDiv)
1501
+ # block div should contain this attributes
1502
+ self.assertTrue(blockDiv.has_attr("data-w-block-arguments-value"))
1503
+ self.assertTrue(blockDiv.has_attr("data-w-block-data-value"))
1498
1504
 
1499
1505
 
1500
1506
  class TestIssue2994(WagtailTestUtils, TestCase):
@@ -1806,7 +1812,7 @@ class TestPageSubscriptionSettings(WagtailTestUtils, TestCase):
1806
1812
  # Login
1807
1813
  self.user = self.login()
1808
1814
 
1809
- def test_commment_notifications_switched_on_by_default(self):
1815
+ def test_comment_notifications_switched_on_by_default(self):
1810
1816
  response = self.client.get(
1811
1817
  reverse(
1812
1818
  "wagtailadmin_pages:add",
@@ -1890,7 +1896,7 @@ class TestCommenting(WagtailTestUtils, TestCase):
1890
1896
  # Login
1891
1897
  self.user = self.login()
1892
1898
 
1893
- def test_commments_enabled_by_default(self):
1899
+ def test_comments_enabled_by_default(self):
1894
1900
  response = self.client.get(
1895
1901
  reverse(
1896
1902
  "wagtailadmin_pages:add",
@@ -1907,7 +1913,7 @@ class TestCommenting(WagtailTestUtils, TestCase):
1907
1913
  self.assertEqual("w-comments:init", form["data-w-init-event-value"])
1908
1914
 
1909
1915
  @override_settings(WAGTAILADMIN_COMMENTS_ENABLED=False)
1910
- def test_commments_disabled(self):
1916
+ def test_comments_disabled(self):
1911
1917
  response = self.client.get(
1912
1918
  reverse(
1913
1919
  "wagtailadmin_pages:add",
@@ -0,0 +1,37 @@
1
+ from django.test import TestCase
2
+
3
+ from wagtail.test.utils import WagtailTestUtils
4
+ from wagtail.test.utils.template_tests import AdminTemplateTestUtils
5
+
6
+
7
+ class TestCustomListing(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
8
+ fixtures = ["test.json"]
9
+
10
+ def test_get(self):
11
+ self.login()
12
+ response = self.client.get("/admin/event_pages/")
13
+ self.assertEqual(response.status_code, 200)
14
+ self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
15
+ self.assertContains(response, "Event pages")
16
+ self.assertContains(response, "Christmas")
17
+ self.assertContains(response, "Saint Patrick")
18
+ self.assertNotContains(response, "Welcome to the Wagtail test site!")
19
+ self.assertBreadcrumbsItemsRendered(
20
+ [{"url": "", "label": "Event pages"}],
21
+ response.content,
22
+ )
23
+ soup = self.get_soup(response.content)
24
+ breadcrumbs_icon = soup.select_one(".w-breadcrumbs__icon")
25
+ self.assertIsNotNone(breadcrumbs_icon)
26
+ use = breadcrumbs_icon.select_one("use")
27
+ self.assertIsNotNone(use)
28
+ self.assertEqual(use["href"], "#icon-calendar")
29
+
30
+ def test_filter(self):
31
+ self.login()
32
+ response = self.client.get("/admin/event_pages/", {"audience": "private"})
33
+ self.assertEqual(response.status_code, 200)
34
+ self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
35
+ self.assertContains(response, "Event pages")
36
+ self.assertNotContains(response, "Christmas")
37
+ self.assertContains(response, "Saint Patrick")
@@ -2067,7 +2067,7 @@ class TestPageEdit(WagtailTestUtils, TestCase):
2067
2067
  # as when running it within the full test suite
2068
2068
  self.client.get(reverse("wagtailadmin_pages:edit", args=(self.event_page.id,)))
2069
2069
 
2070
- with self.assertNumQueries(35):
2070
+ with self.assertNumQueries(33):
2071
2071
  self.client.get(
2072
2072
  reverse("wagtailadmin_pages:edit", args=(self.event_page.id,))
2073
2073
  )
@@ -2080,7 +2080,7 @@ class TestPageEdit(WagtailTestUtils, TestCase):
2080
2080
  # Warm up the cache as above.
2081
2081
  self.client.get(reverse("wagtailadmin_pages:edit", args=(self.event_page.id,)))
2082
2082
 
2083
- with self.assertNumQueries(39):
2083
+ with self.assertNumQueries(37):
2084
2084
  self.client.get(
2085
2085
  reverse("wagtailadmin_pages:edit", args=(self.event_page.id,))
2086
2086
  )
@@ -3088,7 +3088,7 @@ class TestPageSubscriptionSettings(WagtailTestUtils, TestCase):
3088
3088
  # Login
3089
3089
  self.user = self.login()
3090
3090
 
3091
- def test_commment_notifications_switched_off(self):
3091
+ def test_comment_notifications_switched_off(self):
3092
3092
  response = self.client.get(
3093
3093
  reverse("wagtailadmin_pages:edit", args=[self.child_page.id])
3094
3094
  )
@@ -3104,7 +3104,7 @@ class TestPageSubscriptionSettings(WagtailTestUtils, TestCase):
3104
3104
  ).exists()
3105
3105
  )
3106
3106
 
3107
- def test_commment_notifications_switched_on(self):
3107
+ def test_comment_notifications_switched_on(self):
3108
3108
  PageSubscription.objects.create(
3109
3109
  page=self.child_page, user=self.user, comment_notifications=True
3110
3110
  )
@@ -3247,7 +3247,7 @@ class TestCommenting(WagtailTestUtils, TestCase):
3247
3247
  [to for email in mail.outbox for to in email.to],
3248
3248
  )
3249
3249
 
3250
- def test_commments_enabled_by_default(self):
3250
+ def test_comments_enabled_by_default(self):
3251
3251
  response = self.client.get(
3252
3252
  reverse("wagtailadmin_pages:edit", args=[self.child_page.id])
3253
3253
  )
@@ -3261,7 +3261,7 @@ class TestCommenting(WagtailTestUtils, TestCase):
3261
3261
  self.assertEqual("w-comments:init", form["data-w-init-event-value"])
3262
3262
 
3263
3263
  @override_settings(WAGTAILADMIN_COMMENTS_ENABLED=False)
3264
- def test_commments_disabled(self):
3264
+ def test_comments_disabled(self):
3265
3265
  response = self.client.get(
3266
3266
  reverse("wagtailadmin_pages:edit", args=[self.child_page.id])
3267
3267
  )
@@ -59,12 +59,13 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
59
59
  self.assertEqual(active_filter.get_text(separator=" ", strip=True), text)
60
60
  self.assertIsNotNone(clear_button)
61
61
  self.assertNotIn(param, clear_button.attrs.get("data-w-swap-src-value"))
62
+ self.assertEqual(clear_button.attrs.get("data-w-swap-reflect-value"), "true")
62
63
 
63
64
  def test_explore(self):
64
65
  explore_url = reverse("wagtailadmin_explore", args=(self.root_page.id,))
65
66
  response = self.client.get(explore_url)
66
67
  self.assertEqual(response.status_code, 200)
67
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
68
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
68
69
  self.assertEqual(self.root_page, response.context["parent_page"])
69
70
 
70
71
  # child pages should be most recent first
@@ -120,7 +121,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
120
121
  def test_explore_root(self):
121
122
  response = self.client.get(reverse("wagtailadmin_explore_root"))
122
123
  self.assertEqual(response.status_code, 200)
123
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
124
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
124
125
  self.assertEqual(Page.objects.get(id=1), response.context["parent_page"])
125
126
  self.assertIn(self.root_page, response.context["pages"])
126
127
  # Should not contain a link to the history view
@@ -146,7 +147,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
146
147
  {"ordering": "title"},
147
148
  )
148
149
  self.assertEqual(response.status_code, 200)
149
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
150
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
150
151
  self.assertEqual(response.context["ordering"], "title")
151
152
 
152
153
  # child pages should be ordered by title
@@ -212,7 +213,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
212
213
  reverse("wagtailadmin_explore", args=(self.root_page.id,))
213
214
  )
214
215
  self.assertEqual(response.status_code, 200)
215
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
216
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
216
217
 
217
218
  # child pages should be ordered by title
218
219
  page_ids = [page.id for page in response.context["pages"]]
@@ -231,7 +232,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
231
232
  {"ordering": "-title"},
232
233
  )
233
234
  self.assertEqual(response.status_code, 200)
234
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
235
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
235
236
  self.assertEqual(response.context["ordering"], "-title")
236
237
 
237
238
  # child pages should be ordered by title
@@ -246,7 +247,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
246
247
  {"ordering": "latest_revision_created_at"},
247
248
  )
248
249
  self.assertEqual(response.status_code, 200)
249
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
250
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
250
251
  self.assertEqual(response.context["ordering"], "latest_revision_created_at")
251
252
 
252
253
  # child pages should be oldest revision first
@@ -262,7 +263,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
262
263
  {"ordering": "invalid_order"},
263
264
  )
264
265
  self.assertEqual(response.status_code, 200)
265
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
266
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
266
267
  self.assertEqual(response.context["ordering"], "-latest_revision_created_at")
267
268
 
268
269
  def test_reordering(self):
@@ -271,7 +272,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
271
272
  {"ordering": "ord"},
272
273
  )
273
274
  self.assertEqual(response.status_code, 200)
274
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
275
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
275
276
  self.assertEqual(response.context["ordering"], "ord")
276
277
 
277
278
  # child pages should be ordered by native tree order (i.e. by creation time)
@@ -292,7 +293,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
292
293
  {"polite_pages_only": "yes_please"},
293
294
  )
294
295
  self.assertEqual(response.status_code, 200)
295
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
296
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
296
297
  page_ids = [page.id for page in response.context["pages"]]
297
298
  self.assertEqual(page_ids, [self.child_page.id])
298
299
 
@@ -333,7 +334,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
333
334
  reverse("wagtailadmin_explore", args=(self.root_page.id,))
334
335
  )
335
336
  self.assertEqual(response.status_code, 200)
336
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
337
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
337
338
  self.assertContains(response, "Dummy Button")
338
339
  self.assertContains(response, "/dummy-button")
339
340
 
@@ -355,7 +356,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
355
356
  reverse("wagtailadmin_explore", args=(self.root_page.id,))
356
357
  )
357
358
  self.assertEqual(response.status_code, 200)
358
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
359
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
359
360
  self.assertContains(response, "Dummy Button")
360
361
  self.assertContains(response, "/dummy-button")
361
362
 
@@ -378,7 +379,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
378
379
 
379
380
  # Check response
380
381
  self.assertEqual(response.status_code, 200)
381
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
382
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
382
383
 
383
384
  # Check that we got the correct page
384
385
  self.assertEqual(response.context["page_obj"].number, 2)
@@ -534,14 +535,14 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
534
535
  reverse("wagtailadmin_explore", args=(self.root_page.id,))
535
536
  )
536
537
  self.assertEqual(response.status_code, 200)
537
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
538
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
538
539
 
539
540
  # try to browse into the page itself
540
541
  response = self.client.get(
541
542
  reverse("wagtailadmin_explore", args=(self.old_page.id,))
542
543
  )
543
544
  self.assertEqual(response.status_code, 200)
544
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
545
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
545
546
 
546
547
  def test_search(self):
547
548
  response = self.client.get(
@@ -549,7 +550,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
549
550
  {"q": "old"},
550
551
  )
551
552
  self.assertEqual(response.status_code, 200)
552
- self.assertTemplateUsed(response, "wagtailadmin/pages/index.html")
553
+ self.assertTemplateUsed(response, "wagtailadmin/pages/explorable_index.html")
553
554
 
554
555
  page_ids = [page.id for page in response.context["pages"]]
555
556
  self.assertEqual(page_ids, [self.old_page.id])
@@ -642,7 +643,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
642
643
 
643
644
  response = self.client.get(
644
645
  reverse("wagtailadmin_explore", args=(self.root_page.id,)),
645
- {"latest_revision_created_at_after": "2015-01-01"},
646
+ {"latest_revision_created_at_from": "2015-01-01"},
646
647
  )
647
648
  self.assertEqual(response.status_code, 200)
648
649
  page_ids = {page.id for page in response.context["pages"]}
@@ -650,7 +651,7 @@ class TestPageExplorer(WagtailTestUtils, TestCase):
650
651
  self.assertContainsActiveFilter(
651
652
  response,
652
653
  "Date updated: Jan. 1, 2015 -",
653
- "latest_revision_created_at_after=2015-01-01",
654
+ "latest_revision_created_at_from=2015-01-01",
654
655
  )
655
656
 
656
657
  def test_filter_by_owner(self):
@@ -1270,7 +1271,7 @@ class TestInWorkflowStatus(WagtailTestUtils, TestCase):
1270
1271
  # Warm up cache
1271
1272
  self.client.get(self.url)
1272
1273
 
1273
- with self.assertNumQueries(51):
1274
+ with self.assertNumQueries(47):
1274
1275
  response = self.client.get(self.url)
1275
1276
 
1276
1277
  self.assertEqual(response.status_code, 200)
@@ -243,7 +243,7 @@ class TestPageMove(WagtailTestUtils, TestCase):
243
243
 
244
244
  def test_page_move_after_parent_page_types_changes_to_different_parent_model(self):
245
245
  # Test for issue #10348
246
- # While BusinesSubIndex cannot be created under a SimplePage, we can
246
+ # While BusinessSubIndex cannot be created under a SimplePage, we can
247
247
  # still create it under a SimplePage invoking django-treebeard's add_child
248
248
  # which works great for our purposes.
249
249
  self.assertFalse(BusinessSubIndex.can_exist_under(self.section_a))
@@ -8,9 +8,13 @@ from wagtail.test.testapp.models import (
8
8
  SimplePage,
9
9
  )
10
10
  from wagtail.test.utils import WagtailTestUtils
11
+ from wagtail.test.utils.template_tests import AdminTemplateTestUtils
11
12
 
12
13
 
13
- class TestPageUsage(WagtailTestUtils, TestCase):
14
+ class TestPageUsage(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
15
+ # We don't show the "Home" breadcrumb item in page views
16
+ base_breadcrumb_items = []
17
+
14
18
  def setUp(self):
15
19
  self.user = self.login()
16
20
  self.root_page = Page.objects.get(id=2)
@@ -24,7 +28,7 @@ class TestPageUsage(WagtailTestUtils, TestCase):
24
28
  page.save_revision().publish()
25
29
  self.page = SimplePage.objects.get(id=page.id)
26
30
 
27
- def test_no_usage(self):
31
+ def test_simple(self):
28
32
  usage_url = reverse("wagtailadmin_pages:usage", args=(self.page.id,))
29
33
  response = self.client.get(usage_url)
30
34
 
@@ -33,6 +37,37 @@ class TestPageUsage(WagtailTestUtils, TestCase):
33
37
  self.assertContains(response, "Usage")
34
38
  self.assertContains(response, "Hello world!")
35
39
 
40
+ items = [
41
+ {
42
+ "url": reverse("wagtailadmin_explore_root"),
43
+ "label": "Root",
44
+ },
45
+ {
46
+ "url": reverse("wagtailadmin_explore", args=(self.root_page.id,)),
47
+ "label": "Welcome to your new Wagtail site!",
48
+ },
49
+ {
50
+ "url": reverse("wagtailadmin_explore", args=(self.page.id,)),
51
+ "label": "Hello world! (simple page)",
52
+ },
53
+ {
54
+ "url": "",
55
+ "label": "Usage",
56
+ "sublabel": "Hello world! (simple page)",
57
+ },
58
+ ]
59
+ self.assertBreadcrumbsItemsRendered(items, response.content)
60
+
61
+ # There should be exactly one edit link, rendered as a header button
62
+ edit_url = reverse("wagtailadmin_pages:edit", args=(self.page.id,))
63
+ soup = self.get_soup(response.content)
64
+ edit_links = soup.select(f"a[href='{edit_url}']")
65
+ self.assertEqual(len(edit_links), 1)
66
+ edit_link = edit_links[0]
67
+ classes = edit_link.attrs.get("class")
68
+ self.assertIn("w-header-button", classes)
69
+ self.assertIn("button", classes)
70
+
36
71
  def test_has_private_usage(self):
37
72
  PageChooserModel.objects.create(page=self.page)
38
73
  usage_url = reverse("wagtailadmin_pages:usage", args=(self.page.id,))
@@ -72,3 +107,16 @@ class TestPageUsage(WagtailTestUtils, TestCase):
72
107
  )
73
108
  self.assertContains(response, "Thank you redirect page")
74
109
  self.assertContains(response, "<td>Form page with redirect</td>", html=True)
110
+
111
+ def test_pagination(self):
112
+ for _ in range(50):
113
+ PageChooserModel.objects.create(page=self.page)
114
+
115
+ usage_url = reverse("wagtailadmin_pages:usage", args=(self.page.id,))
116
+ response = self.client.get(f"{usage_url}?p=2")
117
+
118
+ self.assertEqual(response.status_code, 200)
119
+ self.assertTemplateUsed(response, "wagtailadmin/generic/listing.html")
120
+ self.assertContains(response, "Page 2 of 3.")
121
+ self.assertContains(response, f"{usage_url}?p=1")
122
+ self.assertContains(response, f"{usage_url}?p=3")