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
@@ -24,6 +24,7 @@ from taggit.models import Tag
24
24
  from wagtail import hooks
25
25
  from wagtail.admin.admin_url_finder import AdminURLFinder
26
26
  from wagtail.admin.forms import WagtailAdminModelForm
27
+ from wagtail.admin.forms.search import SearchForm
27
28
  from wagtail.admin.menu import admin_menu
28
29
  from wagtail.admin.panels import FieldPanel, ObjectList, get_edit_handler
29
30
  from wagtail.admin.widgets.button import ButtonWithDropdown
@@ -33,6 +34,7 @@ from wagtail.models import Locale, ModelLogEntry, Revision
33
34
  from wagtail.signals import published, unpublished
34
35
  from wagtail.snippets.action_menu import (
35
36
  ActionMenuItem,
37
+ DeleteMenuItem,
36
38
  get_base_snippet_action_menu_items,
37
39
  )
38
40
  from wagtail.snippets.blocks import SnippetChooserBlock
@@ -61,10 +63,12 @@ from wagtail.test.testapp.models import (
61
63
  AdvertWithCustomPrimaryKey,
62
64
  AdvertWithCustomUUIDPrimaryKey,
63
65
  AdvertWithTabbedInterface,
66
+ CustomPreviewSizesModel,
64
67
  DraftStateCustomPrimaryKeyModel,
65
68
  DraftStateModel,
66
69
  FullFeaturedSnippet,
67
70
  MultiPreviewModesModel,
71
+ PreviewableModel,
68
72
  RevisableChildModel,
69
73
  RevisableModel,
70
74
  SnippetChooserModel,
@@ -215,19 +219,17 @@ class TestSnippetListView(WagtailTestUtils, TestCase):
215
219
  self.assertEqual(response.context["page_obj"][0].text, "advert 10")
216
220
 
217
221
  def test_simple_pagination(self):
218
- # page numbers in range should be accepted
219
- response = self.get({"p": 1})
220
- self.assertEqual(response.status_code, 200)
221
- self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
222
- # page numbers out of range should return 404
223
- response = self.get({"p": 9999})
224
- self.assertEqual(response.status_code, 404)
222
+ pages = ["0", "1", "-1", "9999", "Not a page"]
223
+ for page in pages:
224
+ response = self.get({"p": page})
225
+ self.assertEqual(response.status_code, 200)
226
+ self.assertTemplateUsed(response, "wagtailsnippets/snippets/index.html")
225
227
 
226
228
  def test_displays_add_button(self):
227
229
  self.assertContains(self.get(), "Add advert")
228
230
 
229
231
  def test_not_searchable(self):
230
- self.assertFalse(self.get().context["is_searchable"])
232
+ self.assertFalse(self.get().context.get("search_form"))
231
233
 
232
234
  def test_register_snippet_listing_buttons_hook(self):
233
235
  advert = Advert.objects.create(text="My Lovely advert")
@@ -716,7 +718,7 @@ class TestSnippetListViewWithSearchableSnippet(WagtailTestUtils, TransactionTest
716
718
  self.assertNotContains(response, "This field is required.")
717
719
 
718
720
  def test_is_searchable(self):
719
- self.assertTrue(self.get().context["is_searchable"])
721
+ self.assertIsInstance(self.get().context["search_form"], SearchForm)
720
722
 
721
723
  def test_search_hello(self):
722
724
  response = self.get({"q": "Hello"})
@@ -808,6 +810,33 @@ class TestSnippetCreateView(WagtailTestUtils, TestCase):
808
810
  self.assertTemplateUsed(response, "wagtailsnippets/snippets/create.html")
809
811
  self.assertNotContains(response, 'role="tablist"', html=True)
810
812
 
813
+ soup = self.get_soup(response.content)
814
+
815
+ # Should have the unsaved controller set up
816
+ editor_form = soup.select_one("#w-editor-form")
817
+ self.assertIsNotNone(editor_form)
818
+ self.assertIn("w-unsaved", editor_form.attrs.get("data-controller").split())
819
+ self.assertTrue(
820
+ {
821
+ "w-unsaved#submit",
822
+ "beforeunload@window->w-unsaved#confirm",
823
+ "change->w-unsaved#check",
824
+ "keyup->w-unsaved#check",
825
+ }.issubset(editor_form.attrs.get("data-action").split())
826
+ )
827
+ self.assertEqual(
828
+ editor_form.attrs.get("data-w-unsaved-confirmation-value"),
829
+ "true",
830
+ )
831
+ self.assertEqual(
832
+ editor_form.attrs.get("data-w-unsaved-force-value"),
833
+ "false",
834
+ )
835
+ self.assertIn(
836
+ "edits",
837
+ editor_form.attrs.get("data-w-unsaved-watch-value").split(),
838
+ )
839
+
811
840
  def test_snippet_with_tabbed_interface(self):
812
841
  response = self.client.get(
813
842
  reverse("wagtailsnippets_tests_advertwithtabbedinterface:add")
@@ -847,6 +876,34 @@ class TestSnippetCreateView(WagtailTestUtils, TestCase):
847
876
  self.assertContains(response, "error-message", count=1)
848
877
  self.assertContains(response, "This field is required", count=1)
849
878
 
879
+ soup = self.get_soup(response.content)
880
+
881
+ # Should have the unsaved controller set up
882
+ editor_form = soup.select_one("#w-editor-form")
883
+ self.assertIsNotNone(editor_form)
884
+ self.assertIn("w-unsaved", editor_form.attrs.get("data-controller").split())
885
+ self.assertTrue(
886
+ {
887
+ "w-unsaved#submit",
888
+ "beforeunload@window->w-unsaved#confirm",
889
+ "change->w-unsaved#check",
890
+ "keyup->w-unsaved#check",
891
+ }.issubset(editor_form.attrs.get("data-action").split())
892
+ )
893
+ self.assertEqual(
894
+ editor_form.attrs.get("data-w-unsaved-confirmation-value"),
895
+ "true",
896
+ )
897
+ self.assertEqual(
898
+ editor_form.attrs.get("data-w-unsaved-force-value"),
899
+ # The form is invalid, we want to force it to be "dirty" on initial load
900
+ "true",
901
+ )
902
+ self.assertIn(
903
+ "edits",
904
+ editor_form.attrs.get("data-w-unsaved-watch-value").split(),
905
+ )
906
+
850
907
  def test_create(self):
851
908
  response = self.post(
852
909
  post_data={"text": "test_advert", "url": "http://www.example.com/"}
@@ -965,7 +1022,7 @@ class TestSnippetCreateView(WagtailTestUtils, TestCase):
965
1022
  label = "Test"
966
1023
  name = "test"
967
1024
  icon_name = "check"
968
- classname = "action-secondary"
1025
+ classname = "custom-class"
969
1026
 
970
1027
  def is_shown(self, context):
971
1028
  return True
@@ -982,7 +1039,7 @@ class TestSnippetCreateView(WagtailTestUtils, TestCase):
982
1039
 
983
1040
  self.assertContains(
984
1041
  response,
985
- '<button type="submit" name="test" value="Test" class="button action-secondary"><svg class="icon icon-check icon" aria-hidden="true"><use href="#icon-check"></use></svg>Test</button>',
1042
+ '<button type="submit" name="test" value="Test" class="button custom-class"><svg class="icon icon-check icon" aria-hidden="true"><use href="#icon-check"></use></svg>Test</button>',
986
1043
  html=True,
987
1044
  )
988
1045
 
@@ -1003,11 +1060,15 @@ class TestSnippetCreateView(WagtailTestUtils, TestCase):
1003
1060
  label = "Test"
1004
1061
  name = "test"
1005
1062
  icon_name = "check"
1006
- classname = "action-secondary"
1063
+ classname = "custom-class"
1007
1064
 
1008
1065
  def is_shown(self, context):
1009
1066
  return True
1010
1067
 
1068
+ class Media:
1069
+ js = ["js/some-default-item.js"]
1070
+ css = {"all": ["css/some-default-item.css"]}
1071
+
1011
1072
  def hook_func(menu_items, request, context):
1012
1073
  self.assertIsInstance(menu_items, list)
1013
1074
  self.assertIsInstance(request, WSGIRequest)
@@ -1020,12 +1081,28 @@ class TestSnippetCreateView(WagtailTestUtils, TestCase):
1020
1081
  with self.register_hook("construct_snippet_action_menu", hook_func):
1021
1082
  response = self.get()
1022
1083
 
1023
- self.assertContains(
1024
- response,
1025
- '<button type="submit" name="test" value="Test" class="button action-secondary"><svg class="icon icon-check icon" aria-hidden="true"><use href="#icon-check"></use></svg>Test</button>',
1026
- html=True,
1027
- )
1028
- self.assertNotContains(response, "<em>'Save'</em>")
1084
+ soup = self.get_soup(response.content)
1085
+ custom_action = soup.select_one("form button[name='test']")
1086
+ self.assertIsNotNone(custom_action)
1087
+
1088
+ # We're replacing the save button, so it should not be in a dropdown
1089
+ # as it's the main action
1090
+ dropdown_parent = custom_action.find_parent(attrs={"class": "w-dropdown"})
1091
+ self.assertIsNone(dropdown_parent)
1092
+
1093
+ self.assertEqual(custom_action.text.strip(), "Test")
1094
+ self.assertEqual(custom_action.attrs.get("class"), ["button", "custom-class"])
1095
+ icon = custom_action.select_one("svg use[href='#icon-check']")
1096
+ self.assertIsNotNone(icon)
1097
+
1098
+ # Should contain media files
1099
+ js = soup.select_one("script[src='/static/js/some-default-item.js']")
1100
+ self.assertIsNotNone(js)
1101
+ css = soup.select_one("link[href='/static/css/some-default-item.css']")
1102
+ self.assertIsNotNone(css)
1103
+
1104
+ save_item = soup.select_one("form button[name='action-save']")
1105
+ self.assertIsNone(save_item)
1029
1106
 
1030
1107
 
1031
1108
  class TestSnippetCopyView(WagtailTestUtils, TestCase):
@@ -1668,6 +1745,33 @@ class TestSnippetEditView(BaseTestSnippetEditView):
1668
1745
  allow_extra_attrs=True,
1669
1746
  )
1670
1747
 
1748
+ soup = self.get_soup(response.content)
1749
+
1750
+ # Should have the unsaved controller set up
1751
+ editor_form = soup.select_one("#w-editor-form")
1752
+ self.assertIsNotNone(editor_form)
1753
+ self.assertIn("w-unsaved", editor_form.attrs.get("data-controller").split())
1754
+ self.assertTrue(
1755
+ {
1756
+ "w-unsaved#submit",
1757
+ "beforeunload@window->w-unsaved#confirm",
1758
+ "change->w-unsaved#check",
1759
+ "keyup->w-unsaved#check",
1760
+ }.issubset(editor_form.attrs.get("data-action").split())
1761
+ )
1762
+ self.assertEqual(
1763
+ editor_form.attrs.get("data-w-unsaved-confirmation-value"),
1764
+ "true",
1765
+ )
1766
+ self.assertEqual(
1767
+ editor_form.attrs.get("data-w-unsaved-force-value"),
1768
+ "false",
1769
+ )
1770
+ self.assertIn(
1771
+ "edits",
1772
+ editor_form.attrs.get("data-w-unsaved-watch-value").split(),
1773
+ )
1774
+
1671
1775
  url_finder = AdminURLFinder(self.user)
1672
1776
  expected_url = "/admin/snippets/tests/advert/edit/%d/" % self.test_snippet.pk
1673
1777
  self.assertEqual(url_finder.get_edit_url(self.test_snippet), expected_url)
@@ -1707,6 +1811,34 @@ class TestSnippetEditView(BaseTestSnippetEditView):
1707
1811
  self.assertContains(response, "error-message", count=1)
1708
1812
  self.assertContains(response, "This field is required", count=1)
1709
1813
 
1814
+ soup = self.get_soup(response.content)
1815
+
1816
+ # Should have the unsaved controller set up
1817
+ editor_form = soup.select_one("#w-editor-form")
1818
+ self.assertIsNotNone(editor_form)
1819
+ self.assertIn("w-unsaved", editor_form.attrs.get("data-controller").split())
1820
+ self.assertTrue(
1821
+ {
1822
+ "w-unsaved#submit",
1823
+ "beforeunload@window->w-unsaved#confirm",
1824
+ "change->w-unsaved#check",
1825
+ "keyup->w-unsaved#check",
1826
+ }.issubset(editor_form.attrs.get("data-action").split())
1827
+ )
1828
+ self.assertEqual(
1829
+ editor_form.attrs.get("data-w-unsaved-confirmation-value"),
1830
+ "true",
1831
+ )
1832
+ self.assertEqual(
1833
+ editor_form.attrs.get("data-w-unsaved-force-value"),
1834
+ # The form is invalid, we want to force it to be "dirty" on initial load
1835
+ "true",
1836
+ )
1837
+ self.assertIn(
1838
+ "edits",
1839
+ editor_form.attrs.get("data-w-unsaved-watch-value").split(),
1840
+ )
1841
+
1710
1842
  def test_edit(self):
1711
1843
  response = self.post(
1712
1844
  post_data={
@@ -1798,7 +1930,7 @@ class TestSnippetEditView(BaseTestSnippetEditView):
1798
1930
  label = "Test"
1799
1931
  name = "test"
1800
1932
  icon_name = "check"
1801
- classname = "action-secondary"
1933
+ classname = "custom-class"
1802
1934
 
1803
1935
  def is_shown(self, context):
1804
1936
  return True
@@ -1815,7 +1947,7 @@ class TestSnippetEditView(BaseTestSnippetEditView):
1815
1947
 
1816
1948
  self.assertContains(
1817
1949
  response,
1818
- '<button type="submit" name="test" value="Test" class="button action-secondary"><svg class="icon icon-check icon" aria-hidden="true"><use href="#icon-check"></use></svg>Test</button>',
1950
+ '<button type="submit" name="test" value="Test" class="button custom-class"><svg class="icon icon-check icon" aria-hidden="true"><use href="#icon-check"></use></svg>Test</button>',
1819
1951
  html=True,
1820
1952
  )
1821
1953
 
@@ -1835,6 +1967,75 @@ class TestSnippetEditView(BaseTestSnippetEditView):
1835
1967
 
1836
1968
  self.assertNotContains(response, "<em>Save</em>")
1837
1969
 
1970
+ def test_register_deprecated_delete_menu_item(self):
1971
+ def hook_func(model):
1972
+ return DeleteMenuItem(order=900)
1973
+
1974
+ get_base_snippet_action_menu_items.cache_clear()
1975
+ with self.register_hook(
1976
+ "register_snippet_action_menu_item", hook_func
1977
+ ), self.assertWarnsMessage(
1978
+ RemovedInWagtail70Warning,
1979
+ "DeleteMenuItem is deprecated. "
1980
+ "The delete option is now provided via EditView.get_header_more_buttons().",
1981
+ ):
1982
+ response = self.get()
1983
+
1984
+ get_base_snippet_action_menu_items.cache_clear()
1985
+
1986
+ delete_url = reverse(
1987
+ self.test_snippet.snippet_viewset.get_url_name("delete"),
1988
+ args=(quote(self.test_snippet.pk),),
1989
+ )
1990
+ self.assertContains(
1991
+ response,
1992
+ f'<a class="button" href="{ delete_url }"><svg class="icon icon-bin icon" aria-hidden="true"><use href="#icon-bin"></use></svg>Delete</a>',
1993
+ html=True,
1994
+ )
1995
+
1996
+ def test_previewable_snippet(self):
1997
+ self.test_snippet = PreviewableModel.objects.create(
1998
+ text="Preview-enabled snippet"
1999
+ )
2000
+ response = self.get()
2001
+ self.assertEqual(response.status_code, 200)
2002
+ soup = self.get_soup(response.content)
2003
+
2004
+ radios = soup.select('input[type="radio"][name="preview-size"]')
2005
+ self.assertEqual(len(radios), 3)
2006
+
2007
+ self.assertEqual(
2008
+ [
2009
+ "Preview in mobile size",
2010
+ "Preview in tablet size",
2011
+ "Preview in desktop size",
2012
+ ],
2013
+ [radio["aria-label"] for radio in radios],
2014
+ )
2015
+
2016
+ self.assertEqual("375", radios[0]["data-device-width"])
2017
+ self.assertTrue(radios[0].has_attr("checked"))
2018
+
2019
+ def test_custom_preview_sizes(self):
2020
+ self.test_snippet = CustomPreviewSizesModel.objects.create(
2021
+ text="Preview-enabled with custom sizes",
2022
+ )
2023
+
2024
+ response = self.get()
2025
+ self.assertEqual(response.status_code, 200)
2026
+ soup = self.get_soup(response.content)
2027
+
2028
+ radios = soup.select('input[type="radio"][name="preview-size"]')
2029
+ self.assertEqual(len(radios), 2)
2030
+
2031
+ self.assertEqual("412", radios[0]["data-device-width"])
2032
+ self.assertEqual("Custom mobile preview", radios[0]["aria-label"])
2033
+ self.assertFalse(radios[0].has_attr("checked"))
2034
+
2035
+ self.assertEqual("1280", radios[1]["data-device-width"])
2036
+ self.assertEqual("Original desktop", radios[1]["aria-label"])
2037
+ self.assertTrue(radios[1].has_attr("checked"))
2038
+
1838
2039
 
1839
2040
  class TestEditTabbedSnippet(BaseTestSnippetEditView):
1840
2041
  def setUp(self):
@@ -2002,7 +2203,7 @@ class TestEditDraftStateSnippet(BaseTestSnippetEditView):
2002
2203
  )
2003
2204
  self.assertNotContains(
2004
2205
  response,
2005
- f'<a class="button action-secondary" href="{unpublish_url}">',
2206
+ f'<a class="button" href="{unpublish_url}">',
2006
2207
  )
2007
2208
  self.assertNotContains(response, "Unpublish")
2008
2209
 
@@ -2403,7 +2604,7 @@ class TestEditDraftStateSnippet(BaseTestSnippetEditView):
2403
2604
  )
2404
2605
  self.assertNotContains(
2405
2606
  response,
2406
- f'<a class="button action-secondary" href="{unpublish_url}">',
2607
+ f'<a class="button" href="{unpublish_url}">',
2407
2608
  )
2408
2609
  self.assertNotContains(response, "Unpublish")
2409
2610
 
@@ -2439,7 +2640,7 @@ class TestEditDraftStateSnippet(BaseTestSnippetEditView):
2439
2640
  )
2440
2641
  self.assertContains(
2441
2642
  response,
2442
- f'<a class="button action-secondary" href="{unpublish_url}">',
2643
+ f'<a class="button" href="{unpublish_url}">',
2443
2644
  )
2444
2645
  self.assertContains(response, "Unpublish")
2445
2646
 
@@ -2477,7 +2678,7 @@ class TestEditDraftStateSnippet(BaseTestSnippetEditView):
2477
2678
  )
2478
2679
  self.assertContains(
2479
2680
  response,
2480
- f'<a class="button action-secondary" href="{unpublish_url}">',
2681
+ f'<a class="button" href="{unpublish_url}">',
2481
2682
  )
2482
2683
  self.assertContains(response, "Unpublish")
2483
2684
 
@@ -4448,15 +4649,24 @@ class TestSnippetRevisions(WagtailTestUtils, TestCase):
4448
4649
 
4449
4650
  self.assertEqual(response.status_code, 200)
4450
4651
  self.assertTemplateUsed(response, "wagtailsnippets/snippets/edit.html")
4652
+ soup = self.get_soup(response.content)
4451
4653
 
4452
4654
  # The save button should be labelled "Replace current draft"
4453
- self.assertContains(response, "Replace current draft")
4454
- # The publish button should exist
4455
- self.assertContains(response, "Publish this version")
4456
- # The publish button should have name="action-publish"
4457
- self.assertContains(
4458
- response,
4459
- '<button\n type="submit"\n name="action-publish"\n value="action-publish"\n class="button action-save button-longrunning warning"\n data-controller="w-progress w-kbd"\n data-action="w-progress#activate"\n data-w-kbd-key-value="mod+s"\n',
4655
+ footer = soup.select_one("footer")
4656
+ save_button = footer.select_one(
4657
+ 'button[type="submit"]:not([name="action-publish"])'
4658
+ )
4659
+ self.assertIsNotNone(save_button)
4660
+ self.assertEqual(save_button.text.strip(), "Replace current draft")
4661
+ # The publish button should exist and have name="action-publish"
4662
+ publish_button = footer.select_one(
4663
+ 'button[type="submit"][name="action-publish"]'
4664
+ )
4665
+ self.assertIsNotNone(publish_button)
4666
+ self.assertEqual(publish_button.text.strip(), "Publish this version")
4667
+ self.assertEqual(
4668
+ set(publish_button.get("class")),
4669
+ {"button", "action-save", "button-longrunning"},
4460
4670
  )
4461
4671
 
4462
4672
  # Should not show the Unpublish action menu item
@@ -4464,11 +4674,8 @@ class TestSnippetRevisions(WagtailTestUtils, TestCase):
4464
4674
  "wagtailsnippets_tests_draftstatemodel:unpublish",
4465
4675
  args=(quote(self.snippet.pk),),
4466
4676
  )
4467
- self.assertNotContains(
4468
- response,
4469
- f'<a class="button action-secondary" href="{unpublish_url}">',
4470
- )
4471
- self.assertNotContains(response, "Unpublish")
4677
+ unpublish_button = footer.select_one(f'a[href="{unpublish_url}"]')
4678
+ self.assertIsNone(unpublish_button)
4472
4679
 
4473
4680
  def test_get_with_previewable_snippet(self):
4474
4681
  self.snippet = MultiPreviewModesModel.objects.create(text="Preview-enabled foo")
@@ -4503,10 +4710,12 @@ class TestSnippetRevisions(WagtailTestUtils, TestCase):
4503
4710
  # Should show the preview panel
4504
4711
  preview_url = self.get_url("preview_on_edit")
4505
4712
  self.assertContains(response, 'data-side-panel="preview"')
4506
- self.assertContains(response, f'data-action="{preview_url}"')
4713
+ soup = self.get_soup(response.content)
4714
+ controller = soup.select_one('[data-controller="w-preview"]')
4715
+ self.assertIsNotNone(controller)
4716
+ self.assertEqual(controller.get("data-w-preview-url-value"), preview_url)
4507
4717
 
4508
4718
  # Should have the preview side panel toggle button
4509
- soup = self.get_soup(response.content)
4510
4719
  toggle_button = soup.find("button", {"data-side-panel-toggle": "preview"})
4511
4720
  self.assertIsNotNone(toggle_button)
4512
4721
  self.assertEqual("w-tooltip w-kbd", toggle_button["data-controller"])
@@ -97,6 +97,11 @@ class TestSnippetUsageView(WagtailTestUtils, TestCase):
97
97
  self.assertContains(response, "<th>Field</th>", html=True)
98
98
  self.assertNotContains(response, "<th>If you confirm deletion</th>", html=True)
99
99
  self.assertContains(response, "Snippet content object")
100
+ self.assertContains(
101
+ response,
102
+ reverse("wagtailadmin_pages:edit", args=[gfk_page.id])
103
+ + "#:w:contentpath=snippet_content_object",
104
+ )
100
105
 
101
106
  def test_usage_without_edit_permission_on_snippet(self):
102
107
  # Create a user with basic admin backend access
@@ -15,6 +15,7 @@ from django.utils.timezone import now
15
15
  from openpyxl import load_workbook
16
16
 
17
17
  from wagtail.admin.admin_url_finder import AdminURLFinder
18
+ from wagtail.admin.forms.search import SearchForm
18
19
  from wagtail.admin.menu import admin_menu, settings_menu
19
20
  from wagtail.admin.panels import get_edit_handler
20
21
  from wagtail.admin.staticfiles import versioned_static
@@ -89,7 +90,7 @@ class TestCustomIcon(BaseSnippetViewSetTests):
89
90
  ("delete", [pk], "header.html"),
90
91
  ("usage", [pk], "headers/slim_header.html"),
91
92
  ("unpublish", [pk], "header.html"),
92
- ("workflow_history", [pk], "header.html"),
93
+ ("workflow_history", [pk], "headers/slim_header.html"),
93
94
  ("revisions_revert", [pk, self.revision_1.id], "headers/slim_header.html"),
94
95
  (
95
96
  "revisions_compare",
@@ -136,11 +137,9 @@ class TestCustomIcon(BaseSnippetViewSetTests):
136
137
  )
137
138
  )
138
139
  self.assertEqual(response.status_code, 200)
139
- self.assertTemplateUsed(response, "wagtailadmin/shared/header.html")
140
- # The icon is not displayed in the header,
141
- # but it is displayed in the main content
142
- self.assertEqual(response.context["header_icon"], "list-ul")
143
- self.assertContains(response, "icon icon-list-ul")
140
+ self.assertTemplateNotUsed(response, "wagtailadmin/shared/header.html")
141
+ self.assertEqual(response.context["header_icon"], "cog")
142
+ self.assertContains(response, "icon icon-clipboard-list")
144
143
  self.assertContains(response, "icon icon-cog")
145
144
 
146
145
 
@@ -1077,7 +1076,7 @@ class TestDjangoORMSearchBackend(BaseSnippetViewSetTests):
1077
1076
  self.assertNotContains(response, "This field is required.")
1078
1077
 
1079
1078
  def test_is_searchable(self):
1080
- self.assertTrue(self.get().context["is_searchable"])
1079
+ self.assertIsInstance(self.get().context["search_form"], SearchForm)
1081
1080
 
1082
1081
  def test_search_index_view(self):
1083
1082
  response = self.get({"q": "Django"})
@@ -1137,14 +1136,14 @@ class TestMenuItemRegistration(BaseSnippetViewSetTests):
1137
1136
  self.model = RevisableModel
1138
1137
  revisable_item = group_item.menu_items[0]
1139
1138
  self.assertEqual(revisable_item.name, "revisable-models")
1140
- self.assertEqual(revisable_item.label, "Revisable Models")
1139
+ self.assertEqual(revisable_item.label, "Revisable models")
1141
1140
  self.assertEqual(revisable_item.icon_name, "snippet")
1142
1141
  self.assertEqual(revisable_item.url, self.get_url("list"))
1143
1142
 
1144
1143
  self.model = RevisableChildModel
1145
1144
  revisable_child_item = group_item.menu_items[1]
1146
1145
  self.assertEqual(revisable_child_item.name, "revisable-child-models")
1147
- self.assertEqual(revisable_child_item.label, "Revisable Child Models")
1146
+ self.assertEqual(revisable_child_item.label, "Revisable child models")
1148
1147
  self.assertEqual(revisable_child_item.icon_name, "snippet")
1149
1148
  self.assertEqual(revisable_child_item.url, self.get_url("list"))
1150
1149
 
@@ -1556,6 +1555,23 @@ class TestBreadcrumbs(AdminTemplateTestUtils, BaseSnippetViewSetTests):
1556
1555
  ]
1557
1556
  self.assertBreadcrumbsItemsRendered(items, response.content)
1558
1557
 
1558
+ def test_workflow_history_view(self):
1559
+ response = self.client.get(
1560
+ self.get_url("workflow_history", args=(self.object.pk,))
1561
+ )
1562
+ items = [
1563
+ {
1564
+ "url": self.get_url("list"),
1565
+ "label": "Full-featured snippets",
1566
+ },
1567
+ {
1568
+ "url": self.get_url("edit", args=(self.object.pk,)),
1569
+ "label": str(self.object),
1570
+ },
1571
+ {"url": "", "label": "Workflow history", "sublabel": str(self.object)},
1572
+ ]
1573
+ self.assertBreadcrumbsItemsRendered(items, response.content)
1574
+
1559
1575
 
1560
1576
  class TestCustomMethods(BaseSnippetViewSetTests):
1561
1577
  model = FullFeaturedSnippet