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
@@ -4,6 +4,7 @@ from django.db.models import F
4
4
  from django.forms import CheckboxSelectMultiple, RadioSelect
5
5
  from django.shortcuts import get_object_or_404, redirect
6
6
  from django.urls import reverse
7
+ from django.utils.functional import cached_property, classproperty
7
8
  from django.utils.translation import gettext_lazy as _
8
9
  from django_filters.filters import (
9
10
  ChoiceFilter,
@@ -67,11 +68,6 @@ class EditedByFilter(MultipleUserFilter):
67
68
 
68
69
 
69
70
  class PageFilterSet(WagtailFilterSet):
70
- content_type = MultipleContentTypeFilter(
71
- label=_("Page type"),
72
- queryset=lambda request: get_page_content_types(include_base_page_type=False),
73
- widget=CheckboxSelectMultiple,
74
- )
75
71
  latest_revision_created_at = DateFromToRangeFilter(
76
72
  label=_("Date updated"),
77
73
  widget=DateRangePickerWidget,
@@ -119,6 +115,14 @@ class PageFilterSet(WagtailFilterSet):
119
115
  fields = [] # only needed for filters being generated automatically
120
116
 
121
117
 
118
+ class ExplorablePageFilterSet(PageFilterSet):
119
+ content_type = MultipleContentTypeFilter(
120
+ label=_("Page type"),
121
+ queryset=lambda request: get_page_content_types(include_base_page_type=False),
122
+ widget=CheckboxSelectMultiple,
123
+ )
124
+
125
+
122
126
  class IndexView(generic.IndexView):
123
127
  template_name = "wagtailadmin/pages/index.html"
124
128
  results_template_name = "wagtailadmin/pages/index_results.html"
@@ -137,7 +141,11 @@ class IndexView(generic.IndexView):
137
141
  table_class = PageTable
138
142
  table_classname = "listing full-width"
139
143
  filterset_class = PageFilterSet
140
- page_title = _("Exploring")
144
+ index_url_name = None
145
+ index_results_url_name = None
146
+ default_ordering = "-latest_revision_created_at"
147
+ model = Page
148
+ _show_breadcrumbs = True
141
149
 
142
150
  columns = [
143
151
  BulkActionsColumn("bulk_actions"),
@@ -153,54 +161,15 @@ class IndexView(generic.IndexView):
153
161
  sort_key="latest_revision_created_at",
154
162
  width="12%",
155
163
  ),
156
- Column(
157
- "type",
158
- label=_("Type"),
159
- accessor="page_type_display_name",
160
- sort_key="content_type",
161
- width="12%",
162
- ),
163
164
  PageStatusColumn(
164
165
  "status",
165
166
  label=_("Status"),
166
167
  sort_key="live",
167
168
  width="12%",
168
169
  ),
169
- NavigateToChildrenColumn("navigate", width="10%"),
170
170
  ]
171
171
 
172
- def get(self, request, parent_page_id=None):
173
- if parent_page_id:
174
- self.parent_page = get_object_or_404(
175
- Page.objects.all().prefetch_workflow_states(), id=parent_page_id
176
- )
177
- else:
178
- self.parent_page = Page.get_first_root_node()
179
-
180
- # This will always succeed because of the check performed by PermissionCheckedMixin.
181
- root_page = self.permission_policy.explorable_root_instance(request.user)
182
-
183
- # If this page isn't a descendant of the user's explorable root page,
184
- # then redirect to that explorable root page instead.
185
- if not (
186
- self.parent_page.pk == root_page.pk
187
- or self.parent_page.is_descendant_of(root_page)
188
- ):
189
- return redirect("wagtailadmin_explore", root_page.pk)
190
-
191
- self.parent_page = self.parent_page.specific
192
- self.scheduled_page = self.parent_page.get_scheduled_revision_as_object()
193
-
194
- if (
195
- getattr(settings, "WAGTAIL_I18N_ENABLED", False)
196
- and not self.parent_page.is_root()
197
- ):
198
- self.locale = self.parent_page.locale
199
- self.translations = self.get_translations()
200
- else:
201
- self.locale = None
202
- self.translations = []
203
-
172
+ def get(self, request):
204
173
  # Search
205
174
  self.query_string = None
206
175
  self.is_searching = False
@@ -214,30 +183,19 @@ class IndexView(generic.IndexView):
214
183
  if self.query_string:
215
184
  self.is_searching = True
216
185
 
217
- self.is_searching_whole_tree = bool(self.request.GET.get("search_all")) and (
218
- self.is_searching or self.is_filtering
219
- )
220
-
221
186
  return super().get(request)
222
187
 
223
188
  def get_valid_orderings(self):
224
- valid_orderings = [
225
- "title",
226
- "-title",
227
- "live",
228
- "-live",
229
- "latest_revision_created_at",
230
- "-latest_revision_created_at",
231
- ]
232
-
233
- if not self.is_searching:
234
- # ordering by page order is only available when not searching
235
- valid_orderings.append("ord")
189
+ valid_orderings = super().get_valid_orderings()
236
190
 
191
+ if self.is_searching:
237
192
  # ordering by content type not currently available when searching, due to
238
193
  # https://github.com/wagtail/wagtail/issues/6616
239
- valid_orderings.append("content_type")
240
- valid_orderings.append("-content_type")
194
+ try:
195
+ valid_orderings.remove("content_type")
196
+ valid_orderings.remove("-content_type")
197
+ except ValueError:
198
+ pass
241
199
 
242
200
  return valid_orderings
243
201
 
@@ -246,7 +204,7 @@ class IndexView(generic.IndexView):
246
204
  # default to ordering by relevance
247
205
  default_ordering = None
248
206
  else:
249
- default_ordering = self.parent_page.get_admin_default_ordering()
207
+ default_ordering = self.default_ordering
250
208
 
251
209
  ordering = self.request.GET.get("ordering", default_ordering)
252
210
  if ordering not in self.get_valid_orderings():
@@ -255,17 +213,16 @@ class IndexView(generic.IndexView):
255
213
  return ordering
256
214
 
257
215
  def get_base_queryset(self):
258
- if self.is_searching or self.is_filtering:
259
- if self.is_searching_whole_tree:
260
- pages = Page.objects.all()
261
- else:
262
- pages = self.parent_page.get_descendants()
263
- else:
264
- pages = self.parent_page.get_children()
216
+ pages = self.model.objects.filter(depth__gt=1)
217
+ pages = self._annotate_queryset(pages)
218
+ return pages
265
219
 
266
- pages = pages.prefetch_related(
267
- "content_type", "sites_rooted_here"
268
- ) & self.permission_policy.explorable_instances(self.request.user)
220
+ def _annotate_queryset(self, pages):
221
+ pages = pages.prefetch_related("content_type", "sites_rooted_here").filter(
222
+ pk__in=self.permission_policy.explorable_instances(
223
+ self.request.user
224
+ ).values_list("pk", flat=True)
225
+ )
269
226
 
270
227
  # We want specific page instances, but do not need streamfield values here
271
228
  pages = pages.defer_streamfields().specific()
@@ -309,12 +266,6 @@ class IndexView(generic.IndexView):
309
266
  return queryset
310
267
 
311
268
  def search_queryset(self, queryset):
312
- # allow hooks to modify queryset. This should happen as close as possible to the
313
- # final queryset, but (for backward compatibility) needs to be passed an actual queryset
314
- # rather than a search result object
315
- for hook in hooks.get_hooks("construct_explorer_page_queryset"):
316
- queryset = hook(self.parent_page, queryset, self.request)
317
-
318
269
  if self.is_searching:
319
270
  queryset = queryset.autocomplete(
320
271
  self.query_string, order_by_relevance=(not self.is_explicitly_ordered)
@@ -322,19 +273,125 @@ class IndexView(generic.IndexView):
322
273
 
323
274
  return queryset
324
275
 
325
- def get_paginate_by(self, queryset):
326
- if self.ordering == "ord":
327
- # Don't paginate if sorting by page order - all pages must be shown to
328
- # allow drag-and-drop reordering
329
- return None
276
+ def get_index_url(self):
277
+ return reverse(self.index_url_name)
278
+
279
+ def get_index_results_url(self):
280
+ return reverse(self.index_results_url_name)
281
+
282
+ def get_breadcrumbs_items(self):
283
+ return self.breadcrumbs_items + [{"url": "", "label": self.get_page_title()}]
284
+
285
+ def get_table_kwargs(self):
286
+ kwargs = super().get_table_kwargs()
287
+ kwargs["actions_next_url"] = self.index_url
288
+ return kwargs
289
+
290
+ def get_context_data(self, **kwargs):
291
+ context = super().get_context_data(**kwargs)
292
+
293
+ context.update(
294
+ {
295
+ "ordering": self.ordering,
296
+ "search_form": self.search_form,
297
+ "is_searching": self.is_searching,
298
+ }
299
+ )
300
+ return context
301
+
302
+
303
+ class ExplorableIndexView(IndexView):
304
+ """
305
+ A version of the page listing where the user is presented with a view of a specified parent page;
306
+ normally this will show the children of that page, but it may show results from the whole tree while
307
+ searching or filtering.
308
+ """
309
+
310
+ template_name = "wagtailadmin/pages/explorable_index.html"
311
+ index_url_name = "wagtailadmin_explore"
312
+ index_results_url_name = "wagtailadmin_explore_results"
313
+ page_title = _("Exploring")
314
+ filterset_class = ExplorablePageFilterSet
315
+
316
+ @classproperty
317
+ def columns(cls):
318
+ columns = super().columns.copy()
319
+ columns.insert(
320
+ 3,
321
+ Column(
322
+ "type",
323
+ label=_("Type"),
324
+ accessor="page_type_display_name",
325
+ sort_key="content_type",
326
+ width="12%",
327
+ ),
328
+ )
329
+ columns.append(NavigateToChildrenColumn("navigate", width="10%"))
330
+ return columns
331
+
332
+ def get(self, request, parent_page_id=None):
333
+ if parent_page_id:
334
+ self.parent_page = get_object_or_404(
335
+ Page.objects.all().prefetch_workflow_states(), id=parent_page_id
336
+ )
330
337
  else:
331
- return self.paginate_by
338
+ self.parent_page = Page.get_first_root_node()
339
+
340
+ # This will always succeed because of the check performed by PermissionCheckedMixin.
341
+ root_page = self.permission_policy.explorable_root_instance(request.user)
342
+
343
+ # If this page isn't a descendant of the user's explorable root page,
344
+ # then redirect to that explorable root page instead.
345
+ if not (
346
+ self.parent_page.pk == root_page.pk
347
+ or self.parent_page.is_descendant_of(root_page)
348
+ ):
349
+ return redirect(self.index_url_name, root_page.pk)
350
+
351
+ self.parent_page = self.parent_page.specific
352
+ self.scheduled_page = self.parent_page.get_scheduled_revision_as_object()
353
+
354
+ self.i18n_enabled = getattr(settings, "WAGTAIL_I18N_ENABLED", False)
355
+ if self.i18n_enabled and not self.parent_page.is_root():
356
+ self.locale = self.parent_page.locale
357
+ self.translations = self.get_translations()
358
+ else:
359
+ self.locale = None
360
+ self.translations = []
361
+
362
+ return super().get(request)
363
+
364
+ @cached_property
365
+ def is_searching_whole_tree(self):
366
+ return bool(self.request.GET.get("search_all")) and (
367
+ self.is_searching or self.is_filtering
368
+ )
369
+
370
+ def get_base_queryset(self):
371
+ if self.is_searching or self.is_filtering:
372
+ if self.is_searching_whole_tree:
373
+ pages = Page.objects.all()
374
+ else:
375
+ pages = self.parent_page.get_descendants()
376
+ else:
377
+ pages = self.parent_page.get_children()
378
+
379
+ pages = self._annotate_queryset(pages)
380
+ return pages
381
+
382
+ def search_queryset(self, queryset):
383
+ # allow hooks to modify queryset. This should happen as close as possible to the
384
+ # final queryset, but (for backward compatibility) needs to be passed an actual queryset
385
+ # rather than a search result object
386
+ for hook in hooks.get_hooks("construct_explorer_page_queryset"):
387
+ queryset = hook(self.parent_page, queryset, self.request)
388
+ return super().search_queryset(queryset)
332
389
 
333
390
  def get_index_url(self):
334
- return reverse("wagtailadmin_explore", args=[self.parent_page.id])
391
+ return reverse(self.index_url_name, args=[self.parent_page.id])
335
392
 
336
393
  def get_index_results_url(self):
337
- return reverse("wagtailadmin_explore_results", args=[self.parent_page.id])
394
+ return reverse(self.index_results_url_name, args=[self.parent_page.id])
338
395
 
339
396
  def get_history_url(self):
340
397
  permissions = self.parent_page.permissions_for_user(self.request.user)
@@ -346,7 +403,6 @@ class IndexView(generic.IndexView):
346
403
  kwargs["use_row_ordering_attributes"] = self.show_ordering_column
347
404
  kwargs["parent_page"] = self.parent_page
348
405
  kwargs["show_locale_labels"] = self.i18n_enabled and self.parent_page.is_root()
349
- kwargs["actions_next_url"] = self.index_url
350
406
 
351
407
  if self.show_ordering_column:
352
408
  kwargs["caption"] = _(
@@ -367,6 +423,36 @@ class IndexView(generic.IndexView):
367
423
  }
368
424
  return kwargs
369
425
 
426
+ def get_valid_orderings(self):
427
+ valid_orderings = super().get_valid_orderings()
428
+
429
+ if not self.is_searching:
430
+ # ordering by page order is only available when not searching
431
+ valid_orderings.append("ord")
432
+
433
+ return valid_orderings
434
+
435
+ def get_ordering(self):
436
+ if self.is_searching and not self.is_explicitly_ordered:
437
+ # default to ordering by relevance
438
+ default_ordering = None
439
+ else:
440
+ default_ordering = self.parent_page.get_admin_default_ordering()
441
+
442
+ ordering = self.request.GET.get("ordering", default_ordering)
443
+ if ordering not in self.get_valid_orderings():
444
+ ordering = default_ordering
445
+
446
+ return ordering
447
+
448
+ def get_paginate_by(self, queryset):
449
+ if self.ordering == "ord":
450
+ # Don't paginate if sorting by page order - all pages must be shown to
451
+ # allow drag-and-drop reordering
452
+ return None
453
+ else:
454
+ return self.paginate_by
455
+
370
456
  def get_page_subtitle(self):
371
457
  return self.parent_page.get_admin_display_title()
372
458
 
@@ -375,8 +461,6 @@ class IndexView(generic.IndexView):
375
461
  if self.show_ordering_column:
376
462
  self.columns = self.columns.copy()
377
463
  self.columns[0] = OrderingColumn("ordering", width="80px", sort_key="ord")
378
- self.i18n_enabled = getattr(settings, "WAGTAIL_I18N_ENABLED", False)
379
-
380
464
  context = super().get_context_data(**kwargs)
381
465
 
382
466
  if self.is_searching:
@@ -397,10 +481,7 @@ class IndexView(generic.IndexView):
397
481
  context.update(
398
482
  {
399
483
  "parent_page": self.parent_page,
400
- "ordering": self.ordering,
401
484
  "history_url": self.get_history_url(),
402
- "search_form": self.search_form,
403
- "is_searching": self.is_searching,
404
485
  "is_searching_whole_tree": self.is_searching_whole_tree,
405
486
  }
406
487
  )
@@ -412,17 +493,6 @@ class IndexView(generic.IndexView):
412
493
 
413
494
  return context
414
495
 
415
- def get_translations(self):
416
- return [
417
- {
418
- "locale": translation.locale,
419
- "url": reverse("wagtailadmin_explore", args=[translation.id]),
420
- }
421
- for translation in self.parent_page.get_translations()
422
- .only("id", "locale")
423
- .select_related("locale")
424
- ]
425
-
426
496
  def get_side_panels(self):
427
497
  # Don't show side panels on the root page
428
498
  if self.parent_page.is_root():
@@ -440,3 +510,14 @@ class IndexView(generic.IndexView):
440
510
  ),
441
511
  ]
442
512
  return MediaContainer(side_panels)
513
+
514
+ def get_translations(self):
515
+ return [
516
+ {
517
+ "locale": translation.locale,
518
+ "url": reverse(self.index_url_name, args=[translation.id]),
519
+ }
520
+ for translation in self.parent_page.get_translations()
521
+ .only("id", "locale")
522
+ .select_related("locale")
523
+ ]
@@ -15,6 +15,9 @@ from wagtail.admin.ui.tables.pages import (
15
15
  )
16
16
  from wagtail.admin.views import generic
17
17
  from wagtail.admin.views.generic.base import BaseListingView
18
+ from wagtail.admin.views.pages.utils import (
19
+ GenericPageBreadcrumbsMixin,
20
+ )
18
21
  from wagtail.models import Page
19
22
 
20
23
 
@@ -71,10 +74,12 @@ class ContentTypeUseView(BaseListingView):
71
74
  return context
72
75
 
73
76
 
74
- class UsageView(generic.UsageView):
77
+ class UsageView(GenericPageBreadcrumbsMixin, generic.UsageView):
75
78
  model = Page
76
79
  pk_url_kwarg = "page_id"
77
80
  header_icon = "doc-empty-inverse"
81
+ usage_url_name = "wagtailadmin_pages:usage"
82
+ edit_url_name = "wagtailadmin_pages:edit"
78
83
 
79
84
  def dispatch(self, request, *args, **kwargs):
80
85
  if not self.object.permissions_for_user(request.user).can_edit():
@@ -1,2 +1,71 @@
1
+ from django.urls import reverse
2
+ from django.utils.functional import cached_property
3
+
1
4
  # Retain backwards compatibility for imports
2
- from wagtail.admin.utils import get_valid_next_url_from_request # noqa: F401
5
+ from wagtail.admin.utils import ( # noqa: F401
6
+ get_latest_str,
7
+ get_valid_next_url_from_request,
8
+ )
9
+ from wagtail.permissions import page_permission_policy
10
+
11
+
12
+ def get_breadcrumbs_items_for_page(
13
+ page,
14
+ user,
15
+ url_name="wagtailadmin_explore",
16
+ root_url_name="wagtailadmin_explore_root",
17
+ include_self=True,
18
+ querystring_value="",
19
+ ):
20
+ # find the closest common ancestor of the pages that this user has direct explore permission
21
+ # (i.e. add/edit/publish/lock) over; this will be the root of the breadcrumb
22
+ cca = page_permission_policy.explorable_root_instance(user)
23
+ if not cca:
24
+ return []
25
+
26
+ pages = (
27
+ page.get_ancestors(inclusive=include_self)
28
+ .descendant_of(cca, inclusive=True)
29
+ .specific(defer=True)
30
+ )
31
+
32
+ items = []
33
+ for page in pages:
34
+ if page.is_root() and root_url_name:
35
+ url = reverse(root_url_name)
36
+ else:
37
+ url = reverse(url_name, args=(page.id,))
38
+ items.append({"url": url + querystring_value, "label": get_latest_str(page)})
39
+
40
+ return items
41
+
42
+
43
+ class GenericPageBreadcrumbsMixin:
44
+ """
45
+ A mixin that allows a view for pages that extends a generic view to combine
46
+ the page explorer breadcrumbs with the generic view's breadcrumbs.
47
+
48
+ This is done by generating the explorer breadcrumbs items for the page as a
49
+ normalised breadcrumbs items list, and then concatenating that with the last
50
+ item of the generic view's generated breadcrumbs items.
51
+ """
52
+
53
+ _show_breadcrumbs = True
54
+ breadcrumbs_items_to_take = 1
55
+
56
+ @cached_property
57
+ def breadcrumbs_items(self):
58
+ return get_breadcrumbs_items_for_page(self.object, self.request.user)
59
+
60
+ def get_breadcrumbs_items(self):
61
+ # The generic view tends to generate breadcrumbs with items such as
62
+ # IndexView > EditView > CurrentView,
63
+ # but we don't want that because we want the preceding items to be links
64
+ # to the explore view of the page's ancestors for consistency with how
65
+ # page breadcrumbs have always worked. So we only take the last N items,
66
+ # which in most cases is the final item that links to the current view.
67
+ # However, this can be customised in the case of generic views that are
68
+ # nested inside another generic view.
69
+ return self.breadcrumbs_items + [
70
+ super().get_breadcrumbs_items()[-self.breadcrumbs_items_to_take]
71
+ ]