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
@@ -556,7 +556,7 @@ class FormPage(AbstractEmailForm):
556
556
 
557
557
  # This is redundant (SubmissionsListView is the default view class), but importing
558
558
  # SubmissionsListView in this models.py helps us to confirm that this recipe
559
- # https://docs.wagtail.org/en/stable/reference/contrib/forms/customisation.html#customise-form-submissions-listing-in-wagtail-admin
559
+ # https://docs.wagtail.org/en/stable/reference/contrib/forms/customization.html#customise-form-submissions-listing-in-wagtail-admin
560
560
  # works without triggering circular dependency issues -
561
561
  # see https://github.com/wagtail/wagtail/issues/6265
562
562
  submissions_list_view_class = SubmissionsListView
@@ -1011,6 +1011,17 @@ class AdvertWithTabbedInterface(models.Model):
1011
1011
  register_snippet(AdvertWithTabbedInterface)
1012
1012
 
1013
1013
 
1014
+ class CustomManager(models.Manager):
1015
+ pass
1016
+
1017
+
1018
+ class ModelWithCustomManager(models.Model):
1019
+ instances = CustomManager()
1020
+
1021
+
1022
+ register_snippet(ModelWithCustomManager)
1023
+
1024
+
1014
1025
  # Models with RevisionMixin
1015
1026
  class RevisableModel(RevisionMixin, models.Model):
1016
1027
  text = models.TextField()
@@ -1423,7 +1434,11 @@ class EventPageChooserModel(models.Model):
1423
1434
  class SnippetChooserModel(models.Model):
1424
1435
  advert = models.ForeignKey(Advert, help_text="help text", on_delete=models.CASCADE)
1425
1436
  full_featured = models.ForeignKey(
1426
- FullFeaturedSnippet, on_delete=models.CASCADE, null=True, blank=True
1437
+ FullFeaturedSnippet,
1438
+ on_delete=models.CASCADE,
1439
+ null=True,
1440
+ blank=True,
1441
+ verbose_name="Chosen snippet",
1427
1442
  )
1428
1443
 
1429
1444
  panels = [
@@ -1611,6 +1626,36 @@ class DefaultStreamPage(Page):
1611
1626
  ]
1612
1627
 
1613
1628
 
1629
+ class ComplexDefaultStreamPage(Page):
1630
+ body = StreamField(
1631
+ [
1632
+ ("text", CharBlock()),
1633
+ ("rich_text", RichTextBlock()),
1634
+ (
1635
+ "books",
1636
+ StreamBlock(
1637
+ [
1638
+ ("title", CharBlock()),
1639
+ ("author", CharBlock()),
1640
+ ]
1641
+ ),
1642
+ ),
1643
+ ],
1644
+ default=[
1645
+ ("rich_text", "<p>My <i>lovely</i> books</p>"),
1646
+ (
1647
+ "books",
1648
+ [("title", "The Great Gatsby"), ("author", "F. Scott Fitzgerald")],
1649
+ ),
1650
+ ],
1651
+ )
1652
+
1653
+ content_panels = [
1654
+ FieldPanel("title"),
1655
+ FieldPanel("body"),
1656
+ ]
1657
+
1658
+
1614
1659
  class MTIBasePage(Page):
1615
1660
  is_creatable = False
1616
1661
 
@@ -2256,3 +2301,35 @@ class CustomPermissionTester(PagePermissionTester):
2256
2301
  class CustomPermissionPage(Page):
2257
2302
  def permissions_for_user(self, user):
2258
2303
  return CustomPermissionTester(user, self)
2304
+
2305
+
2306
+ class CustomPermissionModel(models.Model):
2307
+ text = models.TextField(default="Tailwag")
2308
+
2309
+ class Meta:
2310
+ verbose_name = "ADVANCED permission model"
2311
+ verbose_name_plural = "ADVANCED permission models"
2312
+
2313
+ # Django's default_permissions are ("add", "change", "delete", "view").
2314
+ # Django will generate permissions for each of these actions with the
2315
+ # format f"{action}_{model_name}" and the label "Can {action} {verbose_name}".
2316
+ # This means if the action is "bulk_update", the codename will be
2317
+ # "bulk_update_custompermissionmodel" and the label will be
2318
+ # "Can bulk_update ADVANCED permission model".
2319
+ # See https://github.com/django/django/blob/stable/5.0.x/django/contrib/auth/management/__init__.py#L22-L35
2320
+ default_permissions = ("add", "change", "delete", "view", "bulk_update")
2321
+
2322
+ # Permissions with completely custom codenames and labels
2323
+ permissions = [
2324
+ # Starts with can_ and "Can "
2325
+ ("can_start_trouble", "Can start trouble"),
2326
+ # Doesn't start with can_ and "Can "
2327
+ ("cause_chaos", "Cause chaos for advanced permission model"),
2328
+ # Starts with an action similar to a built-in permission "change_"
2329
+ ("change_text", "Change text"),
2330
+ # Without any _ and the label ends with the default verbose_name
2331
+ ("control", "Manage custom permission model"),
2332
+ ]
2333
+
2334
+
2335
+ register_snippet(CustomPermissionModel)
@@ -0,0 +1,10 @@
1
+ {% extends "tests/base.html" %}
2
+
3
+ {% block content %}
4
+ <p>Test only.</p>
5
+ <form action="{{ action_url }}" method="POST">
6
+ {% csrf_token %}
7
+ {{ form.as_p }}
8
+ <input type="submit" value="Continue" class="button" />
9
+ </form>
10
+ {% endblock %}
@@ -0,0 +1,10 @@
1
+ {% extends "tests/base.html" %}
2
+
3
+ {% block content %}
4
+ <p>Test only.</p>
5
+ <form action="{{ action_url }}" method="POST">
6
+ {% csrf_token %}
7
+ {{ form.as_p }}
8
+ <input type="submit" value="Continue" class="button" />
9
+ </form>
10
+ {% endblock %}
@@ -12,14 +12,16 @@ from wagtail.admin import messages
12
12
  from wagtail.admin.auth import user_passes_test
13
13
  from wagtail.admin.filters import WagtailFilterSet
14
14
  from wagtail.admin.panels import FieldPanel
15
- from wagtail.admin.ui.tables import BooleanColumn, UpdatedAtColumn
15
+ from wagtail.admin.ui.tables import BooleanColumn, Column, UpdatedAtColumn
16
16
  from wagtail.admin.views.generic import DeleteView, EditView, IndexView
17
17
  from wagtail.admin.viewsets.base import ViewSet, ViewSetGroup
18
18
  from wagtail.admin.viewsets.chooser import ChooserViewSet
19
19
  from wagtail.admin.viewsets.model import ModelViewSet, ModelViewSetGroup
20
+ from wagtail.admin.viewsets.pages import PageListingViewSet
20
21
  from wagtail.contrib.forms.views import SubmissionsListView
21
22
  from wagtail.test.testapp.models import (
22
23
  Advert,
24
+ EventPage,
23
25
  FeatureCompleteToy,
24
26
  JSONBlockCountsStreamModel,
25
27
  JSONMinMaxCountStreamModel,
@@ -49,7 +51,7 @@ def message_test(request):
49
51
 
50
52
  class CustomSubmissionsListView(SubmissionsListView):
51
53
  paginate_by = 50
52
- ordering = ("submit_time",)
54
+ default_ordering = ("submit_time",)
53
55
  ordering_csv = ("-submit_time",)
54
56
 
55
57
  def get_csv_filename(self):
@@ -295,3 +297,23 @@ animated_advert_chooser_viewset = AnimatedAdvertChooserViewSet(
295
297
  )
296
298
 
297
299
  AdvertChooserWidget = animated_advert_chooser_viewset.widget_class
300
+
301
+
302
+ class EventPageFilterSet(PageListingViewSet.filterset_class):
303
+ class Meta:
304
+ model = EventPage
305
+ fields = ["audience"]
306
+
307
+
308
+ class EventPageListingViewSet(PageListingViewSet):
309
+ model = EventPage
310
+ icon = "calendar"
311
+ menu_label = "Event pages"
312
+ add_to_admin_menu = True
313
+ columns = PageListingViewSet.columns + [
314
+ Column("audience", label="Audience", sort_key="audience"),
315
+ ]
316
+ filterset_class = EventPageFilterSet
317
+
318
+
319
+ event_page_listing_viewset = EventPageListingViewSet("event_pages")
@@ -31,6 +31,7 @@ from wagtail.test.testapp.models import (
31
31
  ModeratedModel,
32
32
  RevisableChildModel,
33
33
  RevisableModel,
34
+ SnippetChooserModel,
34
35
  VariousOnDeleteModel,
35
36
  )
36
37
  from wagtail.test.testapp.views import (
@@ -39,6 +40,7 @@ from wagtail.test.testapp.views import (
39
40
  SearchTestModelViewSet,
40
41
  ToyViewSetGroup,
41
42
  animated_advert_chooser_viewset,
43
+ event_page_listing_viewset,
42
44
  )
43
45
 
44
46
  from .forms import FavouriteColourForm
@@ -384,12 +386,24 @@ class VariousOnDeleteModelViewSet(SnippetViewSet):
384
386
  inspect_view_enabled = True
385
387
 
386
388
 
389
+ class SnippetChooserModelViewSet(SnippetViewSet):
390
+ model = SnippetChooserModel
391
+
392
+ list_display = [
393
+ "__str__",
394
+ "full_featured__text",
395
+ "full_featured__latest_revision__created_at",
396
+ ]
397
+ exclude_form_fields = []
398
+
399
+
387
400
  register_snippet(FullFeaturedSnippet, viewset=FullFeaturedSnippetViewSet)
388
401
  register_snippet(DraftStateModel, viewset=DraftStateModelViewSet)
389
402
  # Works with both classes and instances
390
403
  register_snippet(ModeratedModelViewSet())
391
404
  register_snippet(RevisableViewSetGroup)
392
405
  register_snippet(VariousOnDeleteModelViewSet)
406
+ register_snippet(SnippetChooserModelViewSet)
393
407
 
394
408
 
395
409
  @hooks.register("register_bulk_action")
@@ -404,3 +418,8 @@ class DisableBulkAction(SnippetBulkAction):
404
418
  @hooks.register("register_admin_viewset")
405
419
  def register_animated_advert_chooser_viewset():
406
420
  return animated_advert_chooser_viewset
421
+
422
+
423
+ @hooks.register("register_admin_viewset")
424
+ def register_event_page_listing_viewset():
425
+ return event_page_listing_viewset
@@ -9,8 +9,8 @@ from django.test.testcases import assert_and_parse_html
9
9
 
10
10
  class WagtailTestUtils:
11
11
  @staticmethod
12
- def get_soup(markup: Union[str, bytes], parser="html.parser") -> BeautifulSoup:
13
- return BeautifulSoup(markup, parser)
12
+ def get_soup(markup: Union[str, bytes]) -> BeautifulSoup:
13
+ return BeautifulSoup(markup, "html.parser")
14
14
 
15
15
  @staticmethod
16
16
  def create_test_user():
@@ -50,6 +50,13 @@ class ContextCharBlock(blocks.CharBlock):
50
50
  return super(blocks.CharBlock, self).get_context(value, parent_context)
51
51
 
52
52
 
53
+ class TestBlock(SimpleTestCase):
54
+ def test_normalize(self):
55
+ """The base normalize implementation should return its argument unchanged"""
56
+ obj = object()
57
+ self.assertIs(blocks.Block().normalize(obj), obj)
58
+
59
+
53
60
  class TestFieldBlock(WagtailTestUtils, SimpleTestCase):
54
61
  def test_charfield_render(self):
55
62
  block = blocks.CharBlock()
@@ -206,7 +213,8 @@ class TestFieldBlock(WagtailTestUtils, SimpleTestCase):
206
213
  def test_form_handling_is_independent_of_serialisation(self):
207
214
  class Base64EncodingCharBlock(blocks.CharBlock):
208
215
  """A CharBlock with a deliberately perverse JSON (de)serialisation format
209
- so that it visibly blows up if we call to_python / get_prep_value where we shouldn't"""
216
+ so that it visibly blows up if we call to_python / get_prep_value where we shouldn't
217
+ """
210
218
 
211
219
  def to_python(self, jsonish_value):
212
220
  # decode as base64 on the way out of the JSON serialisation
@@ -718,6 +726,14 @@ class TestRichTextBlock(TestCase):
718
726
 
719
727
  self.assertEqual(list(block.extract_references(value)), [(Page, "1", "", "")])
720
728
 
729
+ def test_normalize(self):
730
+ block = blocks.RichTextBlock()
731
+ for value in ("Hello, world", RichText("Hello, world")):
732
+ with self.subTest(value=value):
733
+ normalized = block.normalize(value)
734
+ self.assertIsInstance(normalized, RichText)
735
+ self.assertEqual(normalized.source, "Hello, world")
736
+
721
737
 
722
738
  class TestChoiceBlock(WagtailTestUtils, SimpleTestCase):
723
739
  def setUp(self):
@@ -2256,6 +2272,53 @@ class TestStructBlock(SimpleTestCase):
2256
2272
  self.assertIs(value.block, copied.block)
2257
2273
  self.assertEqual(value, copied)
2258
2274
 
2275
+ def test_normalize_base_cases(self):
2276
+ """Test the trivially recursive and already normalized cases"""
2277
+ block = blocks.StructBlock([("title", blocks.CharBlock())])
2278
+ self.assertEqual(
2279
+ block.normalize({"title": "Foo"}), block._to_struct_value({"title": "Foo"})
2280
+ )
2281
+ self.assertEqual(
2282
+ block.normalize(block._to_struct_value({"title": "Foo"})),
2283
+ block._to_struct_value({"title": "Foo"}),
2284
+ )
2285
+
2286
+ def test_recursive_normalize(self):
2287
+ """StructBlock.normalize should recursively normalize all children"""
2288
+
2289
+ block = blocks.StructBlock(
2290
+ [
2291
+ (
2292
+ "inner_stream",
2293
+ blocks.StreamBlock(
2294
+ [
2295
+ ("inner_char", blocks.CharBlock()),
2296
+ ("inner_int", blocks.IntegerBlock()),
2297
+ ]
2298
+ ),
2299
+ ),
2300
+ ("list_of_ints", blocks.ListBlock(blocks.IntegerBlock())),
2301
+ ]
2302
+ )
2303
+
2304
+ # A value in the human friendly format
2305
+ value = {
2306
+ "inner_stream": [("inner_char", "Hello, world"), ("inner_int", 42)],
2307
+ "list_of_ints": [5, 6, 7, 8],
2308
+ }
2309
+
2310
+ normalized = block.normalize(value)
2311
+ self.assertIsInstance(normalized, blocks.StructValue)
2312
+ self.assertIsInstance(normalized["inner_stream"], blocks.StreamValue)
2313
+ self.assertIsInstance(
2314
+ normalized["inner_stream"][0], blocks.StreamValue.StreamChild
2315
+ )
2316
+ self.assertIsInstance(
2317
+ normalized["inner_stream"][1], blocks.StreamValue.StreamChild
2318
+ )
2319
+ self.assertIsInstance(normalized["list_of_ints"], blocks.list_block.ListValue)
2320
+ self.assertIsInstance(normalized["list_of_ints"][0], int)
2321
+
2259
2322
 
2260
2323
  class TestStructBlockWithCustomStructValue(SimpleTestCase):
2261
2324
  def test_initialisation(self):
@@ -2433,8 +2496,50 @@ class TestStructBlockWithCustomStructValue(SimpleTestCase):
2433
2496
  html = block.render(struct_value)
2434
2497
  self.assertEqual(html, "<div>EMPTY TITLE</div>\n")
2435
2498
 
2499
+ def test_normalize(self):
2500
+ """A normalized StructBlock value should be an instance of the StructBlock's value_class"""
2501
+
2502
+ class CustomStructValue(blocks.StructValue):
2503
+ pass
2504
+
2505
+ class CustomStructBlock(blocks.StructBlock):
2506
+ text = blocks.TextBlock()
2507
+
2508
+ class Meta:
2509
+ value_class = CustomStructValue
2510
+
2511
+ self.assertIsInstance(
2512
+ CustomStructBlock().normalize({"text": "She sells sea shells"}),
2513
+ CustomStructValue,
2514
+ )
2515
+
2516
+ def test_normalize_incorrect_value_class(self):
2517
+ """
2518
+ If StructBlock.normalize is passed a StructValue instance that doesn't
2519
+ match the StructBlock's `value_class', it should convert the value
2520
+ to the correct class.
2521
+ """
2522
+
2523
+ class CustomStructValue(blocks.StructValue):
2524
+ pass
2525
+
2526
+ class CustomStructBlock(blocks.StructBlock):
2527
+ text = blocks.TextBlock()
2528
+
2529
+ class Meta:
2530
+ value_class = CustomStructValue
2531
+
2532
+ block = CustomStructBlock()
2533
+ # Not an instance of CustomStructValue, which CustomStructBlock uses.
2534
+ value = blocks.StructValue(block, {"text": "The quick brown fox"})
2535
+ self.assertIsInstance(block.normalize(value), CustomStructValue)
2536
+
2436
2537
 
2437
2538
  class TestListBlock(WagtailTestUtils, SimpleTestCase):
2539
+ def assert_eq_list_values(self, p, q):
2540
+ # We can't directly compare ListValue instances yet
2541
+ self.assertEqual(list(p), list(q))
2542
+
2438
2543
  def test_initialise_with_class(self):
2439
2544
  block = blocks.ListBlock(blocks.CharBlock)
2440
2545
 
@@ -2961,6 +3066,42 @@ class TestListBlock(WagtailTestUtils, SimpleTestCase):
2961
3066
  self.assertEqual(len(result[0]["value"]), 2)
2962
3067
  self.assertEqual(result[0]["value"][0]["value"], "foo")
2963
3068
 
3069
+ def test_normalize_base_case(self):
3070
+ """Test normalize when trivially recursive, or already a ListValue"""
3071
+ block = blocks.ListBlock(blocks.IntegerBlock)
3072
+ normalized = block.normalize([0, 1, 1, 2, 3])
3073
+ self.assertIsInstance(normalized, blocks.list_block.ListValue)
3074
+ self.assert_eq_list_values(normalized, [0, 1, 1, 2, 3])
3075
+
3076
+ normalized = block.normalize(
3077
+ blocks.list_block.ListValue(block, [0, 1, 1, 2, 3])
3078
+ )
3079
+ self.assertIsInstance(normalized, blocks.list_block.ListValue)
3080
+ self.assert_eq_list_values(normalized, [0, 1, 1, 2, 3])
3081
+
3082
+ def test_normalize_empty(self):
3083
+ block = blocks.ListBlock(blocks.IntegerBlock())
3084
+ normalized = block.normalize([])
3085
+ self.assertIsInstance(normalized, blocks.list_block.ListValue)
3086
+ self.assert_eq_list_values(normalized, [])
3087
+
3088
+ def test_recursive_normalize(self):
3089
+ """
3090
+ ListBlock.normalize should recursively normalize all values passed to
3091
+ it, and return a ListValue.
3092
+ """
3093
+ inner_list_block = blocks.ListBlock(blocks.IntegerBlock())
3094
+ block = blocks.ListBlock(inner_list_block)
3095
+ values = [
3096
+ [[1, 2, 3]],
3097
+ [blocks.list_block.ListValue(block, [1, 2, 3])],
3098
+ ]
3099
+
3100
+ for value in values:
3101
+ normalized = block.normalize(value)
3102
+ self.assertIsInstance(normalized, blocks.list_block.ListValue)
3103
+ self.assert_eq_list_values(normalized[0], [1, 2, 3])
3104
+
2964
3105
 
2965
3106
  class TestListBlockWithFixtures(TestCase):
2966
3107
  fixtures = ["test.json"]
@@ -4228,6 +4369,120 @@ class TestStreamBlock(WagtailTestUtils, SimpleTestCase):
4228
4369
  )
4229
4370
 
4230
4371
 
4372
+ class TestNormalizeStreamBlock(SimpleTestCase):
4373
+ @classmethod
4374
+ def setUpClass(cls):
4375
+ super().setUpClass()
4376
+ cls.simple_block = blocks.StreamBlock(
4377
+ [("number", blocks.IntegerBlock()), ("text", blocks.TextBlock())]
4378
+ )
4379
+ cls.recursive_block = blocks.StreamBlock(
4380
+ [
4381
+ (
4382
+ "inner_stream",
4383
+ blocks.StreamBlock(
4384
+ [
4385
+ ("number", blocks.IntegerBlock()),
4386
+ ("text", blocks.TextBlock()),
4387
+ ("inner_list", blocks.ListBlock(blocks.IntegerBlock)),
4388
+ ]
4389
+ ),
4390
+ ),
4391
+ ("struct", blocks.StructBlock([("bool", blocks.BooleanBlock())])),
4392
+ ("list", blocks.ListBlock(blocks.IntegerBlock)),
4393
+ ]
4394
+ )
4395
+
4396
+ def test_normalize_empty_stream(self):
4397
+ values = [[], "", None]
4398
+ for value in values:
4399
+ with self.subTest(value=value):
4400
+ self.assertEqual(
4401
+ self.simple_block.normalize(value),
4402
+ blocks.StreamValue(self.simple_block, []),
4403
+ )
4404
+
4405
+ def test_normalize_base_case(self):
4406
+ """
4407
+ Test normalize when trivially recursive, or already a StreamValue
4408
+ """
4409
+ value = [("number", 1), ("text", "ichiban")]
4410
+ stream_value = blocks.StreamValue(self.simple_block, value)
4411
+ self.assertEqual(stream_value, self.simple_block.normalize(value))
4412
+ self.assertEqual(stream_value, self.simple_block.normalize(stream_value))
4413
+
4414
+ def test_normalize_recursive(self):
4415
+ """
4416
+ A stream block is normalized iff all of its sub-blocks are normalized.
4417
+ """
4418
+ values = (
4419
+ # A smart, "list of tuples" representation
4420
+ [
4421
+ ("struct", {"bool": True}),
4422
+ (
4423
+ "inner_stream",
4424
+ [
4425
+ ("number", 1),
4426
+ ("text", "one"),
4427
+ ("inner_list", [0, 1, 1, 2, 3, 5]),
4428
+ ],
4429
+ ),
4430
+ ("list", [0, 1, 1, 2, 3, 5]),
4431
+ ],
4432
+ # A json-ish representation - the serialized format
4433
+ [
4434
+ {"type": "struct", "value": {"bool": True}},
4435
+ {
4436
+ "type": "inner_stream",
4437
+ "value": [
4438
+ {"type": "number", "value": 1},
4439
+ {"type": "text", "value": "one"},
4440
+ {
4441
+ "type": "inner_list",
4442
+ "value": [
4443
+ # Unlike StreamBlock, ListBlock requires that its items
4444
+ # have IDs, to distinguish the new serialization format
4445
+ # from the old.
4446
+ {"type": "item", "value": 0, "id": 1},
4447
+ {"type": "item", "value": 1, "id": 2},
4448
+ {"type": "item", "value": 2, "id": 3},
4449
+ ],
4450
+ },
4451
+ ],
4452
+ },
4453
+ {
4454
+ "type": "list",
4455
+ "value": [
4456
+ {"type": "item", "value": 0, "id": 1},
4457
+ {"type": "item", "value": 1, "id": 2},
4458
+ {"type": "item", "value": 2, "id": 3},
4459
+ ],
4460
+ },
4461
+ ],
4462
+ )
4463
+
4464
+ for value in values:
4465
+ with self.subTest(value=value):
4466
+ # Normalize the value.
4467
+ normalized = self.recursive_block.normalize(value)
4468
+ # Then check all of the sub-blocks have been normalized:
4469
+ # the StructBlock child
4470
+ self.assertIsInstance(normalized[0].value, blocks.StructValue)
4471
+ self.assertIsInstance(normalized[0].value["bool"], bool)
4472
+ # the nested StreamBlock child
4473
+ self.assertIsInstance(normalized[1].value, blocks.StreamValue)
4474
+ self.assertIsInstance(normalized[1].value[0].value, int)
4475
+ self.assertIsInstance(normalized[1].value[1].value, str)
4476
+ # the ListBlock child
4477
+ self.assertIsInstance(normalized[2].value[0], int)
4478
+ self.assertIsInstance(normalized[2].value, blocks.list_block.ListValue)
4479
+ # the inner ListBlock nested in the nested streamblock
4480
+ self.assertIsInstance(normalized[1].value[2].value[0], int)
4481
+ self.assertIsInstance(
4482
+ normalized[1].value[2].value, blocks.list_block.ListValue
4483
+ )
4484
+
4485
+
4231
4486
  class TestStructBlockWithFixtures(TestCase):
4232
4487
  fixtures = ["test.json"]
4233
4488
 
@@ -4742,6 +4997,12 @@ class TestStaticBlock(unittest.TestCase):
4742
4997
  result = block.to_python(None)
4743
4998
  self.assertIsNone(result)
4744
4999
 
5000
+ def test_normalize(self):
5001
+ """
5002
+ StaticBlock.normalize always returns None, as a StaticBlock has no value
5003
+ """
5004
+ self.assertIsNone(blocks.StaticBlock().normalize(11))
5005
+
4745
5006
 
4746
5007
  class TestDateBlock(TestCase):
4747
5008
  def test_adapt(self):
@@ -16,7 +16,7 @@ class TestForMigrations(TestCase):
16
16
  app_labels = {
17
17
  app.label
18
18
  for app in apps.get_app_configs()
19
- if app.name.startswith("wagtail.")
19
+ if app.name.split(".")[0] == "wagtail"
20
20
  }
21
21
  for app_label in app_labels:
22
22
  apps.get_app_config(app_label.split(".")[-1])
@@ -68,6 +68,7 @@ from wagtail.test.testapp.models import (
68
68
  TaggedPage,
69
69
  )
70
70
  from wagtail.test.utils import WagtailTestUtils
71
+ from wagtail.url_routing import RouteResult
71
72
 
72
73
 
73
74
  def get_ct(model):
@@ -211,6 +212,50 @@ class TestSiteRouting(TestCase):
211
212
  self.unrecognised_port = "8000"
212
213
  self.unrecognised_hostname = "unknown.site.com"
213
214
 
215
+ def test_route_for_request_query_count(self):
216
+ request = get_dummy_request(site=self.events_site)
217
+ with self.assertNumQueries(2):
218
+ # expect queries for site & page
219
+ Page.route_for_request(request, request.path)
220
+ with self.assertNumQueries(0):
221
+ # subsequent lookups should be cached on the request
222
+ Page.route_for_request(request, request.path)
223
+
224
+ def test_route_for_request_value(self):
225
+ request = get_dummy_request(site=self.events_site)
226
+ self.assertFalse(hasattr(request, "_wagtail_route_for_request"))
227
+ result = Page.route_for_request(request, request.path)
228
+ self.assertTrue(isinstance(result, RouteResult))
229
+ self.assertEqual(
230
+ (result[0], result[1], result[2]),
231
+ (self.events_site.root_page.specific, [], {}),
232
+ )
233
+ self.assertTrue(hasattr(request, "_wagtail_route_for_request"))
234
+ self.assertIs(request._wagtail_route_for_request, result)
235
+
236
+ def test_route_for_request_cached(self):
237
+ request = get_dummy_request(site=self.events_site)
238
+ m = Mock()
239
+ request._wagtail_route_for_request = m
240
+ with self.assertNumQueries(0):
241
+ self.assertEqual(Page.route_for_request(request, request.path), m)
242
+
243
+ def test_route_for_request_suppresses_404(self):
244
+ request = get_dummy_request(path="does-not-exist", site=self.events_site)
245
+ self.assertIsNone(Page.route_for_request(request, request.path))
246
+
247
+ def test_find_for_request(self):
248
+ request_200 = get_dummy_request(site=self.events_site)
249
+ self.assertEqual(
250
+ Page.find_for_request(request_200, request_200.path),
251
+ self.events_site.root_page.specific,
252
+ )
253
+ request_404 = get_dummy_request(path="does-not-exist", site=self.events_site)
254
+ self.assertEqual(
255
+ Page.find_for_request(request_404, request_404.path),
256
+ None,
257
+ )
258
+
214
259
  def test_valid_headers_route_to_specific_site(self):
215
260
  # requests with a known Host: header should be directed to the specific site
216
261
  request = get_dummy_request(site=self.events_site)
@@ -502,6 +547,20 @@ class TestRouting(TestCase):
502
547
  christmas_page.get_url(request=request), "/events/christmas/"
503
548
  )
504
549
 
550
+ def test_cached_parent_obj_set(self):
551
+ homepage = Page.objects.get(url_path="/home/")
552
+ christmas_page = EventPage.objects.get(url_path="/home/events/christmas/")
553
+
554
+ request = get_dummy_request(path="/events/christmas/")
555
+ (found_page, args, kwargs) = homepage.route(request, ["events", "christmas"])
556
+ self.assertEqual(found_page, christmas_page)
557
+
558
+ # parent cache should be set
559
+ events_page = Page.objects.get(url_path="/home/events/").specific
560
+ with self.assertNumQueries(0):
561
+ parent = found_page.get_parent(update=False)
562
+ self.assertEqual(parent, events_page)
563
+
505
564
 
506
565
  @override_settings(
507
566
  ROOT_URLCONF="wagtail.test.urls_multilang",
@@ -3917,3 +3976,21 @@ class TestPageCacheKey(TestCase):
3917
3976
  self.page.slug = "something-else"
3918
3977
  self.page.save()
3919
3978
  self.assertNotEqual(self.page.cache_key, original_cache_key)
3979
+
3980
+
3981
+ class TestPageCachedParentObjExists(TestCase):
3982
+ fixtures = ["test.json"]
3983
+
3984
+ def test_cached_parent_obj_exists(self):
3985
+ # https://github.com/wagtail/wagtail/pull/11737
3986
+
3987
+ # Test if _cached_parent_obj is set after using page.get_parent()
3988
+ # This is treebeard specific, we don't know if their API will change.
3989
+ homepage = Page.objects.get(url_path="/home/")
3990
+ homepage._cached_parent_obj = "_cached_parent_obj_exists"
3991
+ parent = homepage.get_parent(update=False)
3992
+ self.assertEqual(
3993
+ parent,
3994
+ "_cached_parent_obj_exists",
3995
+ "Page.get_parent() (treebeard) no longer uses _cached_parent_obj to cache the parent object",
3996
+ )