wagtail 6.0.1__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 (512) 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/ca/LC_MESSAGES/django.mo +0 -0
  9. wagtail/admin/locale/ca/LC_MESSAGES/django.po +122 -0
  10. wagtail/admin/locale/de/LC_MESSAGES/django.mo +0 -0
  11. wagtail/admin/locale/de/LC_MESSAGES/django.po +5 -5
  12. wagtail/admin/locale/en/LC_MESSAGES/django.po +474 -385
  13. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +3 -3
  14. wagtail/admin/locale/es/LC_MESSAGES/django.mo +0 -0
  15. wagtail/admin/locale/es/LC_MESSAGES/django.po +6 -6
  16. wagtail/admin/locale/fr/LC_MESSAGES/django.mo +0 -0
  17. wagtail/admin/locale/fr/LC_MESSAGES/django.po +70 -3
  18. wagtail/admin/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  19. wagtail/admin/locale/he_IL/LC_MESSAGES/django.po +2 -6
  20. wagtail/admin/locale/he_IL/LC_MESSAGES/djangojs.mo +0 -0
  21. wagtail/admin/locale/he_IL/LC_MESSAGES/djangojs.po +2 -2
  22. wagtail/admin/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  23. wagtail/admin/locale/hr_HR/LC_MESSAGES/django.po +4 -0
  24. wagtail/admin/locale/hu/LC_MESSAGES/django.mo +0 -0
  25. wagtail/admin/locale/hu/LC_MESSAGES/django.po +142 -2
  26. wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
  27. wagtail/admin/locale/it/LC_MESSAGES/django.po +80 -8
  28. wagtail/admin/locale/it/LC_MESSAGES/djangojs.mo +0 -0
  29. wagtail/admin/locale/it/LC_MESSAGES/djangojs.po +14 -2
  30. wagtail/admin/locale/lv/LC_MESSAGES/django.mo +0 -0
  31. wagtail/admin/locale/lv/LC_MESSAGES/django.po +154 -1
  32. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  33. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.po +73 -2
  34. wagtail/admin/locale/ro/LC_MESSAGES/django.mo +0 -0
  35. wagtail/admin/locale/ro/LC_MESSAGES/django.po +3 -3
  36. wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
  37. wagtail/admin/locale/sl/LC_MESSAGES/django.po +145 -2
  38. wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
  39. wagtail/admin/locale/sv/LC_MESSAGES/django.po +77 -3
  40. wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
  41. wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.po +17 -1
  42. wagtail/admin/panels/comment_panel.py +1 -1
  43. wagtail/admin/panels/field_panel.py +1 -1
  44. wagtail/admin/rich_text/converters/editor_html.py +3 -1
  45. wagtail/admin/rich_text/editors/draftail/__init__.py +28 -2
  46. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  47. wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
  48. wagtail/admin/static/wagtailadmin/images/favicon.ico +0 -0
  49. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  50. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  51. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  52. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  53. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  54. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  55. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -1
  56. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  57. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  58. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +1 -1
  59. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  60. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  61. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  62. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  63. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  64. wagtail/admin/static/wagtailadmin/js/preview-panel.js +1 -1
  65. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  66. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  67. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  68. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  69. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  70. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  71. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  72. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  73. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  74. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +4 -4
  75. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  76. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  77. wagtail/admin/staticfiles.py +1 -0
  78. wagtail/admin/templates/wagtailadmin/admin_base.html +1 -0
  79. wagtail/admin/templates/wagtailadmin/base.html +1 -0
  80. wagtail/admin/templates/wagtailadmin/collection_privacy/set_privacy.html +3 -1
  81. wagtail/admin/templates/wagtailadmin/collections/edit.html +0 -1
  82. wagtail/admin/templates/wagtailadmin/collections/index_results.html +10 -0
  83. wagtail/admin/templates/wagtailadmin/generic/base.html +1 -9
  84. wagtail/admin/templates/wagtailadmin/generic/form.html +4 -2
  85. wagtail/admin/templates/wagtailadmin/generic/history/action_cell.html +27 -0
  86. wagtail/admin/templates/wagtailadmin/generic/index_results.html +8 -0
  87. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +3 -4
  88. wagtail/admin/templates/wagtailadmin/icons/keyboard.svg +1 -0
  89. wagtail/admin/templates/wagtailadmin/page_privacy/set_privacy.html +3 -1
  90. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -15
  91. wagtail/admin/templates/wagtailadmin/pages/action_menu/save_draft.html +3 -1
  92. wagtail/admin/templates/wagtailadmin/pages/choose_parent.html +17 -0
  93. wagtail/admin/templates/wagtailadmin/pages/explorable_index.html +8 -0
  94. wagtail/admin/templates/wagtailadmin/pages/history.html +1 -61
  95. wagtail/admin/templates/wagtailadmin/pages/index.html +1 -5
  96. wagtail/admin/templates/wagtailadmin/pages/listing/_locked_indicator.html +2 -2
  97. wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_column_header.html +25 -27
  98. wagtail/admin/templates/wagtailadmin/pages/page_listing_header.html +2 -1
  99. wagtail/admin/templates/wagtailadmin/panels/multi_field_panel_child.html +1 -1
  100. wagtail/admin/templates/wagtailadmin/panels/publishing/schedule_publishing_panel.html +3 -1
  101. wagtail/admin/templates/wagtailadmin/panels/tabbed_interface.html +1 -1
  102. wagtail/admin/templates/wagtailadmin/shared/active_filters.html +2 -1
  103. wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +8 -0
  104. wagtail/admin/templates/wagtailadmin/shared/forms/single_checkbox.html +1 -1
  105. wagtail/admin/templates/wagtailadmin/shared/headers/page_edit_header.html +1 -1
  106. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +21 -9
  107. wagtail/admin/templates/wagtailadmin/shared/human_readable_date.html +1 -1
  108. wagtail/admin/templates/wagtailadmin/shared/keyboard_shortcuts_dialog.html +29 -0
  109. wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +2 -1
  110. wagtail/admin/templates/wagtailadmin/skeleton.html +2 -1
  111. wagtail/admin/templates/wagtailadmin/tables/related_objects_cell.html +9 -0
  112. wagtail/admin/templates/wagtailadmin/tables/title_cell.html +9 -7
  113. wagtail/admin/templates/wagtailadmin/widgets/draftail_rich_text_area.html +1 -1
  114. wagtail/admin/templates/wagtailadmin/workflows/create.html +6 -23
  115. wagtail/admin/templates/wagtailadmin/workflows/create_task.html +6 -15
  116. wagtail/admin/templates/wagtailadmin/workflows/edit.html +6 -23
  117. wagtail/admin/templates/wagtailadmin/workflows/edit_task.html +6 -13
  118. wagtail/admin/templates/wagtailadmin/workflows/includes/task_usage_cell.html +4 -4
  119. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_tasks_cell.html +18 -0
  120. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_title_cell.html +7 -0
  121. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_used_by_cell.html +25 -0
  122. wagtail/admin/templates/wagtailadmin/workflows/index.html +0 -99
  123. wagtail/admin/templates/wagtailadmin/workflows/index_results.html +10 -0
  124. wagtail/admin/templates/wagtailadmin/workflows/task_index.html +0 -30
  125. wagtail/admin/templates/wagtailadmin/workflows/task_index_results.html +10 -0
  126. wagtail/admin/templates/wagtailadmin/workflows/usage.html +1 -1
  127. wagtail/admin/templatetags/wagtailadmin_tags.py +116 -39
  128. wagtail/admin/tests/api/test_pages.py +26 -10
  129. wagtail/admin/tests/pages/test_create_page.py +10 -4
  130. wagtail/admin/tests/pages/test_custom_listing.py +37 -0
  131. wagtail/admin/tests/pages/test_edit_page.py +6 -6
  132. wagtail/admin/tests/pages/test_explorer_view.py +19 -18
  133. wagtail/admin/tests/pages/test_move_page.py +1 -1
  134. wagtail/admin/tests/pages/test_page_usage.py +50 -2
  135. wagtail/admin/tests/pages/test_parent_page_chooser_view.py +119 -0
  136. wagtail/admin/tests/pages/test_preview.py +18 -4
  137. wagtail/admin/tests/test_account_management.py +20 -1
  138. wagtail/admin/tests/test_audit_log.py +172 -5
  139. wagtail/admin/tests/test_checks.py +92 -0
  140. wagtail/admin/tests/test_collections_views.py +19 -5
  141. wagtail/admin/tests/test_compare.py +6 -6
  142. wagtail/admin/tests/test_dashboard.py +404 -0
  143. wagtail/admin/tests/test_dbwhitelister.py +4 -5
  144. wagtail/admin/tests/test_edit_handlers.py +2 -2
  145. wagtail/admin/tests/test_keyboard_shortcuts.py +84 -0
  146. wagtail/admin/tests/test_page_chooser.py +31 -18
  147. wagtail/admin/tests/test_privacy.py +36 -2
  148. wagtail/admin/tests/test_rich_text.py +168 -23
  149. wagtail/admin/tests/test_templatetags.py +411 -43
  150. wagtail/admin/tests/test_views.py +4 -2
  151. wagtail/admin/tests/test_workflows.py +531 -9
  152. wagtail/admin/tests/tests.py +3 -1
  153. wagtail/admin/tests/ui/test_tables.py +48 -1
  154. wagtail/admin/tests/viewsets/test_model_viewset.py +130 -23
  155. wagtail/admin/ui/side_panels.py +3 -1
  156. wagtail/admin/ui/tables/__init__.py +13 -1
  157. wagtail/admin/ui/tables/pages.py +17 -6
  158. wagtail/admin/urls/__init__.py +8 -3
  159. wagtail/admin/urls/pages.py +5 -0
  160. wagtail/admin/urls/workflows.py +10 -0
  161. wagtail/admin/views/chooser.py +20 -24
  162. wagtail/admin/views/collections.py +17 -1
  163. wagtail/admin/views/generic/base.py +34 -4
  164. wagtail/admin/views/generic/history.py +220 -51
  165. wagtail/admin/views/generic/mixins.py +7 -4
  166. wagtail/admin/views/generic/models.py +54 -47
  167. wagtail/admin/views/generic/multiple_upload.py +17 -8
  168. wagtail/admin/views/generic/usage.py +17 -11
  169. wagtail/admin/views/home.py +15 -12
  170. wagtail/admin/views/mixins.py +30 -0
  171. wagtail/admin/views/pages/choose_parent.py +73 -0
  172. wagtail/admin/views/pages/history.py +54 -66
  173. wagtail/admin/views/pages/listing.py +187 -106
  174. wagtail/admin/views/pages/usage.py +6 -1
  175. wagtail/admin/views/pages/utils.py +70 -1
  176. wagtail/admin/views/workflows.py +150 -21
  177. wagtail/admin/viewsets/model.py +2 -2
  178. wagtail/admin/viewsets/pages.py +77 -0
  179. wagtail/admin/wagtail_hooks.py +40 -2
  180. wagtail/admin/widgets/button.py +10 -10
  181. wagtail/api/v2/filters.py +1 -1
  182. wagtail/api/v2/tests/test_pages.py +1 -1
  183. wagtail/blocks/base.py +18 -9
  184. wagtail/blocks/field_block.py +9 -7
  185. wagtail/blocks/list_block.py +16 -6
  186. wagtail/blocks/static_block.py +3 -0
  187. wagtail/blocks/stream_block.py +58 -23
  188. wagtail/blocks/struct_block.py +15 -9
  189. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +39 -47
  190. wagtail/contrib/forms/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  191. wagtail/contrib/forms/locale/he_IL/LC_MESSAGES/django.po +2 -2
  192. wagtail/contrib/forms/models.py +5 -5
  193. wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +44 -33
  194. wagtail/contrib/forms/templates/wagtailforms/submissions_index.html +2 -63
  195. wagtail/contrib/forms/tests/test_models.py +26 -0
  196. wagtail/contrib/forms/urls.py +6 -0
  197. wagtail/contrib/forms/views.py +52 -49
  198. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.mo +0 -0
  199. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.po +3 -3
  200. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +34 -42
  201. wagtail/contrib/redirects/locale/fr/LC_MESSAGES/django.po +2 -2
  202. wagtail/contrib/redirects/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  203. wagtail/contrib/redirects/locale/he_IL/LC_MESSAGES/django.po +2 -2
  204. wagtail/contrib/redirects/signal_handlers.py +1 -1
  205. wagtail/contrib/redirects/templates/wagtailredirects/index.html +1 -36
  206. wagtail/contrib/redirects/templates/wagtailredirects/index_results.html +18 -0
  207. wagtail/contrib/redirects/templates/wagtailredirects/redirect_target_cell.html +8 -0
  208. wagtail/contrib/redirects/tests/test_import_command.py +1 -1
  209. wagtail/contrib/redirects/tests/test_redirects.py +79 -8
  210. wagtail/contrib/redirects/urls.py +2 -1
  211. wagtail/contrib/redirects/views.py +85 -55
  212. wagtail/contrib/search_promotions/admin_urls.py +2 -1
  213. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +41 -64
  214. wagtail/contrib/search_promotions/locale/fr/LC_MESSAGES/django.po +2 -2
  215. wagtail/contrib/search_promotions/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  216. wagtail/contrib/search_promotions/locale/he_IL/LC_MESSAGES/django.po +2 -2
  217. wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  218. wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.po +41 -2
  219. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.mo +0 -0
  220. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +9 -3
  221. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index.html +1 -16
  222. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index_results.html +11 -0
  223. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/list.html +0 -51
  224. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/results.html +3 -16
  225. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/search_promotion_column.html +15 -0
  226. wagtail/contrib/search_promotions/tests.py +122 -9
  227. wagtail/contrib/search_promotions/views.py +66 -65
  228. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +3 -3
  229. wagtail/contrib/settings/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  230. wagtail/contrib/settings/locale/he_IL/LC_MESSAGES/django.po +2 -2
  231. wagtail/contrib/settings/locale/tr/LC_MESSAGES/django.mo +0 -0
  232. wagtail/contrib/settings/locale/tr/LC_MESSAGES/django.po +6 -2
  233. wagtail/contrib/settings/registry.py +10 -5
  234. wagtail/contrib/settings/tests/generic/test_admin.py +9 -0
  235. wagtail/contrib/settings/tests/site_specific/test_admin.py +10 -1
  236. wagtail/contrib/settings/tests/site_specific/test_model.py +3 -3
  237. wagtail/contrib/settings/tests/site_specific/test_templates.py +1 -1
  238. wagtail/contrib/settings/views.py +3 -1
  239. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  240. wagtail/contrib/simple_translation/tests/test_wagtail_hooks.py +2 -2
  241. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  242. wagtail/contrib/styleguide/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  243. wagtail/contrib/styleguide/locale/he_IL/LC_MESSAGES/django.po +2 -2
  244. wagtail/contrib/table_block/blocks.py +2 -2
  245. wagtail/contrib/table_block/locale/ca/LC_MESSAGES/django.mo +0 -0
  246. wagtail/contrib/table_block/locale/ca/LC_MESSAGES/django.po +27 -2
  247. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  248. wagtail/contrib/table_block/locale/hu/LC_MESSAGES/django.mo +0 -0
  249. wagtail/contrib/table_block/locale/hu/LC_MESSAGES/django.po +27 -2
  250. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.mo +0 -0
  251. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.po +27 -2
  252. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  253. wagtail/contrib/table_block/tests.py +6 -0
  254. wagtail/contrib/typed_table_block/locale/ca/LC_MESSAGES/django.mo +0 -0
  255. wagtail/contrib/typed_table_block/locale/ca/LC_MESSAGES/django.po +12 -2
  256. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +1 -1
  257. wagtail/contrib/typed_table_block/locale/hu/LC_MESSAGES/django.mo +0 -0
  258. wagtail/contrib/typed_table_block/locale/hu/LC_MESSAGES/django.po +12 -2
  259. wagtail/contrib/typed_table_block/locale/it/LC_MESSAGES/django.mo +0 -0
  260. wagtail/contrib/typed_table_block/locale/it/LC_MESSAGES/django.po +12 -2
  261. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  262. wagtail/coreutils.py +3 -2
  263. wagtail/documents/admin_urls.py +2 -2
  264. wagtail/documents/locale/en/LC_MESSAGES/django.po +22 -22
  265. wagtail/documents/locale/fr/LC_MESSAGES/django.po +2 -2
  266. wagtail/documents/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  267. wagtail/documents/locale/he_IL/LC_MESSAGES/django.po +2 -2
  268. wagtail/documents/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  269. wagtail/documents/locale/hr_HR/LC_MESSAGES/django.po +19 -2
  270. wagtail/documents/locale/hu/LC_MESSAGES/django.mo +0 -0
  271. wagtail/documents/locale/hu/LC_MESSAGES/django.po +16 -2
  272. wagtail/documents/locale/it/LC_MESSAGES/django.mo +0 -0
  273. wagtail/documents/locale/it/LC_MESSAGES/django.po +19 -2
  274. wagtail/documents/migrations/0013_delete_uploadeddocument.py +16 -0
  275. wagtail/documents/models.py +1 -20
  276. wagtail/documents/rich_text/__init__.py +11 -7
  277. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  278. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  279. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  280. wagtail/documents/templates/wagtaildocs/documents/index.html +0 -16
  281. wagtail/documents/tests/test_admin_views.py +155 -23
  282. wagtail/documents/tests/test_collection_privacy.py +55 -1
  283. wagtail/documents/tests/test_rich_text.py +14 -0
  284. wagtail/documents/views/documents.py +25 -22
  285. wagtail/documents/views/multiple.py +6 -7
  286. wagtail/documents/views/serve.py +16 -1
  287. wagtail/documents/wagtail_hooks.py +20 -15
  288. wagtail/embeds/blocks.py +5 -0
  289. wagtail/embeds/locale/en/LC_MESSAGES/django.po +2 -2
  290. wagtail/embeds/locale/fr/LC_MESSAGES/django.po +2 -2
  291. wagtail/embeds/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  292. wagtail/embeds/locale/he_IL/LC_MESSAGES/django.po +2 -2
  293. wagtail/embeds/rich_text/__init__.py +1 -1
  294. wagtail/embeds/tests/test_rich_text.py +14 -0
  295. wagtail/embeds/wagtail_hooks.py +4 -14
  296. wagtail/fields.py +3 -48
  297. wagtail/images/admin_urls.py +2 -2
  298. wagtail/images/check_files/wagtail.jpg +0 -0
  299. wagtail/images/check_files/wagtail.png +0 -0
  300. wagtail/images/fields.py +2 -0
  301. wagtail/images/image_operations.py +1 -1
  302. wagtail/images/locale/ca/LC_MESSAGES/django.mo +0 -0
  303. wagtail/images/locale/ca/LC_MESSAGES/django.po +12 -0
  304. wagtail/images/locale/en/LC_MESSAGES/django.po +33 -45
  305. wagtail/images/locale/fr/LC_MESSAGES/django.po +2 -2
  306. wagtail/images/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  307. wagtail/images/locale/he_IL/LC_MESSAGES/django.po +2 -2
  308. wagtail/images/locale/hu/LC_MESSAGES/django.mo +0 -0
  309. wagtail/images/locale/hu/LC_MESSAGES/django.po +28 -2
  310. wagtail/images/locale/it/LC_MESSAGES/django.mo +0 -0
  311. wagtail/images/locale/it/LC_MESSAGES/django.po +14 -2
  312. wagtail/images/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  313. wagtail/images/locale/pt_PT/LC_MESSAGES/django.po +4 -0
  314. wagtail/images/migrations/0026_delete_uploadedimage.py +16 -0
  315. wagtail/images/models.py +49 -43
  316. wagtail/images/rich_text/__init__.py +18 -8
  317. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  318. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  319. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  320. wagtail/images/templates/wagtailimages/images/image_listing_header.html +6 -0
  321. wagtail/images/templates/wagtailimages/images/index.html +11 -51
  322. wagtail/images/tests/test_admin_views.py +119 -62
  323. wagtail/images/tests/test_image_operations.py +10 -0
  324. wagtail/images/tests/test_models.py +35 -33
  325. wagtail/images/tests/test_rich_text.py +14 -0
  326. wagtail/images/tests/utils.py +1 -1
  327. wagtail/images/views/images.py +35 -64
  328. wagtail/images/views/multiple.py +6 -7
  329. wagtail/images/wagtail_hooks.py +4 -14
  330. wagtail/locale/en/LC_MESSAGES/django.po +150 -136
  331. wagtail/locale/es/LC_MESSAGES/django.mo +0 -0
  332. wagtail/locale/es/LC_MESSAGES/django.po +3 -2
  333. wagtail/locale/fr/LC_MESSAGES/django.po +2 -2
  334. wagtail/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  335. wagtail/locale/he_IL/LC_MESSAGES/django.po +2 -2
  336. wagtail/locale/it/LC_MESSAGES/django.mo +0 -0
  337. wagtail/locale/it/LC_MESSAGES/django.po +5 -5
  338. wagtail/locale/sl/LC_MESSAGES/django.mo +0 -0
  339. wagtail/locale/sl/LC_MESSAGES/django.po +27 -2
  340. wagtail/locales/locale/ar/LC_MESSAGES/django.po +1 -1
  341. wagtail/locales/locale/be/LC_MESSAGES/django.po +1 -1
  342. wagtail/locales/locale/bg/LC_MESSAGES/django.po +1 -1
  343. wagtail/locales/locale/ca/LC_MESSAGES/django.po +1 -1
  344. wagtail/locales/locale/cs/LC_MESSAGES/django.po +1 -1
  345. wagtail/locales/locale/cy/LC_MESSAGES/django.po +1 -1
  346. wagtail/locales/locale/da/LC_MESSAGES/django.po +1 -1
  347. wagtail/locales/locale/de/LC_MESSAGES/django.po +1 -1
  348. wagtail/locales/locale/el/LC_MESSAGES/django.po +1 -1
  349. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  350. wagtail/locales/locale/es/LC_MESSAGES/django.po +1 -1
  351. wagtail/locales/locale/et/LC_MESSAGES/django.po +2 -2
  352. wagtail/locales/locale/fa/LC_MESSAGES/django.po +1 -1
  353. wagtail/locales/locale/fi/LC_MESSAGES/django.po +1 -1
  354. wagtail/locales/locale/fr/LC_MESSAGES/django.po +1 -1
  355. wagtail/locales/locale/gl/LC_MESSAGES/django.po +1 -1
  356. wagtail/locales/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  357. wagtail/locales/locale/he_IL/LC_MESSAGES/django.po +3 -3
  358. wagtail/locales/locale/hr_HR/LC_MESSAGES/django.po +1 -1
  359. wagtail/locales/locale/hu/LC_MESSAGES/django.po +1 -1
  360. wagtail/locales/locale/id_ID/LC_MESSAGES/django.po +1 -1
  361. wagtail/locales/locale/is_IS/LC_MESSAGES/django.po +1 -1
  362. wagtail/locales/locale/it/LC_MESSAGES/django.po +1 -1
  363. wagtail/locales/locale/ja/LC_MESSAGES/django.po +1 -1
  364. wagtail/locales/locale/ko/LC_MESSAGES/django.po +1 -1
  365. wagtail/locales/locale/lt/LC_MESSAGES/django.po +1 -1
  366. wagtail/locales/locale/lv/LC_MESSAGES/django.po +1 -1
  367. wagtail/locales/locale/mi/LC_MESSAGES/django.po +1 -1
  368. wagtail/locales/locale/mn/LC_MESSAGES/django.po +1 -1
  369. wagtail/locales/locale/my/LC_MESSAGES/django.po +1 -1
  370. wagtail/locales/locale/nb/LC_MESSAGES/django.po +1 -1
  371. wagtail/locales/locale/nl/LC_MESSAGES/django.po +1 -1
  372. wagtail/locales/locale/pl/LC_MESSAGES/django.po +1 -1
  373. wagtail/locales/locale/pt_BR/LC_MESSAGES/django.po +1 -1
  374. wagtail/locales/locale/pt_PT/LC_MESSAGES/django.po +1 -1
  375. wagtail/locales/locale/ro/LC_MESSAGES/django.po +1 -1
  376. wagtail/locales/locale/ru/LC_MESSAGES/django.po +1 -1
  377. wagtail/locales/locale/sk_SK/LC_MESSAGES/django.po +1 -1
  378. wagtail/locales/locale/sl/LC_MESSAGES/django.po +1 -1
  379. wagtail/locales/locale/sv/LC_MESSAGES/django.po +1 -1
  380. wagtail/locales/locale/tet/LC_MESSAGES/django.po +1 -1
  381. wagtail/locales/locale/th/LC_MESSAGES/django.po +1 -1
  382. wagtail/locales/locale/tr/LC_MESSAGES/django.po +1 -1
  383. wagtail/locales/locale/tr_TR/LC_MESSAGES/django.po +1 -1
  384. wagtail/locales/locale/uk/LC_MESSAGES/django.po +1 -1
  385. wagtail/locales/locale/vi/LC_MESSAGES/django.po +1 -1
  386. wagtail/locales/locale/zh/LC_MESSAGES/django.po +1 -1
  387. wagtail/locales/locale/zh_Hans/LC_MESSAGES/django.po +1 -1
  388. wagtail/locales/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
  389. wagtail/locales/tests.py +18 -3
  390. wagtail/locales/views.py +0 -1
  391. wagtail/management/commands/rebuild_references_index.py +3 -1
  392. wagtail/migrations/0092_alter_collectionviewrestriction_password_and_more.py +33 -0
  393. wagtail/migrations/0093_uploadedfile.py +53 -0
  394. wagtail/models/__init__.py +147 -32
  395. wagtail/models/i18n.py +1 -1
  396. wagtail/models/{collections.py → media.py} +33 -2
  397. wagtail/models/reference_index.py +1 -1
  398. wagtail/models/view_restrictions.py +10 -3
  399. wagtail/project_template/project_name/settings/base.py +6 -0
  400. wagtail/project_template/requirements.txt +1 -1
  401. wagtail/rich_text/__init__.py +25 -8
  402. wagtail/rich_text/pages.py +19 -8
  403. wagtail/rich_text/rewriters.py +140 -68
  404. wagtail/search/backends/database/mysql/mysql.py +3 -3
  405. wagtail/search/backends/database/postgres/postgres.py +3 -3
  406. wagtail/search/backends/database/sqlite/sqlite.py +2 -2
  407. wagtail/search/backends/elasticsearch7.py +4 -0
  408. wagtail/search/locale/en/LC_MESSAGES/django.po +3 -3
  409. wagtail/search/tests/test_postgres_backend.py +50 -0
  410. wagtail/sites/locale/en/LC_MESSAGES/django.po +8 -8
  411. wagtail/sites/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  412. wagtail/sites/locale/he_IL/LC_MESSAGES/django.po +2 -2
  413. wagtail/sites/locale/ro/LC_MESSAGES/django.mo +0 -0
  414. wagtail/sites/locale/ro/LC_MESSAGES/django.po +3 -2
  415. wagtail/sites/tests.py +35 -9
  416. wagtail/sites/views.py +3 -1
  417. wagtail/snippets/locale/de/LC_MESSAGES/django.mo +0 -0
  418. wagtail/snippets/locale/de/LC_MESSAGES/django.po +7 -8
  419. wagtail/snippets/locale/en/LC_MESSAGES/django.po +16 -56
  420. wagtail/snippets/locale/fr/LC_MESSAGES/django.po +2 -2
  421. wagtail/snippets/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  422. wagtail/snippets/locale/he_IL/LC_MESSAGES/django.po +2 -2
  423. wagtail/snippets/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  424. wagtail/snippets/locale/hr_HR/LC_MESSAGES/django.po +6 -2
  425. wagtail/snippets/locale/lv/LC_MESSAGES/django.mo +0 -0
  426. wagtail/snippets/locale/lv/LC_MESSAGES/django.po +12 -0
  427. wagtail/snippets/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
  428. wagtail/snippets/locale/zh_Hant/LC_MESSAGES/django.po +4 -0
  429. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  430. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  431. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/publish.html +3 -1
  432. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/save.html +3 -1
  433. wagtail/snippets/templates/wagtailsnippets/snippets/create.html +2 -3
  434. wagtail/snippets/templates/wagtailsnippets/snippets/edit.html +2 -3
  435. wagtail/snippets/tests/test_preview.py +13 -2
  436. wagtail/snippets/tests/test_snippets.py +41 -16
  437. wagtail/snippets/tests/test_viewset.py +95 -18
  438. wagtail/snippets/tests/test_workflows.py +12 -0
  439. wagtail/snippets/views/snippets.py +1 -40
  440. wagtail/templatetags/wagtailcore_tags.py +1 -1
  441. wagtail/test/demosite/models.py +1 -1
  442. wagtail/test/middleware.py +14 -1
  443. wagtail/test/testapp/fixtures/test.json +20 -0
  444. wagtail/test/testapp/migrations/0001_squashed_0073_revisablechildmodel_secret_text.py +8 -8
  445. wagtail/test/testapp/migrations/0023_snippetchoosermodel_full_featured.py +1 -0
  446. wagtail/test/testapp/migrations/0034_custompermissionmodel.py +44 -0
  447. wagtail/test/testapp/migrations/0035_modelwithcustommanager.py +30 -0
  448. wagtail/test/testapp/migrations/0036_complexdefaultstreampage.py +28 -0
  449. wagtail/test/testapp/models.py +79 -2
  450. wagtail/test/testapp/templates/tests/custom_docs_password_required.html +10 -0
  451. wagtail/test/testapp/templates/tests/custom_page_password_required.html +10 -0
  452. wagtail/test/testapp/views.py +24 -2
  453. wagtail/test/testapp/wagtail_hooks.py +19 -0
  454. wagtail/test/utils/wagtail_tests.py +2 -2
  455. wagtail/tests/test_blocks.py +262 -1
  456. wagtail/tests/test_migrations.py +1 -1
  457. wagtail/tests/test_page_model.py +77 -0
  458. wagtail/tests/test_page_privacy.py +18 -1
  459. wagtail/tests/test_rich_text.py +95 -5
  460. wagtail/tests/test_streamfield.py +43 -0
  461. wagtail/tests/test_utils.py +8 -2
  462. wagtail/tests/test_views.py +52 -1
  463. wagtail/tests/test_whitelist.py +7 -7
  464. wagtail/users/forms.py +3 -1
  465. wagtail/users/locale/en/LC_MESSAGES/django.po +124 -96
  466. wagtail/users/locale/fr/LC_MESSAGES/django.po +2 -2
  467. wagtail/users/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  468. wagtail/users/locale/he_IL/LC_MESSAGES/django.po +2 -2
  469. wagtail/users/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  470. wagtail/users/locale/hr_HR/LC_MESSAGES/django.po +13 -2
  471. wagtail/users/migrations/0013_userprofile_density.py +23 -0
  472. wagtail/users/models.py +14 -3
  473. wagtail/users/templates/wagtailusers/groups/create.html +1 -7
  474. wagtail/users/templates/wagtailusers/groups/edit.html +1 -13
  475. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +46 -2
  476. wagtail/users/templates/wagtailusers/groups/includes/group_form_js.html +0 -3
  477. wagtail/users/templates/wagtailusers/users/create.html +1 -14
  478. wagtail/users/templates/wagtailusers/users/edit.html +1 -14
  479. wagtail/users/templates/wagtailusers/users/index.html +2 -5
  480. wagtail/users/templates/wagtailusers/users/index_results.html +3 -13
  481. wagtail/users/templates/wagtailusers/users/user_cell.html +9 -0
  482. wagtail/users/templatetags/wagtailusers_tags.py +107 -20
  483. wagtail/users/tests/test_admin_views.py +669 -90
  484. wagtail/users/views/groups.py +58 -61
  485. wagtail/users/views/users.py +211 -92
  486. wagtail/users/wagtail_hooks.py +6 -38
  487. wagtail/users/widgets.py +3 -5
  488. wagtail/utils/text.py +1 -1
  489. wagtail/views.py +5 -9
  490. wagtail/whitelist.py +1 -1
  491. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/METADATA +5 -6
  492. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/RECORD +496 -477
  493. wagtail/admin/static/wagtailadmin/js/page-editor.js +0 -1
  494. wagtail/admin/static/wagtailadmin/js/vendor/mousetrap.min.js +0 -1
  495. wagtail/admin/static/wagtailadmin/js/vendor/urlify.js +0 -1
  496. wagtail/admin/static/wagtailadmin/js/vendor/xregexp.min.js +0 -1
  497. wagtail/admin/templates/wagtailadmin/collections/index.html +0 -34
  498. wagtail/admin/templates/wagtailadmin/pages/revisions/_actions.html +0 -22
  499. wagtail/admin/templates/wagtailadmin/shared/page_breadcrumbs.html +0 -55
  500. wagtail/admin/tests/pages/test_dashboard.py +0 -172
  501. wagtail/contrib/redirects/templates/wagtailredirects/results.html +0 -23
  502. wagtail/documents/templates/wagtaildocs/documents/list.html +0 -2
  503. wagtail/search/tests/test_postgres_stemming.py +0 -40
  504. wagtail/sites/templates/wagtailsites/create.html +0 -7
  505. wagtail/sites/templates/wagtailsites/edit.html +0 -7
  506. wagtail/snippets/templates/wagtailsnippets/snippets/revisions/_actions.html +0 -36
  507. wagtail/users/templates/wagtailusers/users/list.html +0 -62
  508. wagtail/users/urls/users.py +0 -12
  509. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/LICENSE +0 -0
  510. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/WHEEL +0 -0
  511. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/entry_points.txt +0 -0
  512. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/top_level.txt +0 -0
@@ -1,8 +1,10 @@
1
+ import django_filters
2
+ from django import forms
1
3
  from django.contrib.contenttypes.models import ContentType
2
4
  from django.core.exceptions import PermissionDenied
3
5
  from django.core.paginator import Paginator
4
6
  from django.db import transaction
5
- from django.db.models import Count, OuterRef
7
+ from django.db.models import Count, OuterRef, Prefetch
6
8
  from django.db.models.functions import Lower
7
9
  from django.http import Http404, HttpResponseBadRequest
8
10
  from django.shortcuts import get_object_or_404, redirect, render
@@ -18,6 +20,7 @@ from django.views.generic import TemplateView
18
20
 
19
21
  from wagtail.admin import messages
20
22
  from wagtail.admin.auth import PermissionPolicyChecker
23
+ from wagtail.admin.filters import MultipleContentTypeFilter, WagtailFilterSet
21
24
  from wagtail.admin.forms.workflows import (
22
25
  TaskChooserSearchForm,
23
26
  WorkflowContentTypeForm,
@@ -26,7 +29,7 @@ from wagtail.admin.forms.workflows import (
26
29
  get_workflow_edit_handler,
27
30
  )
28
31
  from wagtail.admin.modal_workflow import render_modal_workflow
29
- from wagtail.admin.ui.tables import Column, TitleColumn
32
+ from wagtail.admin.ui.tables import BaseColumn, Column, TitleColumn
30
33
  from wagtail.admin.views.generic import CreateView, DeleteView, EditView, IndexView
31
34
  from wagtail.coreutils import resolve_model_string
32
35
  from wagtail.models import (
@@ -36,6 +39,7 @@ from wagtail.models import (
36
39
  Workflow,
37
40
  WorkflowContentType,
38
41
  WorkflowState,
42
+ WorkflowTask,
39
43
  )
40
44
  from wagtail.permissions import (
41
45
  page_permission_policy,
@@ -48,35 +52,114 @@ from wagtail.workflows import get_task_types
48
52
  task_permission_checker = PermissionPolicyChecker(task_permission_policy)
49
53
 
50
54
 
55
+ class WorkflowTitleColumn(TitleColumn):
56
+ cell_template_name = "wagtailadmin/workflows/includes/workflow_title_cell.html"
57
+
58
+
59
+ class WorkflowUsedByColumn(TitleColumn):
60
+ cell_template_name = "wagtailadmin/workflows/includes/workflow_used_by_cell.html"
61
+
62
+ def get_cell_context_data(self, instance, parent_context):
63
+ context = super().get_cell_context_data(instance, parent_context)
64
+ context["workflow_enabled_models"] = get_workflow_enabled_models()
65
+ return context
66
+
67
+
68
+ class WorkflowTasksColumn(BaseColumn):
69
+ cell_template_name = "wagtailadmin/workflows/includes/workflow_tasks_cell.html"
70
+ num_tasks = 5
71
+
72
+ def get_cell_context_data(self, instance, parent_context):
73
+ context = super().get_cell_context_data(instance, parent_context)
74
+ context["tasks"] = instance.workflow_tasks.all()[: self.num_tasks]
75
+ context["extra_count"] = instance.workflow_tasks.count() - self.num_tasks
76
+ return context
77
+
78
+
79
+ class BaseWorkflowFilterSet(WagtailFilterSet):
80
+ show_disabled = django_filters.ChoiceFilter(
81
+ label=_("Show disabled"),
82
+ method="filter_show_disabled",
83
+ choices=(("true", _("Yes")), ("false", _("No"))),
84
+ widget=forms.RadioSelect,
85
+ empty_label=None,
86
+ initial="false",
87
+ )
88
+
89
+ def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
90
+ if data is not None:
91
+ if data.get("show_disabled") is None:
92
+ filter = self.base_filters["show_disabled"]
93
+ data = data.copy()
94
+ data["show_disabled"] = filter.extra["initial"]
95
+ super().__init__(data, queryset, request=request, prefix=prefix)
96
+
97
+ def filter_show_disabled(self, queryset, name, value):
98
+ if value == "true":
99
+ return queryset
100
+ return queryset.filter(active=True)
101
+
102
+
103
+ class WorkflowFilterSet(BaseWorkflowFilterSet):
104
+ class Meta:
105
+ model = Workflow
106
+ fields = []
107
+
108
+
51
109
  class Index(IndexView):
52
110
  permission_policy = workflow_permission_policy
53
111
  model = Workflow
54
112
  context_object_name = "workflows"
55
113
  template_name = "wagtailadmin/workflows/index.html"
114
+ results_template_name = "wagtailadmin/workflows/index_results.html"
56
115
  add_url_name = "wagtailadmin_workflows:add"
57
116
  edit_url_name = "wagtailadmin_workflows:edit"
58
117
  index_url_name = "wagtailadmin_workflows:index"
118
+ index_results_url_name = "wagtailadmin_workflows:index_results"
59
119
  page_title = _("Workflows")
60
120
  add_item_label = _("Add a workflow")
61
121
  header_icon = "tasks"
122
+ columns = [
123
+ WorkflowTitleColumn(
124
+ "name",
125
+ label=_("Name"),
126
+ url_name="wagtailadmin_workflows:edit",
127
+ width="25%",
128
+ sort_key="name",
129
+ ),
130
+ WorkflowUsedByColumn(
131
+ "usage",
132
+ label=_("Used by"),
133
+ url_name="wagtailadmin_workflows:usage",
134
+ width="15%",
135
+ ),
136
+ WorkflowTasksColumn("tasks", label=_("Tasks")),
137
+ ]
138
+ default_ordering = "name"
139
+ search_fields = ["name"]
140
+ filterset_class = WorkflowFilterSet
141
+ _show_breadcrumbs = True
142
+ paginate_by = 20
62
143
 
63
144
  def show_disabled(self):
64
- return self.request.GET.get("show_disabled", "false") == "true"
145
+ return self.filters.form.cleaned_data.get("show_disabled") == "true"
65
146
 
66
- def get_queryset(self):
67
- queryset = super().get_queryset()
68
- if not self.show_disabled():
69
- queryset = queryset.filter(active=True)
147
+ def get_base_queryset(self):
148
+ queryset = super().get_base_queryset()
70
149
  content_types = WorkflowContentType.objects.filter(
71
150
  workflow=OuterRef("pk")
72
151
  ).values_list("pk", flat=True)
73
152
  queryset = queryset.annotate(content_types=Count(content_types))
74
- return queryset
153
+ return queryset.prefetch_related(
154
+ "workflow_pages",
155
+ "workflow_pages__page",
156
+ "workflow_tasks",
157
+ "workflow_tasks__task",
158
+ )
75
159
 
76
160
  def get_context_data(self, **kwargs):
77
161
  context = super().get_context_data(**kwargs)
78
162
  context["showing_disabled"] = self.show_disabled()
79
- context["workflow_enabled_models"] = get_workflow_enabled_models()
80
163
  return context
81
164
 
82
165
 
@@ -91,6 +174,7 @@ class Create(CreateView):
91
174
  index_url_name = "wagtailadmin_workflows:index"
92
175
  header_icon = "tasks"
93
176
  edit_handler = None
177
+ _show_breadcrumbs = True
94
178
 
95
179
  def get_edit_handler(self):
96
180
  if not self.edit_handler:
@@ -177,6 +261,7 @@ class Edit(EditView):
177
261
  header_icon = "tasks"
178
262
  edit_handler = None
179
263
  MAX_PAGES = 5
264
+ _show_breadcrumbs = True
180
265
 
181
266
  def get_edit_handler(self):
182
267
  if not self.edit_handler:
@@ -226,7 +311,7 @@ class Edit(EditView):
226
311
  ) and self.object.active
227
312
  context["can_enable"] = (
228
313
  self.permission_policy is None
229
- or self.permission_policy.user_has_permission(self.request.user, "create")
314
+ or self.permission_policy.user_has_permission(self.request.user, "add")
230
315
  ) and not self.object.active
231
316
  context["media"] = bound_panel.media + form.media + pages_formset.media
232
317
  return context
@@ -334,7 +419,7 @@ def enable_workflow(request, pk):
334
419
  workflow = get_object_or_404(Workflow, id=pk)
335
420
 
336
421
  # Check permissions
337
- if not workflow_permission_policy.user_has_permission(request.user, "create"):
422
+ if not workflow_permission_policy.user_has_permission(request.user, "add"):
338
423
  raise PermissionDenied
339
424
 
340
425
  # Set workflow to active if inactive
@@ -396,33 +481,75 @@ class TaskUsageColumn(Column):
396
481
  cell_template_name = "wagtailadmin/workflows/includes/task_usage_cell.html"
397
482
 
398
483
 
484
+ class TaskFilterSet(BaseWorkflowFilterSet):
485
+ def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
486
+ super().__init__(data, queryset, request=request, prefix=prefix)
487
+ task_types = get_task_types()
488
+ ct_ids = [
489
+ ct.id for ct in ContentType.objects.get_for_models(*task_types).values()
490
+ ]
491
+ if len(task_types) > 1:
492
+ self.filters["content_type"] = MultipleContentTypeFilter(
493
+ label=_("Type"),
494
+ widget=forms.CheckboxSelectMultiple,
495
+ queryset=lambda request: ContentType.objects.filter(pk__in=ct_ids),
496
+ field_name="content_type",
497
+ )
498
+
499
+ class Meta:
500
+ model = Task
501
+ fields = []
502
+
503
+
399
504
  class TaskIndex(IndexView):
400
505
  permission_policy = task_permission_policy
401
506
  model = Task
402
507
  context_object_name = "tasks"
403
508
  template_name = "wagtailadmin/workflows/task_index.html"
509
+ results_template_name = "wagtailadmin/workflows/task_index_results.html"
404
510
  add_url_name = "wagtailadmin_workflows:select_task_type"
405
511
  edit_url_name = "wagtailadmin_workflows:edit_task"
406
512
  index_url_name = "wagtailadmin_workflows:task_index"
513
+ index_results_url_name = "wagtailadmin_workflows:task_index_results"
407
514
  page_title = _("Workflow tasks")
408
515
  add_item_label = _("New workflow task")
409
516
  header_icon = "thumbtack"
410
517
  columns = [
411
518
  TaskTitleColumn(
412
- "name", label=_("Name"), url_name="wagtailadmin_workflows:edit_task"
519
+ "name",
520
+ label=_("Name"),
521
+ url_name="wagtailadmin_workflows:edit_task",
522
+ sort_key="name",
523
+ ),
524
+ Column("type", label=_("Type"), accessor="get_verbose_name", width="25%"),
525
+ TaskUsageColumn(
526
+ "usage", label=_("Used on"), accessor="_active_workflows", width="25%"
413
527
  ),
414
- Column("type", label=_("Type"), accessor="get_verbose_name"),
415
- TaskUsageColumn("usage", label=_("Used on"), accessor="active_workflows"),
416
528
  ]
529
+ default_ordering = "name"
530
+ search_fields = ["name"]
531
+ filterset_class = TaskFilterSet
532
+ _show_breadcrumbs = True
533
+ paginate_by = 50
417
534
 
418
535
  def show_disabled(self):
419
- return self.request.GET.get("show_disabled", "false") == "true"
536
+ return self.filters.form.cleaned_data.get("show_disabled") == "true"
420
537
 
421
538
  def get_queryset(self):
422
- queryset = super().get_queryset()
423
- if not self.show_disabled():
424
- queryset = queryset.filter(active=True)
425
- return queryset.specific()
539
+ return (
540
+ super()
541
+ .get_queryset()
542
+ .specific()
543
+ .prefetch_related(
544
+ Prefetch(
545
+ "workflow_tasks",
546
+ queryset=WorkflowTask.objects.filter(
547
+ workflow__active=True
548
+ ).select_related("workflow"),
549
+ to_attr="_active_workflows",
550
+ )
551
+ )
552
+ )
426
553
 
427
554
  def get_context_data(self, **kwargs):
428
555
  context = super().get_context_data(**kwargs)
@@ -473,6 +600,7 @@ class CreateTask(CreateView):
473
600
  edit_url_name = "wagtailadmin_workflows:edit_task"
474
601
  index_url_name = "wagtailadmin_workflows:task_index"
475
602
  header_icon = "thumbtack"
603
+ _show_breadcrumbs = True
476
604
 
477
605
  @cached_property
478
606
  def model(self):
@@ -534,6 +662,7 @@ class EditTask(EditView):
534
662
  enable_item_label = _("Enable")
535
663
  enable_url_name = "wagtailadmin_workflows:enable_task"
536
664
  header_icon = "thumbtack"
665
+ _show_breadcrumbs = True
537
666
 
538
667
  @cached_property
539
668
  def model(self):
@@ -574,7 +703,7 @@ class EditTask(EditView):
574
703
  ) and self.object.active
575
704
  context["can_enable"] = (
576
705
  self.permission_policy is None
577
- or self.permission_policy.user_has_permission(self.request.user, "create")
706
+ or self.permission_policy.user_has_permission(self.request.user, "add")
578
707
  ) and not self.object.active
579
708
 
580
709
  # TODO: add warning msg when there are pages/snippets currently on this task in a workflow, add interaction like resetting task state when saved
@@ -626,7 +755,7 @@ def enable_task(request, pk):
626
755
  task = get_object_or_404(Task, id=pk)
627
756
 
628
757
  # Check permissions
629
- if not task_permission_policy.user_has_permission(request.user, "create"):
758
+ if not task_permission_policy.user_has_permission(request.user, "add"):
630
759
  raise PermissionDenied
631
760
 
632
761
  # Set workflow to active if inactive
@@ -649,8 +649,8 @@ class ModelViewSet(ViewSet):
649
649
  def _legacy_urlpatterns(self):
650
650
  # RemovedInWagtail70Warning: Remove legacy URL patterns
651
651
  return [
652
- path("<int:pk>/", self.redirect_to_edit_view),
653
- path("<int:pk>/delete/", self.redirect_to_delete_view),
652
+ path("<str:pk>/", self.redirect_to_edit_view),
653
+ path("<str:pk>/delete/", self.redirect_to_delete_view),
654
654
  ]
655
655
 
656
656
  def on_register(self):
@@ -0,0 +1,77 @@
1
+ from django.urls import path
2
+
3
+ from wagtail.admin.views.pages.choose_parent import ChooseParentView
4
+ from wagtail.admin.views.pages.listing import IndexView
5
+ from wagtail.models import Page
6
+
7
+ from .base import ViewSet
8
+
9
+
10
+ class PageListingViewSet(ViewSet):
11
+ """
12
+ A viewset to present a flat listing of all pages of a specific type.
13
+ All attributes and methods from :class:`~wagtail.admin.viewsets.base.ViewSet`
14
+ are available.
15
+ For more information on how to use this class, see :ref:`custom_page_listings`.
16
+ """
17
+
18
+ #: The view class to use for the index view; must be a subclass of ``wagtail.admin.views.pages.listing.IndexView``.
19
+ index_view_class = IndexView
20
+ #: The view class to use for choosing the parent page when creating a new page of this page type.
21
+ choose_parent_view_class = ChooseParentView
22
+ #: Required; the page model class that this viewset will work with.
23
+ model = Page
24
+ #: A list of ``wagtail.admin.ui.tables.Column`` instances for the columns in the listing.
25
+ columns = IndexView.columns
26
+ #: A subclass of ``wagtail.admin.filters.WagtailFilterSet``, which is a
27
+ #: subclass of `django_filters.FilterSet <https://django-filter.readthedocs.io/en/stable/ref/filterset.html>`_.
28
+ #: This will be passed to the ``filterset_class`` attribute of the index view.
29
+ filterset_class = IndexView.filterset_class
30
+
31
+ def get_common_view_kwargs(self, **kwargs):
32
+ return super().get_common_view_kwargs(
33
+ **{
34
+ "_show_breadcrumbs": True,
35
+ "header_icon": self.icon,
36
+ "model": self.model,
37
+ "index_url_name": self.get_url_name("index"),
38
+ "add_url_name": self.get_url_name("choose_parent"),
39
+ **kwargs,
40
+ }
41
+ )
42
+
43
+ def get_index_view_kwargs(self, **kwargs):
44
+ return {
45
+ "index_results_url_name": self.get_url_name("index_results"),
46
+ "columns": self.columns,
47
+ "filterset_class": self.filterset_class,
48
+ **kwargs,
49
+ }
50
+
51
+ def get_choose_parent_view_kwargs(self, **kwargs):
52
+ return kwargs
53
+
54
+ @property
55
+ def index_view(self):
56
+ return self.construct_view(
57
+ self.index_view_class, **self.get_index_view_kwargs()
58
+ )
59
+
60
+ @property
61
+ def index_results_view(self):
62
+ return self.construct_view(
63
+ self.index_view_class, **self.get_index_view_kwargs(), results_only=True
64
+ )
65
+
66
+ @property
67
+ def choose_parent_view(self):
68
+ return self.construct_view(
69
+ self.choose_parent_view_class, **self.get_choose_parent_view_kwargs()
70
+ )
71
+
72
+ def get_urlpatterns(self):
73
+ return [
74
+ path("", self.index_view, name="index"),
75
+ path("results/", self.index_results_view, name="index_results"),
76
+ path("choose_parent/", self.choose_parent_view, name="choose_parent"),
77
+ ]
@@ -1,6 +1,6 @@
1
1
  from django.conf import settings
2
2
  from django.contrib.auth.models import Permission
3
- from django.urls import reverse
3
+ from django.urls import reverse, reverse_lazy
4
4
  from django.utils.functional import cached_property
5
5
  from django.utils.http import urlencode
6
6
  from django.utils.translation import gettext
@@ -743,6 +743,21 @@ def register_core_features(features):
743
743
  # Keep pasted links with http/https protocol, and not-pasted links (href = undefined).
744
744
  "href": "^(http:|https:|undefined$)",
745
745
  },
746
+ "chooserUrls": {
747
+ "pageChooser": reverse_lazy("wagtailadmin_choose_page"),
748
+ "externalLinkChooser": reverse_lazy(
749
+ "wagtailadmin_choose_page_external_link"
750
+ ),
751
+ "emailLinkChooser": reverse_lazy(
752
+ "wagtailadmin_choose_page_email_link"
753
+ ),
754
+ "phoneLinkChooser": reverse_lazy(
755
+ "wagtailadmin_choose_page_phone_link"
756
+ ),
757
+ "anchorLinkChooser": reverse_lazy(
758
+ "wagtailadmin_choose_page_anchor_link"
759
+ ),
760
+ },
746
761
  },
747
762
  js=[
748
763
  "wagtailadmin/js/page-chooser-modal.js",
@@ -960,7 +975,7 @@ def register_reports_menu():
960
975
 
961
976
  @hooks.register("register_help_menu_item")
962
977
  def register_whats_new_in_wagtail_version_menu_item():
963
- version = "6.0"
978
+ version = "6.1"
964
979
  return DismissibleMenuItem(
965
980
  _("What's new in Wagtail %(version)s") % {"version": version},
966
981
  wagtail_feature_release_whats_new_link(),
@@ -983,6 +998,28 @@ def register_editors_guide_menu_item():
983
998
  )
984
999
 
985
1000
 
1001
+ @hooks.register("register_help_menu_item")
1002
+ def register_keyboard_shortcuts_menu_item():
1003
+ """
1004
+ Triggers the keyboard shortcuts dialog to open when clicked
1005
+ while preventing the default link click action.
1006
+ """
1007
+
1008
+ return MenuItem(
1009
+ _("Shortcuts"),
1010
+ icon_name="keyboard",
1011
+ order=1200,
1012
+ attrs={
1013
+ "role": "button", # Ensure screen readers announce this as a button
1014
+ "data-a11y-dialog-show": "keyboard-shortcuts-dialog",
1015
+ "data-action": "w-action#noop:prevent:stop",
1016
+ "data-controller": "w-action",
1017
+ },
1018
+ name="keyboard-shortcuts-trigger",
1019
+ url="#",
1020
+ )
1021
+
1022
+
986
1023
  @hooks.register("register_admin_menu_item")
987
1024
  def register_help_menu():
988
1025
  return DismissibleSubmenuMenuItem(
@@ -1061,6 +1098,7 @@ def register_icons(icons):
1061
1098
  "info-circle.svg",
1062
1099
  "italic.svg",
1063
1100
  "key.svg",
1101
+ "keyboard.svg",
1064
1102
  "link.svg",
1065
1103
  "link-external.svg",
1066
1104
  "list-ol.svg",
@@ -103,20 +103,20 @@ class HeaderButton(Button):
103
103
  classname="",
104
104
  icon_name=None,
105
105
  attrs={},
106
+ icon_only=False,
106
107
  **kwargs,
107
108
  ):
108
- classname = f"{classname} w-header-button".strip()
109
+ classname = f"{classname} w-header-button button".strip()
109
110
  attrs = attrs.copy()
110
- attrs.update(
111
- {
112
- "data-controller": "w-tooltip w-link",
113
- "data-w-tooltip-content-value": label,
114
- "data-action": "w-swap:success@document->w-link#setParamsFromSwapRequest",
115
- "aria-label": label,
116
- }
117
- )
111
+ if icon_only:
112
+ controller = f"{attrs.get('data-controller', '')} w-tooltip".strip()
113
+ attrs["data-controller"] = controller
114
+ attrs["data-w-tooltip-content-value"] = label
115
+ attrs["aria-label"] = label
116
+ label = ""
117
+
118
118
  super().__init__(
119
- label="",
119
+ label=label,
120
120
  url=url,
121
121
  classname=classname,
122
122
  icon_name=icon_name,
wagtail/api/v2/filters.py CHANGED
@@ -221,7 +221,7 @@ class AncestorOfFilter(BaseFilterBackend):
221
221
 
222
222
  class DescendantOfFilter(BaseFilterBackend):
223
223
  """
224
- Implements the ?decendant_of filter which limits the set of pages to a
224
+ Implements the ?descendant_of filter which limits the set of pages to a
225
225
  particular branch of the page tree.
226
226
  """
227
227
 
@@ -197,7 +197,7 @@ class TestPageListing(WagtailTestUtils, TestCase):
197
197
  self.assertTrue(blog_page_seen, msg="No blog pages were found in the items")
198
198
  self.assertTrue(event_page_seen, msg="No event pages were found in the items")
199
199
 
200
- def test_non_existant_type_gives_error(self):
200
+ def test_non_existent_type_gives_error(self):
201
201
  response = self.get_response(type="demosite.IDontExist")
202
202
  content = json.loads(response.content.decode("UTF-8"))
203
203
 
wagtail/blocks/base.py CHANGED
@@ -143,10 +143,10 @@ class Block(metaclass=BaseBlock):
143
143
  Return this block's default value (conventionally found in self.meta.default),
144
144
  converted to the value type expected by this block. This caters for the case
145
145
  where that value type is not something that can be expressed statically at
146
- model definition type (e.g. something like StructValue which incorporates a
147
- pointer back to the block definion object).
146
+ model definition time (e.g. something like StructValue which incorporates a
147
+ pointer back to the block definition object).
148
148
  """
149
- return self.meta.default
149
+ return self.normalize(self.meta.default)
150
150
 
151
151
  def clean(self, value):
152
152
  """
@@ -159,6 +159,15 @@ class Block(metaclass=BaseBlock):
159
159
  """
160
160
  return value
161
161
 
162
+ def normalize(self, value):
163
+ """
164
+ Given a value for any acceptable type for this block (e.g. string or RichText for a RichTextBlock;
165
+ dict or StructValue for a StructBlock), return a value of the block's native type (e.g. RichText
166
+ for RichTextBlock, StructValue for StructBlock). In simple cases this will return the value
167
+ unchanged.
168
+ """
169
+ return value
170
+
162
171
  def to_python(self, value):
163
172
  """
164
173
  Convert 'value' from a simple (JSON-serialisable) value to a (possibly complex) Python value to be
@@ -167,6 +176,9 @@ class Block(metaclass=BaseBlock):
167
176
  like the original value but provides a native HTML rendering when inserted into a template; or it
168
177
  might be something totally different (e.g. an image chooser will use the image ID as the clean
169
178
  value, and turn this back into an actual image object here).
179
+
180
+ For blocks that are usable at the top level of a StreamField, this must also accept any type accepted
181
+ by normalize. (This is because Django calls `Field.to_python` from `Field.clean`.)
170
182
  """
171
183
  return value
172
184
 
@@ -215,7 +227,7 @@ class Block(metaclass=BaseBlock):
215
227
  Return the template to use for rendering the block if specified on meta class.
216
228
  This extraction was added to make dynamic templates possible if you override this method
217
229
 
218
- value contains the current value of the block, allowing overriden methods to
230
+ value contains the current value of the block, allowing overridden methods to
219
231
  select the proper template based on the actual block value.
220
232
  """
221
233
  return getattr(self.meta, "template", None)
@@ -564,14 +576,11 @@ class BlockWidget(forms.Widget):
564
576
  error = errors.as_data()[0]
565
577
  error_json = json.dumps(get_error_json_data(error))
566
578
  else:
567
- error_json = "null"
579
+ error_json = json.dumps(None)
568
580
 
569
581
  return format_html(
570
582
  """
571
- <div id="{id}" data-block="{block_json}" data-value="{value_json}" data-error="{error_json}"></div>
572
- <script>
573
- initBlockWidget('{id}');
574
- </script>
583
+ <div id="{id}" data-block data-controller="w-block" data-w-block-data-value="{block_json}" data-w-block-arguments-value="[{value_json},{error_json}]"></div>
575
584
  """,
576
585
  id=name,
577
586
  block_json=self.block_json,
@@ -687,12 +687,6 @@ class RichTextBlock(FieldBlock):
687
687
  self.search_index = search_index
688
688
  super().__init__(**kwargs)
689
689
 
690
- def get_default(self):
691
- if isinstance(self.meta.default, RichText):
692
- return self.meta.default
693
- else:
694
- return RichText(self.meta.default)
695
-
696
690
  def to_python(self, value):
697
691
  # convert a source-HTML string from the JSONish representation
698
692
  # to a RichText object
@@ -703,6 +697,11 @@ class RichTextBlock(FieldBlock):
703
697
  # the JSONish representation
704
698
  return value.source
705
699
 
700
+ def normalize(self, value):
701
+ if isinstance(value, RichText):
702
+ return value
703
+ return RichText(value)
704
+
706
705
  @cached_property
707
706
  def field(self):
708
707
  from wagtail.admin.rich_text import get_rich_text_editor_widget
@@ -757,11 +756,14 @@ class RawHTMLBlock(FieldBlock):
757
756
  super().__init__(**kwargs)
758
757
 
759
758
  def get_default(self):
760
- return mark_safe(self.meta.default or "")
759
+ return self.normalize(self.meta.default or "")
761
760
 
762
761
  def to_python(self, value):
763
762
  return mark_safe(value)
764
763
 
764
+ def normalize(self, value):
765
+ return mark_safe(value)
766
+
765
767
  def get_prep_value(self, value):
766
768
  # explicitly convert to a plain string, just in case we're using some serialisation method
767
769
  # that doesn't cope with SafeString values correctly
@@ -151,10 +151,6 @@ class ListBlock(Block):
151
151
  # Default to a list consisting of one empty (i.e. default-valued) child item
152
152
  self.meta.default = [self.child_block.get_default()]
153
153
 
154
- def get_default(self):
155
- # wrap with list() so that each invocation of get_default returns a distinct instance
156
- return ListValue(self, values=list(self.meta.default))
157
-
158
154
  def value_from_datadict(self, data, files, prefix):
159
155
  count = int(data["%s-count" % prefix])
160
156
  child_blocks_with_indexes = []
@@ -183,8 +179,7 @@ class ListBlock(Block):
183
179
  def clean(self, value):
184
180
  # value is expected to be a ListValue, but if it's been assigned through external code it might
185
181
  # be a plain list; normalise it to a ListValue
186
- if not isinstance(value, ListValue):
187
- value = ListValue(self, values=value)
182
+ value = self.normalize(value)
188
183
 
189
184
  result = []
190
185
  block_errors = {}
@@ -224,6 +219,21 @@ class ListBlock(Block):
224
219
 
225
220
  return ListValue(self, bound_blocks=result)
226
221
 
222
+ def normalize(self, value):
223
+ if isinstance(value, ListValue):
224
+ return value
225
+ elif isinstance(value, list):
226
+ return ListValue(
227
+ self, values=[self.child_block.normalize(x) for x in value]
228
+ )
229
+ else:
230
+ raise TypeError(
231
+ f"Cannot handle {value!r} (type {type(value)!r}) as a value of a ListBlock"
232
+ )
233
+
234
+ def empty_value(self):
235
+ return ListValue(self, values=[])
236
+
227
237
  def _item_is_in_block_format(self, item):
228
238
  # check a list item retrieved from the database JSON representation to see whether it follows
229
239
  # the new format (https://github.com/wagtail/rfcs/blob/main/text/065-listblock.md) for a list item