wagtail 6.2.2__py3-none-any.whl → 6.3rc2__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 (386) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_for_translation.py +6 -0
  3. wagtail/actions/publish_revision.py +3 -3
  4. wagtail/admin/action_menu.py +5 -3
  5. wagtail/admin/forms/account.py +1 -1
  6. wagtail/admin/icons.py +2 -6
  7. wagtail/admin/locale/cy/LC_MESSAGES/django.mo +0 -0
  8. wagtail/admin/locale/cy/LC_MESSAGES/django.po +32 -0
  9. wagtail/admin/locale/dv/LC_MESSAGES/django.mo +0 -0
  10. wagtail/admin/locale/dv/LC_MESSAGES/django.po +28 -0
  11. wagtail/admin/locale/en/LC_MESSAGES/django.po +451 -485
  12. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +7 -7
  13. wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
  14. wagtail/admin/locale/sl/LC_MESSAGES/django.po +150 -0
  15. wagtail/admin/locale/sl/LC_MESSAGES/djangojs.mo +0 -0
  16. wagtail/admin/locale/sl/LC_MESSAGES/djangojs.po +9 -2
  17. wagtail/admin/locale/ug/LC_MESSAGES/django.mo +0 -0
  18. wagtail/admin/locale/ug/LC_MESSAGES/django.po +3250 -196
  19. wagtail/admin/localization.py +12 -2
  20. wagtail/admin/panels/model_utils.py +1 -1
  21. wagtail/admin/site_summary.py +0 -2
  22. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  23. wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
  24. wagtail/admin/static/wagtailadmin/images/email-header.jpg +0 -0
  25. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  26. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  27. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +12 -0
  28. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  29. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  30. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  31. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  32. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  33. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +1 -1
  34. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  37. wagtail/admin/staticfiles.py +6 -4
  38. wagtail/admin/templates/wagtailadmin/account/account.html +42 -59
  39. wagtail/admin/templates/wagtailadmin/admin_base.html +0 -28
  40. wagtail/admin/templates/wagtailadmin/bulk_actions/footer.html +1 -1
  41. wagtail/admin/templates/wagtailadmin/chooser/_search_results.html +7 -5
  42. wagtail/admin/templates/wagtailadmin/chooser/tables/page_navigate_to_children_cell.html +1 -1
  43. wagtail/admin/templates/wagtailadmin/generic/chooser/results.html +7 -5
  44. wagtail/admin/templates/wagtailadmin/generic/edit.html +0 -8
  45. wagtail/admin/templates/wagtailadmin/generic/form.html +60 -17
  46. wagtail/admin/templates/wagtailadmin/generic/index.html +17 -0
  47. wagtail/admin/templates/wagtailadmin/generic/index_results.html +9 -35
  48. wagtail/admin/templates/wagtailadmin/generic/inspect.html +2 -21
  49. wagtail/admin/templates/wagtailadmin/generic/listing.html +11 -17
  50. wagtail/admin/templates/wagtailadmin/generic/listing_results.html +19 -1
  51. wagtail/admin/templates/wagtailadmin/home/account_summary.html +26 -0
  52. wagtail/admin/templates/wagtailadmin/home/locked_pages.html +28 -18
  53. wagtail/admin/templates/wagtailadmin/home/recent_edits.html +28 -17
  54. wagtail/admin/templates/wagtailadmin/home/site_summary_pages.html +2 -2
  55. wagtail/admin/templates/wagtailadmin/home/upgrade_notification.html +35 -13
  56. wagtail/admin/templates/wagtailadmin/home/user_objects_in_workflow_moderation.html +28 -18
  57. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +43 -32
  58. wagtail/admin/templates/wagtailadmin/home.html +22 -5
  59. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -1
  60. wagtail/admin/templates/wagtailadmin/pages/action_menu/menu.html +1 -11
  61. wagtail/admin/templates/wagtailadmin/pages/action_menu/menu_item.html +1 -6
  62. wagtail/admin/templates/wagtailadmin/pages/action_menu/page_locked.html +1 -1
  63. wagtail/admin/templates/wagtailadmin/pages/action_menu/publish.html +1 -1
  64. wagtail/admin/templates/wagtailadmin/pages/action_menu/save_draft.html +1 -1
  65. wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_delete.html +12 -6
  66. wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_move.html +12 -7
  67. wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_publish.html +17 -11
  68. wagtail/admin/templates/wagtailadmin/pages/bulk_actions/confirm_bulk_unpublish.html +15 -9
  69. wagtail/admin/templates/wagtailadmin/pages/confirm_delete.html +9 -9
  70. wagtail/admin/templates/wagtailadmin/pages/confirm_move.html +2 -2
  71. wagtail/admin/templates/wagtailadmin/pages/confirm_unpublish.html +4 -4
  72. wagtail/admin/templates/wagtailadmin/pages/create.html +15 -34
  73. wagtail/admin/templates/wagtailadmin/pages/edit.html +15 -30
  74. wagtail/admin/templates/wagtailadmin/pages/explorable_index.html +6 -0
  75. wagtail/admin/templates/wagtailadmin/pages/explorable_index_results.html +60 -0
  76. wagtail/admin/templates/wagtailadmin/pages/index.html +1 -36
  77. wagtail/admin/templates/wagtailadmin/pages/index_results.html +2 -50
  78. wagtail/admin/templates/wagtailadmin/pages/listing/_locked_indicator.html +1 -1
  79. wagtail/admin/templates/wagtailadmin/pages/listing/_ordering_cell.html +1 -1
  80. wagtail/admin/templates/wagtailadmin/pages/listing/_page_header_buttons.html +1 -1
  81. wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_column_header.html +3 -3
  82. wagtail/admin/templates/wagtailadmin/pages/listing/_pagination.html +1 -1
  83. wagtail/admin/templates/wagtailadmin/pages/listing.html +24 -0
  84. wagtail/admin/templates/wagtailadmin/pages/search.html +1 -20
  85. wagtail/admin/templates/wagtailadmin/pages/search_results.html +27 -25
  86. wagtail/admin/templates/wagtailadmin/permissions/includes/collection_member_permissions_formset.html +0 -1
  87. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_report.html +2 -2
  88. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_types_usage.html +6 -6
  89. wagtail/admin/templates/wagtailadmin/reports/workflow_tasks_results.html +1 -1
  90. wagtail/admin/templates/wagtailadmin/shared/action_menu/menu.html +14 -0
  91. wagtail/admin/templates/wagtailadmin/shared/action_menu/menu_item.html +6 -0
  92. wagtail/admin/templates/wagtailadmin/shared/avatar.html +13 -3
  93. wagtail/admin/templates/wagtailadmin/shared/header.html +0 -5
  94. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +1 -1
  95. wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +4 -1
  96. wagtail/admin/templates/wagtailadmin/shared/pagination_nav.html +1 -1
  97. wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +1 -1
  98. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/locale.html +1 -1
  99. wagtail/admin/templates/wagtailadmin/shared/side_panels/includes/status/usage.html +2 -2
  100. wagtail/admin/templates/wagtailadmin/shared/side_panels/preview.html +94 -52
  101. wagtail/admin/templates/wagtailadmin/{pages/_unsaved_changes_warning.html → shared/unsaved_changes_warning.html} +4 -4
  102. wagtail/admin/templates/wagtailadmin/shared/usage_summary.html +2 -2
  103. wagtail/admin/templates/wagtailadmin/shared/workflow_history/detail.html +1 -7
  104. wagtail/admin/templates/wagtailadmin/shared/workflow_history/listing.html +1 -0
  105. wagtail/admin/templates/wagtailadmin/shared/workflow_history/{list.html → listing_results.html} +33 -35
  106. wagtail/admin/templates/wagtailadmin/tables/references_cell.html +1 -1
  107. wagtail/admin/templates/wagtailadmin/workflows/create.html +11 -36
  108. wagtail/admin/templates/wagtailadmin/workflows/create_task.html +0 -38
  109. wagtail/admin/templates/wagtailadmin/workflows/edit.html +44 -74
  110. wagtail/admin/templates/wagtailadmin/workflows/edit_task.html +27 -66
  111. wagtail/admin/templates/wagtailadmin/workflows/includes/task_usage_cell.html +4 -2
  112. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_tasks_cell.html +4 -4
  113. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_used_by_cell.html +15 -11
  114. wagtail/admin/templates/wagtailadmin/workflows/task_chooser/includes/results.html +7 -5
  115. wagtail/admin/templatetags/wagtailadmin_tags.py +51 -22
  116. wagtail/admin/tests/pages/test_content_type_use_view.py +82 -2
  117. wagtail/admin/tests/pages/test_edit_page.py +70 -0
  118. wagtail/admin/tests/pages/test_explorer_view.py +65 -4
  119. wagtail/admin/tests/pages/test_page_search.py +8 -7
  120. wagtail/admin/tests/pages/test_page_usage.py +3 -1
  121. wagtail/admin/tests/pages/test_preview.py +208 -63
  122. wagtail/admin/tests/pages/test_reorder_page.py +91 -1
  123. wagtail/admin/tests/pages/test_view_draft.py +32 -1
  124. wagtail/admin/tests/pages/test_workflow_history.py +288 -7
  125. wagtail/admin/tests/test_account_management.py +23 -3
  126. wagtail/admin/tests/test_audit_log.py +24 -2
  127. wagtail/admin/tests/test_collections_views.py +17 -5
  128. wagtail/admin/tests/test_dashboard.py +1 -1
  129. wagtail/admin/tests/test_edit_handlers.py +3 -2
  130. wagtail/admin/tests/test_icon_sprite.py +4 -0
  131. wagtail/admin/tests/test_privacy.py +5 -19
  132. wagtail/admin/tests/test_reports_views.py +1 -1
  133. wagtail/admin/tests/test_site_summary.py +3 -3
  134. wagtail/admin/tests/test_templatetags.py +27 -3
  135. wagtail/admin/tests/test_upgrade_notification.py +71 -18
  136. wagtail/admin/tests/test_views.py +2 -2
  137. wagtail/admin/tests/test_views_generic.py +13 -0
  138. wagtail/admin/tests/test_widgets.py +3 -3
  139. wagtail/admin/tests/test_workflows.py +172 -27
  140. wagtail/admin/tests/tests.py +1 -1
  141. wagtail/admin/tests/viewsets/test_model_viewset.py +55 -3
  142. wagtail/admin/ui/side_panels.py +19 -0
  143. wagtail/admin/ui/sidebar.py +4 -3
  144. wagtail/admin/ui/tables/__init__.py +14 -1
  145. wagtail/admin/ui/tables/pages.py +6 -1
  146. wagtail/admin/urls/pages.py +10 -1
  147. wagtail/admin/urls/workflows.py +6 -1
  148. wagtail/admin/views/account.py +5 -1
  149. wagtail/admin/views/collections.py +0 -2
  150. wagtail/admin/views/generic/base.py +118 -1
  151. wagtail/admin/views/generic/history.py +158 -26
  152. wagtail/admin/views/generic/models.py +113 -99
  153. wagtail/admin/views/home.py +24 -9
  154. wagtail/admin/views/page_privacy.py +54 -30
  155. wagtail/admin/views/pages/history.py +11 -4
  156. wagtail/admin/views/pages/listing.py +76 -89
  157. wagtail/admin/views/pages/preview.py +1 -1
  158. wagtail/admin/views/pages/search.py +27 -71
  159. wagtail/admin/views/pages/usage.py +63 -24
  160. wagtail/admin/views/pages/utils.py +4 -3
  161. wagtail/admin/views/reports/base.py +0 -6
  162. wagtail/admin/views/workflows.py +67 -25
  163. wagtail/admin/viewsets/model.py +4 -3
  164. wagtail/admin/widgets/chooser.py +2 -1
  165. wagtail/api/v2/tests/test_pages.py +15 -2
  166. wagtail/blocks/field_block.py +15 -3
  167. wagtail/blocks/static_block.py +3 -0
  168. wagtail/contrib/forms/locale/cy/LC_MESSAGES/django.mo +0 -0
  169. wagtail/contrib/forms/locale/cy/LC_MESSAGES/django.po +29 -1
  170. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +15 -11
  171. wagtail/contrib/forms/templates/wagtailforms/confirm_delete.html +1 -0
  172. wagtail/contrib/forms/templates/wagtailforms/index.html +1 -1
  173. wagtail/contrib/forms/templates/wagtailforms/index_results.html +1 -1
  174. wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +2 -1
  175. wagtail/contrib/forms/templates/wagtailforms/panels/form_responses_panel.html +2 -2
  176. wagtail/contrib/forms/tests/test_views.py +234 -35
  177. wagtail/contrib/forms/utils.py +8 -14
  178. wagtail/contrib/forms/views.py +72 -27
  179. wagtail/contrib/frontend_cache/backends/azure.py +1 -1
  180. wagtail/contrib/frontend_cache/backends/cloudfront.py +2 -2
  181. wagtail/contrib/frontend_cache/backends/dummy.py +11 -0
  182. wagtail/contrib/frontend_cache/tests.py +31 -37
  183. wagtail/contrib/frontend_cache/utils.py +12 -5
  184. wagtail/contrib/redirects/locale/cy/LC_MESSAGES/django.mo +0 -0
  185. wagtail/contrib/redirects/locale/cy/LC_MESSAGES/django.po +16 -1
  186. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +20 -24
  187. wagtail/contrib/redirects/models.py +16 -1
  188. wagtail/contrib/redirects/signal_handlers.py +18 -3
  189. wagtail/contrib/redirects/templates/wagtailredirects/add.html +5 -16
  190. wagtail/contrib/redirects/templates/wagtailredirects/import_summary.html +1 -1
  191. wagtail/contrib/redirects/tests/test_redirects.py +49 -6
  192. wagtail/contrib/redirects/tests/test_signal_handlers.py +42 -1
  193. wagtail/contrib/redirects/views.py +27 -3
  194. wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.mo +0 -0
  195. wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.po +60 -1
  196. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +12 -18
  197. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/add.html +5 -8
  198. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/edit.html +15 -10
  199. wagtail/contrib/search_promotions/tests.py +13 -2
  200. wagtail/contrib/search_promotions/views.py +15 -3
  201. wagtail/contrib/settings/locale/cy/LC_MESSAGES/django.mo +0 -0
  202. wagtail/contrib/settings/locale/cy/LC_MESSAGES/django.po +6 -1
  203. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +2 -10
  204. wagtail/contrib/settings/templates/wagtailsettings/edit.html +12 -45
  205. wagtail/contrib/simple_translation/locale/cy/LC_MESSAGES/django.mo +0 -0
  206. wagtail/contrib/simple_translation/locale/cy/LC_MESSAGES/django.po +13 -1
  207. wagtail/contrib/simple_translation/locale/dv/LC_MESSAGES/django.mo +0 -0
  208. wagtail/contrib/simple_translation/locale/dv/LC_MESSAGES/django.po +3 -0
  209. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +4 -4
  210. wagtail/contrib/simple_translation/locale/sl/LC_MESSAGES/django.mo +0 -0
  211. wagtail/contrib/simple_translation/locale/sl/LC_MESSAGES/django.po +5 -2
  212. wagtail/contrib/simple_translation/tests/test_views.py +51 -0
  213. wagtail/contrib/simple_translation/wagtail_hooks.py +16 -11
  214. wagtail/contrib/styleguide/locale/cy/LC_MESSAGES/django.mo +0 -0
  215. wagtail/contrib/styleguide/locale/cy/LC_MESSAGES/django.po +5 -1
  216. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
  217. wagtail/contrib/table_block/locale/cy/LC_MESSAGES/django.mo +0 -0
  218. wagtail/contrib/table_block/locale/cy/LC_MESSAGES/django.po +27 -1
  219. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  220. wagtail/contrib/typed_table_block/blocks.py +25 -0
  221. wagtail/contrib/typed_table_block/locale/cy/LC_MESSAGES/django.mo +0 -0
  222. wagtail/contrib/typed_table_block/locale/cy/LC_MESSAGES/django.po +12 -1
  223. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  224. wagtail/contrib/typed_table_block/tests.py +24 -1
  225. wagtail/coreutils.py +5 -11
  226. wagtail/documents/admin_urls.py +2 -2
  227. wagtail/documents/locale/cy/LC_MESSAGES/django.mo +0 -0
  228. wagtail/documents/locale/cy/LC_MESSAGES/django.po +10 -0
  229. wagtail/documents/locale/en/LC_MESSAGES/django.po +61 -76
  230. wagtail/documents/migrations/0014_alter_document_file_size.py +18 -0
  231. wagtail/documents/models.py +1 -1
  232. wagtail/documents/rich_text/__init__.py +1 -3
  233. wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
  234. wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_add_tags.html +5 -1
  235. wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_add_to_collection.html +5 -1
  236. wagtail/documents/templates/wagtaildocs/bulk_actions/confirm_bulk_delete.html +11 -5
  237. wagtail/documents/templates/wagtaildocs/documents/add.html +13 -41
  238. wagtail/documents/templates/wagtaildocs/documents/edit.html +28 -56
  239. wagtail/documents/templates/wagtaildocs/documents/index.html +1 -4
  240. wagtail/documents/templates/wagtaildocs/homepage/site_summary_documents.html +2 -2
  241. wagtail/documents/templates/wagtaildocs/multiple/add.html +36 -41
  242. wagtail/documents/tests/test_admin_views.py +64 -6
  243. wagtail/documents/tests/test_site_summary.py +3 -3
  244. wagtail/documents/views/documents.py +103 -113
  245. wagtail/documents/views/multiple.py +19 -1
  246. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  247. wagtail/embeds/oembed_providers.py +9 -19
  248. wagtail/fields.py +43 -27
  249. wagtail/images/admin_urls.py +2 -2
  250. wagtail/images/blocks.py +230 -2
  251. wagtail/images/fields.py +17 -29
  252. wagtail/images/image_operations.py +1 -1
  253. wagtail/images/locale/cy/LC_MESSAGES/django.mo +0 -0
  254. wagtail/images/locale/cy/LC_MESSAGES/django.po +128 -1
  255. wagtail/images/locale/en/LC_MESSAGES/django.po +119 -129
  256. wagtail/images/migrations/0025_alter_image_file_alter_rendition_file.py +36 -43
  257. wagtail/images/migrations/0027_image_description.py +20 -0
  258. wagtail/images/models.py +120 -45
  259. wagtail/images/rich_text/__init__.py +1 -3
  260. wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
  261. wagtail/images/static/wagtailimages/js/image-block.js +1 -0
  262. wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_add_tags.html +5 -1
  263. wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_add_to_collection.html +5 -1
  264. wagtail/images/templates/wagtailimages/bulk_actions/confirm_bulk_delete.html +11 -5
  265. wagtail/images/templates/wagtailimages/chooser/results.html +2 -2
  266. wagtail/images/templates/wagtailimages/homepage/site_summary_images.html +2 -2
  267. wagtail/images/templates/wagtailimages/images/_file_field.html +2 -2
  268. wagtail/images/templates/wagtailimages/images/add.html +13 -31
  269. wagtail/images/templates/wagtailimages/images/edit.html +53 -82
  270. wagtail/images/templates/wagtailimages/images/index.html +1 -4
  271. wagtail/images/templates/wagtailimages/images/url_generator.html +1 -1
  272. wagtail/images/templates/wagtailimages/multiple/add.html +40 -47
  273. wagtail/images/templates/wagtailimages/widgets/image.html +5 -0
  274. wagtail/images/templates/wagtailimages/widgets/image_chooser.html +1 -1
  275. wagtail/images/tests/test_admin_views.py +70 -10
  276. wagtail/images/tests/test_blocks.py +367 -1
  277. wagtail/images/tests/test_image_operations.py +23 -0
  278. wagtail/images/tests/test_models.py +20 -0
  279. wagtail/images/tests/test_signal_handlers.py +99 -95
  280. wagtail/images/tests/test_site_summary.py +3 -3
  281. wagtail/images/tests/test_templatetags.py +11 -7
  282. wagtail/images/tests/tests.py +4 -0
  283. wagtail/images/views/images.py +103 -104
  284. wagtail/images/views/multiple.py +17 -1
  285. wagtail/locale/cy/LC_MESSAGES/django.mo +0 -0
  286. wagtail/locale/cy/LC_MESSAGES/django.po +3 -0
  287. wagtail/locale/en/LC_MESSAGES/django.po +137 -125
  288. wagtail/locale/sl/LC_MESSAGES/django.mo +0 -0
  289. wagtail/locale/sl/LC_MESSAGES/django.po +3 -0
  290. wagtail/locales/locale/en/LC_MESSAGES/django.po +8 -8
  291. wagtail/locales/views.py +4 -1
  292. wagtail/migrations/0089_log_entry_data_json_null_to_object.py +1 -7
  293. wagtail/models/__init__.py +122 -14
  294. wagtail/models/audit_log.py +1 -3
  295. wagtail/models/i18n.py +1 -2
  296. wagtail/project_template/Dockerfile +3 -3
  297. wagtail/project_template/requirements.txt +2 -2
  298. wagtail/query.py +17 -4
  299. wagtail/rich_text/__init__.py +2 -3
  300. wagtail/rich_text/pages.py +2 -4
  301. wagtail/rich_text/rewriters.py +2 -2
  302. wagtail/search/backends/database/mysql/query.py +3 -3
  303. wagtail/search/backends/database/sqlite/query.py +6 -6
  304. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  305. wagtail/sites/locale/en/LC_MESSAGES/django.po +6 -6
  306. wagtail/sites/views.py +0 -1
  307. wagtail/snippets/action_menu.py +14 -4
  308. wagtail/snippets/locale/cy/LC_MESSAGES/django.mo +0 -0
  309. wagtail/snippets/locale/cy/LC_MESSAGES/django.po +73 -1
  310. wagtail/snippets/locale/en/LC_MESSAGES/django.po +27 -33
  311. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  312. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  313. wagtail/snippets/templates/wagtailsnippets/bulk_actions/confirm_bulk_delete.html +5 -3
  314. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/locked.html +1 -1
  315. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu.html +1 -11
  316. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu_item.html +1 -6
  317. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/publish.html +1 -1
  318. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/save.html +1 -1
  319. wagtail/snippets/templates/wagtailsnippets/snippets/create.html +1 -18
  320. wagtail/snippets/templates/wagtailsnippets/snippets/edit.html +1 -14
  321. wagtail/snippets/templates/wagtailsnippets/snippets/index.html +2 -5
  322. wagtail/snippets/tests/test_preview.py +193 -61
  323. wagtail/snippets/tests/test_snippets.py +247 -38
  324. wagtail/snippets/tests/test_usage.py +5 -0
  325. wagtail/snippets/tests/test_viewset.py +25 -9
  326. wagtail/snippets/tests/test_workflows.py +232 -19
  327. wagtail/snippets/views/snippets.py +1 -10
  328. wagtail/test/numberformat.py +104 -0
  329. wagtail/test/settings.py +10 -0
  330. wagtail/test/settings_ui.py +2 -0
  331. wagtail/test/testapp/migrations/0010_alter_customimage_file_and_more.py +71 -78
  332. wagtail/test/testapp/migrations/0040_nocreatablesubpagetypespage_nosubpagetypespage.py +54 -0
  333. wagtail/test/testapp/migrations/0041_alter_jsonstreammodel_options.py +17 -0
  334. wagtail/test/testapp/migrations/0042_alter_customdocument_file_size_and_more.py +28 -0
  335. wagtail/test/testapp/migrations/0043_customimage_description.py +41 -0
  336. wagtail/test/testapp/migrations/0044_custompreviewsizesmodel_custompreviewsizespage.py +52 -0
  337. wagtail/test/testapp/migrations/0045_alter_streampage_body.py +52 -0
  338. wagtail/test/testapp/models.py +62 -4
  339. wagtail/test/testapp/rich_text.py +2 -2
  340. wagtail/test/testapp/templates/tests/form_page_landing.html +2 -1
  341. wagtail/test/testapp/urls.py +5 -0
  342. wagtail/test/testapp/views.py +5 -0
  343. wagtail/test/utils/page_tests.py +5 -5
  344. wagtail/test/utils/template_tests.py +2 -2
  345. wagtail/tests/test_blocks.py +15 -0
  346. wagtail/tests/test_page_permissions.py +109 -0
  347. wagtail/tests/test_revision_model.py +27 -0
  348. wagtail/tests/test_signals.py +21 -2
  349. wagtail/tests/test_tests.py +30 -0
  350. wagtail/users/locale/cy/LC_MESSAGES/django.mo +0 -0
  351. wagtail/users/locale/cy/LC_MESSAGES/django.po +118 -1
  352. wagtail/users/locale/dv/LC_MESSAGES/django.mo +0 -0
  353. wagtail/users/locale/dv/LC_MESSAGES/django.po +3 -0
  354. wagtail/users/locale/en/LC_MESSAGES/django.po +89 -113
  355. wagtail/users/locale/sl/LC_MESSAGES/django.mo +0 -0
  356. wagtail/users/locale/sl/LC_MESSAGES/django.po +3 -0
  357. wagtail/users/migrations/0014_userprofile_contrast.py +23 -0
  358. wagtail/users/models.py +11 -0
  359. wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_assign_role.html +5 -1
  360. wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_delete.html +5 -1
  361. wagtail/users/templates/wagtailusers/bulk_actions/confirm_bulk_set_active_state.html +5 -1
  362. wagtail/users/templates/wagtailusers/groups/create.html +19 -17
  363. wagtail/users/templates/wagtailusers/groups/edit.html +2 -40
  364. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +1 -62
  365. wagtail/users/templates/wagtailusers/groups/includes/page_permissions_formset.html +0 -1
  366. wagtail/users/templates/wagtailusers/users/create.html +46 -50
  367. wagtail/users/templates/wagtailusers/users/edit.html +45 -62
  368. wagtail/users/templates/wagtailusers/users/index.html +1 -4
  369. wagtail/users/templatetags/wagtailusers_tags.py +1 -5
  370. wagtail/users/tests/test_admin_views.py +85 -66
  371. wagtail/users/views/groups.py +4 -1
  372. wagtail/users/views/users.py +2 -0
  373. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/METADATA +6 -6
  374. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/RECORD +378 -367
  375. wagtail/admin/static/wagtailadmin/js/preview-panel.js +0 -2
  376. wagtail/admin/static/wagtailadmin/js/preview-panel.js.LICENSE.txt +0 -11
  377. wagtail/admin/templates/wagtailadmin/generic/history_results.html +0 -1
  378. wagtail/admin/templates/wagtailadmin/page_privacy/ancestor_privacy.html +0 -3
  379. wagtail/admin/templates/wagtailadmin/pages/usage_results.html +0 -6
  380. wagtail/admin/templates/wagtailadmin/shared/workflow_history/index.html +0 -17
  381. wagtail/admin/templates/wagtailadmin/shared/workflow_history/results.html +0 -17
  382. wagtail/admin/templates/wagtailadmin/workflows/usage.html +0 -44
  383. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/LICENSE +0 -0
  384. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/WHEEL +0 -0
  385. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/entry_points.txt +0 -0
  386. {wagtail-6.2.2.dist-info → wagtail-6.3rc2.dist-info}/top_level.txt +0 -0
@@ -40,8 +40,8 @@ class TestModelViewSetGroup(WagtailTestUtils, TestCase):
40
40
  response,
41
41
  '"name": "tests", "label": "Tests", "icon_name": "folder-open-inverse"',
42
42
  )
43
- # Title-cased from verbose_name_plural
44
- self.assertContains(response, "Json Stream Models")
43
+ # Capitalized-first from verbose_name_plural
44
+ self.assertContains(response, "JSON stream models")
45
45
  self.assertContains(response, reverse("streammodel:index"))
46
46
  self.assertEqual(reverse("streammodel:index"), "/admin/streammodel/")
47
47
  # Set on class
@@ -81,7 +81,7 @@ class TestModelViewSetGroup(WagtailTestUtils, TestCase):
81
81
  )
82
82
 
83
83
  # The menu item for the model is shown
84
- self.assertContains(response, "Json Stream Models")
84
+ self.assertContains(response, "JSON stream models")
85
85
  self.assertContains(response, reverse("streammodel:index"))
86
86
  self.assertEqual(reverse("streammodel:index"), "/admin/streammodel/")
87
87
 
@@ -451,6 +451,7 @@ class TestSearchIndexView(WagtailTestUtils, TestCase):
451
451
 
452
452
  def test_search_disabled(self):
453
453
  response = self.get("fctoy_alt1", {"q": "ork"})
454
+ self.assertFalse(response.context.get("search_form"))
454
455
  self.assertContains(response, "Forky")
455
456
  self.assertContains(response, "Buzz Lightyear")
456
457
  self.assertNotContains(response, "There are 2 matches")
@@ -1043,6 +1044,11 @@ class TestHistoryView(WagtailTestUtils, TestCase):
1043
1044
  for rendered_row, expected_row in zip(rendered_rows, expected):
1044
1045
  self.assertSequenceEqual(rendered_row, expected_row)
1045
1046
 
1047
+ # History view is not searchable
1048
+ input = soup.select_one("input#id_q")
1049
+ self.assertIsNone(input)
1050
+ self.assertFalse(response.context.get("search_form"))
1051
+
1046
1052
  def test_action_filter(self):
1047
1053
  response = self.client.get(self.url)
1048
1054
  self.assertEqual(response.status_code, 200)
@@ -1235,12 +1241,22 @@ class TestUsageView(WagtailTestUtils, TestCase):
1235
1241
  link = tds[0].select_one("a")
1236
1242
  self.assertIsNotNone(link)
1237
1243
  self.assertEqual(link.attrs.get("href"), tbx_edit_url)
1244
+ content_path_link = tds[-1].select_one("a")
1245
+ self.assertEqual(
1246
+ content_path_link.attrs.get("href"),
1247
+ tbx_edit_url + "#:w:contentpath=cascading_toy",
1248
+ )
1238
1249
 
1239
1250
  # Link to referrer's edit view with parameters for the specific field
1240
1251
  link = tds[2].select_one("a")
1241
1252
  self.assertIsNotNone(link)
1242
1253
  self.assertIn(tbx_edit_url, link.attrs.get("href"))
1243
1254
 
1255
+ # Usage view is not searchable
1256
+ input = soup.select_one("input#id_q")
1257
+ self.assertIsNone(input)
1258
+ self.assertFalse(response.context.get("search_form"))
1259
+
1244
1260
  def test_usage_without_permission(self):
1245
1261
  self.user.is_superuser = False
1246
1262
  self.user.save()
@@ -1817,3 +1833,39 @@ class TestDefaultMessages(WagtailTestUtils, TestCase):
1817
1833
  response,
1818
1834
  escape(f"Feature complete toy '{self.object}' deleted."),
1819
1835
  )
1836
+
1837
+
1838
+ class TestHeaderButtons(WagtailTestUtils, TestCase):
1839
+ def setUp(self):
1840
+ self.user = self.login()
1841
+
1842
+ @classmethod
1843
+ def setUpTestData(cls):
1844
+ cls.object = FeatureCompleteToy.objects.create(name="Test Toy")
1845
+ cls.edit_url = reverse(
1846
+ "feature_complete_toy:edit", args=(quote(cls.object.pk),)
1847
+ )
1848
+ cls.copy_url = reverse(
1849
+ "feature_complete_toy:copy", args=(quote(cls.object.pk),)
1850
+ )
1851
+ cls.delete_url = reverse(
1852
+ "feature_complete_toy:delete", args=(quote(cls.object.pk),)
1853
+ )
1854
+ cls.inspect_url = reverse(
1855
+ "feature_complete_toy:inspect", args=(quote(cls.object.pk),)
1856
+ )
1857
+
1858
+ def test_header_buttons_in_edit_view(self):
1859
+ response = self.client.get(self.edit_url)
1860
+ self.assertEqual(response.status_code, 200)
1861
+ soup = self.get_soup(response.content)
1862
+ header_buttons = soup.select(".w-slim-header .w-dropdown a")
1863
+ expected_buttons = [
1864
+ ("Copy", self.copy_url),
1865
+ ("Delete", self.delete_url),
1866
+ ("Inspect", self.inspect_url),
1867
+ ]
1868
+ self.assertEqual(
1869
+ [(a.text.strip(), a.get("href")) for a in header_buttons],
1870
+ expected_buttons,
1871
+ )
@@ -1,3 +1,6 @@
1
+ import warnings
2
+
3
+ from django.conf import settings
1
4
  from django.urls import reverse
2
5
  from django.utils.text import capfirst
3
6
  from django.utils.translation import gettext_lazy, ngettext
@@ -6,6 +9,7 @@ from wagtail import hooks
6
9
  from wagtail.admin.ui.components import Component
7
10
  from wagtail.admin.userbar import AccessibilityItem
8
11
  from wagtail.models import DraftStateMixin, LockableMixin, Page, ReferenceIndex
12
+ from wagtail.utils.deprecation import RemovedInWagtail70Warning
9
13
 
10
14
 
11
15
  class BaseSidePanel(Component):
@@ -350,8 +354,23 @@ class PreviewSidePanel(BaseSidePanel):
350
354
  super().__init__(object, request)
351
355
  self.preview_url = preview_url
352
356
 
357
+ @property
358
+ def auto_update_interval(self):
359
+ if hasattr(settings, "WAGTAIL_AUTO_UPDATE_PREVIEW"):
360
+ warnings.warn(
361
+ "`WAGTAIL_AUTO_UPDATE_PREVIEW` is deprecated. "
362
+ "Set `WAGTAIL_AUTO_UPDATE_PREVIEW_INTERVAL = 0` to disable "
363
+ "auto-update for previews.",
364
+ RemovedInWagtail70Warning,
365
+ )
366
+ if not settings.WAGTAIL_AUTO_UPDATE_PREVIEW:
367
+ return 0
368
+
369
+ return getattr(settings, "WAGTAIL_AUTO_UPDATE_PREVIEW_INTERVAL", 500)
370
+
353
371
  def get_context_data(self, parent_context):
354
372
  context = super().get_context_data(parent_context)
355
373
  context["preview_url"] = self.preview_url
356
374
  context["has_multiple_modes"] = len(self.object.preview_modes) > 1
375
+ context["auto_update_interval"] = self.auto_update_interval
357
376
  return context
@@ -1,4 +1,5 @@
1
- from typing import Any, List, Mapping
1
+ from collections.abc import Mapping
2
+ from typing import Any
2
3
  from warnings import warn
3
4
 
4
5
  from django import forms
@@ -152,7 +153,7 @@ class SubMenuItem(MenuItem):
152
153
  self,
153
154
  name: str,
154
155
  label: str,
155
- menu_items: List[MenuItem],
156
+ menu_items: list[MenuItem],
156
157
  icon_name: str = "",
157
158
  classname: str = "",
158
159
  classnames: str = "",
@@ -262,7 +263,7 @@ class SearchModule:
262
263
  @adapter("wagtail.sidebar.MainMenuModule", base=BaseSidebarAdapter)
263
264
  class MainMenuModule:
264
265
  def __init__(
265
- self, menu_items: List[MenuItem], account_menu_items: List[MenuItem], user
266
+ self, menu_items: list[MenuItem], account_menu_items: list[MenuItem], user
266
267
  ):
267
268
  self.menu_items = menu_items
268
269
  self.account_menu_items = account_menu_items
@@ -6,6 +6,7 @@ from collections.abc import Mapping
6
6
  from django.contrib.admin.utils import quote
7
7
  from django.forms import MediaDefiningClass
8
8
  from django.template.loader import get_template
9
+ from django.templatetags.l10n import unlocalize
9
10
  from django.urls import reverse
10
11
  from django.utils.functional import cached_property
11
12
  from django.utils.text import capfirst
@@ -156,7 +157,13 @@ class Column(BaseColumn):
156
157
 
157
158
  def get_cell_context_data(self, instance, parent_context):
158
159
  context = super().get_cell_context_data(instance, parent_context)
159
- context["value"] = self.get_value(instance)
160
+ value = self.get_value(instance)
161
+ if isinstance(value, int) and not isinstance(value, bool):
162
+ # To prevent errors arising from USE_THOUSAND_SEPARATOR, we require all numbers output
163
+ # on templates to be explicitly localized or unlocalized. For numeric table cells, we
164
+ # unlocalize them by default; developers may subclass Column to obtain formatted numbers.
165
+ value = unlocalize(value)
166
+ context["value"] = value
160
167
  return context
161
168
 
162
169
 
@@ -272,6 +279,12 @@ class BooleanColumn(Column):
272
279
 
273
280
  cell_template_name = "wagtailadmin/tables/boolean_cell.html"
274
281
 
282
+ def get_value(self, instance):
283
+ value = super().get_value(instance)
284
+ if value is None:
285
+ return None
286
+ return bool(value)
287
+
275
288
 
276
289
  class LiveStatusTagColumn(StatusTagColumn):
277
290
  """Represents a live/draft status tag"""
@@ -56,6 +56,8 @@ class ParentPageColumn(Column):
56
56
  cell_template_name = "wagtailadmin/pages/listing/_parent_page_cell.html"
57
57
 
58
58
  def get_value(self, instance):
59
+ if parent := getattr(instance, "_parent_page", None):
60
+ return parent
59
61
  return instance.get_parent()
60
62
 
61
63
 
@@ -92,7 +94,10 @@ class NavigateToChildrenColumn(BaseColumn):
92
94
  return context
93
95
 
94
96
  def render_header_html(self, parent_context):
95
- return mark_safe("<th></th>")
97
+ # This column has no header, as the cell's function will vary between "explore child pages"
98
+ # and "add child page", and this link provides all the signposting needed. Render it as a
99
+ # <td> rather than <th> as headings cannot be empty (https://dequeuniversity.com/rules/axe/4.9/empty-table-header).
100
+ return mark_safe("<td></td>")
96
101
 
97
102
 
98
103
  class PageTable(Table):
@@ -36,6 +36,11 @@ urlpatterns = [
36
36
  usage.ContentTypeUseView.as_view(),
37
37
  name="type_use",
38
38
  ),
39
+ path(
40
+ "usage/<slug:content_type_app_name>/<slug:content_type_model_name>/results/",
41
+ usage.ContentTypeUseView.as_view(results_only=True),
42
+ name="type_use_results",
43
+ ),
39
44
  path("<int:page_id>/usage/", usage.UsageView.as_view(), name="usage"),
40
45
  path("<int:page_id>/edit/", edit.EditView.as_view(), name="edit"),
41
46
  path(
@@ -53,7 +58,11 @@ urlpatterns = [
53
58
  name="convert_alias",
54
59
  ),
55
60
  path("search/", search.SearchView.as_view(), name="search"),
56
- path("search/results/", search.SearchResultsView.as_view(), name="search_results"),
61
+ path(
62
+ "search/results/",
63
+ search.SearchView.as_view(results_only=True),
64
+ name="search_results",
65
+ ),
57
66
  path("<int:page_to_move_id>/move/", move.move_choose_destination, name="move"),
58
67
  path(
59
68
  "<int:page_to_move_id>/move/<int:destination_id>/confirm/",
@@ -14,7 +14,12 @@ urlpatterns = [
14
14
  path("enable/<int:pk>/", workflows.enable_workflow, name="enable"),
15
15
  path("disable/<int:pk>/", workflows.Disable.as_view(), name="disable"),
16
16
  path("edit/<int:pk>/", workflows.Edit.as_view(), name="edit"),
17
- path("usage/<int:pk>/", workflows.usage, name="usage"),
17
+ path("usage/<int:pk>/", workflows.WorkflowUsageView.as_view(), name="usage"),
18
+ path(
19
+ "usage/<int:pk>/results/",
20
+ workflows.WorkflowUsageView.as_view(results_only=True),
21
+ name="usage_results",
22
+ ),
18
23
  path("remove/<int:page_pk>/", workflows.remove_workflow, name="remove"),
19
24
  path(
20
25
  "remove/<int:page_pk>/<int:workflow_pk>/",
@@ -31,7 +31,7 @@ from wagtail.admin.localization import (
31
31
  get_available_admin_languages,
32
32
  get_available_admin_time_zones,
33
33
  )
34
- from wagtail.admin.views.generic import WagtailAdminTemplateMixin
34
+ from wagtail.admin.views.generic import EditView, WagtailAdminTemplateMixin
35
35
  from wagtail.log_actions import log
36
36
  from wagtail.users.models import UserProfile
37
37
  from wagtail.utils.loading import get_custom_form
@@ -239,6 +239,10 @@ class AccountView(WagtailAdminTemplateMixin, TemplateView):
239
239
  context["menu_items"] = self.get_menu_items()
240
240
  context["media"] = self.get_media(panels)
241
241
  context["user"] = self.request.user
242
+ # Remove these when this view is refactored to a generic.EditView subclass.
243
+ # Avoid defining new translatable strings.
244
+ context["submit_button_label"] = EditView.submit_button_label
245
+ context["submit_button_active_label"] = EditView.submit_button_active_label
242
246
  return context
243
247
 
244
248
  def get_panels(self):
@@ -30,7 +30,6 @@ class Index(IndexView):
30
30
  accessor="1",
31
31
  )
32
32
  ]
33
- _show_breadcrumbs = True
34
33
 
35
34
  def get_queryset(self):
36
35
  return self.permission_policy.instances_user_has_any_permission_for(
@@ -76,7 +75,6 @@ class Edit(EditView):
76
75
  template_name = "wagtailadmin/collections/edit.html"
77
76
  success_message = gettext_lazy("Collection '%(object)s' updated.")
78
77
  error_message = gettext_lazy("The collection could not be saved due to errors.")
79
- delete_item_label = gettext_lazy("Delete collection")
80
78
  edit_url_name = "wagtailadmin_collections:edit"
81
79
  index_url_name = "wagtailadmin_collections:index"
82
80
  delete_url_name = "wagtailadmin_collections:delete"
@@ -1,8 +1,10 @@
1
+ import warnings
1
2
  from collections import namedtuple
2
3
 
3
4
  from django.contrib.admin.utils import quote, unquote
4
5
  from django.core.exceptions import ImproperlyConfigured
5
6
  from django.db import models
7
+ from django.db.models import Q
6
8
  from django.shortcuts import get_object_or_404, redirect
7
9
  from django.urls import reverse, reverse_lazy
8
10
  from django.utils.formats import date_format
@@ -20,9 +22,12 @@ from django_filters.filters import (
20
22
  )
21
23
 
22
24
  from wagtail.admin import messages
25
+ from wagtail.admin.forms.search import SearchForm
23
26
  from wagtail.admin.ui.tables import Column, Table
24
27
  from wagtail.admin.utils import get_valid_next_url_from_request
25
28
  from wagtail.admin.widgets.button import ButtonWithDropdown
29
+ from wagtail.search.backends import get_search_backend
30
+ from wagtail.search.index import class_is_indexed
26
31
  from wagtail.utils.utils import flatten_choices
27
32
 
28
33
 
@@ -191,8 +196,14 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
191
196
  index_url_name = None
192
197
  index_results_url_name = None
193
198
  page_kwarg = "p"
199
+ is_searchable = None # Subclasses must explicitly set this to True to enable search
200
+ search_kwarg = "q"
201
+ search_fields = None
202
+ search_backend_name = "default"
194
203
  default_ordering = None
195
204
  filterset_class = None
205
+ verbose_name_plural = None
206
+ _show_breadcrumbs = True
196
207
 
197
208
  def get_template_names(self):
198
209
  if self.results_only:
@@ -202,6 +213,74 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
202
213
  else:
203
214
  return super().get_template_names()
204
215
 
216
+ def get_breadcrumbs_items(self):
217
+ return self.breadcrumbs_items + [
218
+ {
219
+ "url": "",
220
+ "label": self.get_page_title(),
221
+ "sublabel": self.get_page_subtitle(),
222
+ },
223
+ ]
224
+
225
+ def get_search_form(self):
226
+ if not self.is_searchable:
227
+ return None
228
+
229
+ if self.search_kwarg in self.request.GET:
230
+ return SearchForm(self.request.GET)
231
+
232
+ return SearchForm()
233
+
234
+ @cached_property
235
+ def search_form(self):
236
+ return self.get_search_form()
237
+
238
+ @cached_property
239
+ def search_query(self):
240
+ if self.search_form and self.search_form.is_valid():
241
+ return self.search_form.cleaned_data[self.search_kwarg]
242
+ return ""
243
+
244
+ @cached_property
245
+ def is_searching(self):
246
+ return bool(self.search_query)
247
+
248
+ def search_queryset(self, queryset):
249
+ if not self.is_searching:
250
+ return queryset
251
+
252
+ # Use Wagtail Search if the model is indexed and a search backend is defined.
253
+ # Django ORM can still be used on an indexed model by unsetting
254
+ # search_backend_name and defining search_fields on the view.
255
+ if class_is_indexed(queryset.model) and self.search_backend_name:
256
+ search_backend = get_search_backend(self.search_backend_name)
257
+ if queryset.model.get_autocomplete_search_fields():
258
+ return search_backend.autocomplete(
259
+ self.search_query,
260
+ queryset,
261
+ fields=self.search_fields,
262
+ order_by_relevance=(not self.is_explicitly_ordered),
263
+ )
264
+ else:
265
+ # fall back on non-autocompleting search
266
+ warnings.warn(
267
+ f"{queryset.model} is defined as Indexable but does not specify "
268
+ "any AutocompleteFields. Searches within the admin will only "
269
+ "respond to complete words.",
270
+ category=RuntimeWarning,
271
+ )
272
+ return search_backend.search(
273
+ self.search_query,
274
+ queryset,
275
+ fields=self.search_fields,
276
+ order_by_relevance=(not self.is_explicitly_ordered),
277
+ )
278
+
279
+ query = Q()
280
+ for field in self.search_fields or []:
281
+ query |= Q(**{field + "__icontains": self.search_query})
282
+ return queryset.filter(query)
283
+
205
284
  @cached_property
206
285
  def filters(self):
207
286
  if self.filterset_class:
@@ -317,7 +396,7 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
317
396
  ActiveFilter(
318
397
  bound_field.auto_id,
319
398
  filter_def.label,
320
- "%s - %s" % (start_date_display, end_date_display),
399
+ f"{start_date_display} - {end_date_display}",
321
400
  self.get_url_without_filter_param(
322
401
  [
323
402
  widget.suffixed(field_name, suffix)
@@ -405,8 +484,21 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
405
484
  queryset = self.get_base_queryset()
406
485
  queryset = self.order_queryset(queryset)
407
486
  queryset = self.filter_queryset(queryset)
487
+ queryset = self.search_queryset(queryset)
408
488
  return queryset
409
489
 
490
+ def paginate_queryset(self, queryset, page_size):
491
+ paginator = self.get_paginator(
492
+ queryset,
493
+ page_size,
494
+ orphans=self.get_paginate_orphans(),
495
+ allow_empty_first_page=self.get_allow_empty(),
496
+ )
497
+
498
+ page_number = self.request.GET.get(self.page_kwarg)
499
+ page = paginator.get_page(page_number)
500
+ return (paginator, page, page.object_list, page.has_other_pages())
501
+
410
502
  def get_table_kwargs(self):
411
503
  return {
412
504
  "ordering": self.ordering,
@@ -437,6 +529,20 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
437
529
  if self.index_results_url_name:
438
530
  return reverse(self.index_results_url_name)
439
531
 
532
+ @cached_property
533
+ def no_results_message(self):
534
+ if not self.verbose_name_plural:
535
+ return _("There are no results.")
536
+
537
+ if self.is_searching or self.is_filtering:
538
+ return _("No %(model_name)s match your query.") % {
539
+ "model_name": self.verbose_name_plural
540
+ }
541
+
542
+ return _("There are no %(model_name)s to display.") % {
543
+ "model_name": self.verbose_name_plural
544
+ }
545
+
440
546
  def get_context_data(self, *args, **kwargs):
441
547
  context = super().get_context_data(*args, **kwargs)
442
548
 
@@ -444,6 +550,9 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
444
550
 
445
551
  context["index_url"] = self.index_url
446
552
  context["index_results_url"] = self.index_results_url
553
+ context["verbose_name_plural"] = self.verbose_name_plural
554
+ context["no_results_message"] = self.no_results_message
555
+ context["ordering"] = self.ordering
447
556
  context["table"] = table
448
557
  context["media"] = table.media
449
558
  # On Django's BaseListView, a listing where pagination is applied, but the results
@@ -462,6 +571,12 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
462
571
  context["is_filtering"] = self.is_filtering
463
572
  context["media"] += self.filters.form.media
464
573
 
574
+ if self.search_form:
575
+ context["search_form"] = self.search_form
576
+ context["is_searching"] = self.is_searching
577
+ context["query_string"] = self.search_query
578
+ context["media"] += self.search_form.media
579
+
465
580
  # If we're rendering the results as an HTML fragment, the caller can pass a _w_filter_fragment=1
466
581
  # URL parameter to indicate that the filters should be rendered as a <template> block so that
467
582
  # we can replace the existing filters.
@@ -470,6 +585,8 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
470
585
  and self.filters
471
586
  and self.results_only
472
587
  )
588
+ # Ensure that the header buttons get re-rendered for the results-only view,
589
+ # in case they make use of the search/filter state
473
590
  context["render_buttons_fragment"] = (
474
591
  context.get("header_buttons") and self.results_only
475
592
  )