wagtail 7.1.1__py3-none-any.whl → 7.2rc1__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 (345) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_page.py +1 -1
  3. wagtail/actions/create_alias.py +1 -1
  4. wagtail/actions/delete_page.py +1 -1
  5. wagtail/actions/publish_page_revision.py +1 -1
  6. wagtail/actions/publish_revision.py +1 -1
  7. wagtail/actions/revert_to_page_revision.py +1 -1
  8. wagtail/actions/unpublish.py +1 -1
  9. wagtail/actions/unpublish_page.py +1 -1
  10. wagtail/admin/auth.py +3 -1
  11. wagtail/admin/checks.py +2 -2
  12. wagtail/admin/filters.py +28 -1
  13. wagtail/admin/forms/collections.py +1 -1
  14. wagtail/admin/forms/comments.py +1 -1
  15. wagtail/admin/forms/models.py +1 -1
  16. wagtail/admin/forms/pages.py +1 -1
  17. wagtail/admin/forms/tags.py +1 -1
  18. wagtail/admin/locale/cs/LC_MESSAGES/django.mo +0 -0
  19. wagtail/admin/locale/cs/LC_MESSAGES/django.po +25 -1
  20. wagtail/admin/locale/en/LC_MESSAGES/django.po +278 -192
  21. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +29 -15
  22. wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
  23. wagtail/admin/locale/it/LC_MESSAGES/django.po +3 -2
  24. wagtail/admin/locale/nl/LC_MESSAGES/django.mo +0 -0
  25. wagtail/admin/locale/nl/LC_MESSAGES/django.po +57 -3
  26. wagtail/admin/locale/nl/LC_MESSAGES/djangojs.mo +0 -0
  27. wagtail/admin/locale/nl/LC_MESSAGES/djangojs.po +8 -2
  28. wagtail/admin/locale/ru/LC_MESSAGES/django.mo +0 -0
  29. wagtail/admin/locale/ru/LC_MESSAGES/django.po +58 -1
  30. wagtail/admin/locale/tr/LC_MESSAGES/django.mo +0 -0
  31. wagtail/admin/locale/tr/LC_MESSAGES/django.po +3 -2
  32. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  33. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  34. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  37. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  38. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  39. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +2 -2
  40. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  41. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  42. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  43. wagtail/admin/static/wagtailadmin/js/icons.js +1 -1
  44. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  45. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  46. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  47. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  48. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  49. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  50. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  51. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  52. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  53. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  54. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  55. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  56. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +2 -2
  57. wagtail/admin/static/wagtailadmin/js/vendor/bootstrap-modal.js +1 -1
  58. wagtail/admin/static/wagtailadmin/js/vendor/bootstrap-transition.js +1 -1
  59. wagtail/admin/static/wagtailadmin/js/vendor/jquery-3.6.0.min.js +1 -1
  60. wagtail/admin/static/wagtailadmin/js/vendor/jquery-ui-1.13.2.min.js +1 -1
  61. wagtail/admin/static/wagtailadmin/js/vendor/jquery.datetimepicker.js +1 -1
  62. wagtail/admin/static/wagtailadmin/js/vendor/jquery.fileupload-process.js +1 -1
  63. wagtail/admin/static/wagtailadmin/js/vendor/jquery.fileupload.js +1 -1
  64. wagtail/admin/static/wagtailadmin/js/vendor/jquery.iframe-transport.js +1 -1
  65. wagtail/admin/static/wagtailadmin/js/vendor/tag-it.js +1 -1
  66. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  67. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +1 -1
  68. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  69. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  70. wagtail/admin/templates/wagtailadmin/account/account.html +2 -0
  71. wagtail/admin/templates/wagtailadmin/base.html +14 -0
  72. wagtail/admin/templates/wagtailadmin/generic/chooser/chooser.html +2 -1
  73. wagtail/admin/templates/wagtailadmin/generic/chooser/creation_form.html +2 -1
  74. wagtail/admin/templates/wagtailadmin/generic/form.html +3 -1
  75. wagtail/admin/templates/wagtailadmin/panels/multi_field_panel_child.html +1 -1
  76. wagtail/admin/templates/wagtailadmin/panels/object_list.html +1 -1
  77. wagtail/admin/templates/wagtailadmin/panels/tabbed_interface.html +3 -2
  78. wagtail/admin/templates/wagtailadmin/shared/formatted_field.html +1 -1
  79. wagtail/admin/templates/wagtailadmin/shared/forms/single_checkbox.html +1 -1
  80. wagtail/admin/templates/wagtailadmin/shared/keyboard_shortcuts_dialog.html +19 -0
  81. wagtail/admin/templates/wagtailadmin/shared/panel.html +1 -1
  82. wagtail/admin/templates/wagtailadmin/shared/set_privacy.html +15 -0
  83. wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html +28 -1
  84. wagtail/admin/templates/wagtailadmin/shared/workflow_history/detail.html +2 -2
  85. wagtail/admin/templates/wagtailadmin/{pages/listing/_ordering_header.html → tables/ordering_header.html} +2 -2
  86. wagtail/admin/templates/wagtailadmin/tables/title_cell.html +1 -1
  87. wagtail/admin/templates/wagtailadmin/userbar/base.html +6 -3
  88. wagtail/admin/templates/wagtailadmin/userbar/item_admin.html +2 -2
  89. wagtail/admin/templates/wagtailadmin/userbar/item_page_add.html +2 -2
  90. wagtail/admin/templates/wagtailadmin/userbar/item_page_edit.html +2 -2
  91. wagtail/admin/templates/wagtailadmin/userbar/item_page_explore.html +2 -2
  92. wagtail/admin/templates/wagtailadmin/widgets/{daterange_input.html → range_input.html} +1 -1
  93. wagtail/admin/templates/wagtailadmin/workflows/task_chooser/chooser.html +4 -2
  94. wagtail/admin/templatetags/wagtailadmin_tags.py +56 -22
  95. wagtail/admin/tests/api/test_pages.py +7 -7
  96. wagtail/admin/tests/api/test_renderer_classes.py +16 -0
  97. wagtail/admin/tests/pages/test_create_page.py +34 -2
  98. wagtail/admin/tests/pages/test_edit_page.py +128 -14
  99. wagtail/admin/tests/pages/test_explorer_view.py +34 -7
  100. wagtail/admin/tests/pages/test_reorder_page.py +11 -0
  101. wagtail/admin/tests/test_collections_views.py +12 -0
  102. wagtail/admin/tests/test_edit_handlers.py +3 -3
  103. wagtail/admin/tests/test_filters.py +2 -2
  104. wagtail/admin/tests/test_keyboard_shortcuts.py +52 -2
  105. wagtail/admin/tests/test_menu.py +0 -2
  106. wagtail/admin/tests/test_privacy.py +16 -16
  107. wagtail/admin/tests/test_templatetags.py +137 -0
  108. wagtail/admin/tests/test_userbar.py +75 -35
  109. wagtail/admin/tests/test_views_generic.py +34 -0
  110. wagtail/admin/tests/test_workflows.py +34 -0
  111. wagtail/admin/tests/viewsets/test_model_viewset.py +322 -0
  112. wagtail/admin/ui/tables/orderable.py +73 -0
  113. wagtail/admin/ui/tables/pages.py +3 -13
  114. wagtail/admin/userbar.py +6 -1
  115. wagtail/admin/views/collection_privacy.py +6 -2
  116. wagtail/admin/views/generic/__init__.py +1 -0
  117. wagtail/admin/views/generic/mixins.py +20 -2
  118. wagtail/admin/views/generic/models.py +67 -1
  119. wagtail/admin/views/generic/ordering.py +79 -0
  120. wagtail/admin/views/home.py +3 -3
  121. wagtail/admin/views/page_privacy.py +5 -2
  122. wagtail/admin/views/pages/create.py +1 -1
  123. wagtail/admin/views/pages/edit.py +2 -2
  124. wagtail/admin/views/pages/listing.py +7 -42
  125. wagtail/admin/views/pages/move.py +1 -1
  126. wagtail/admin/views/pages/ordering.py +1 -1
  127. wagtail/admin/viewsets/base.py +1 -1
  128. wagtail/admin/viewsets/model.py +49 -1
  129. wagtail/admin/wagtail_hooks.py +2 -1
  130. wagtail/admin/widgets/slug.py +10 -10
  131. wagtail/api/v2/serializers.py +1 -1
  132. wagtail/api/v2/tests/test_renderer_classes.py +32 -0
  133. wagtail/apps.py +2 -0
  134. wagtail/bin/wagtail.py +1 -1
  135. wagtail/blocks/struct_block.py +2 -1
  136. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +14 -14
  137. wagtail/contrib/forms/locale/nl/LC_MESSAGES/django.mo +0 -0
  138. wagtail/contrib/forms/locale/nl/LC_MESSAGES/django.po +19 -2
  139. wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.mo +0 -0
  140. wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.po +18 -1
  141. wagtail/contrib/frontend_cache/tests.py +4 -2
  142. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +4 -4
  143. wagtail/contrib/redirects/tests/test_tmp_storages.py +20 -0
  144. wagtail/contrib/redirects/tmp_storages.py +1 -1
  145. wagtail/contrib/redirects/views.py +3 -3
  146. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +3 -3
  147. wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.mo +0 -0
  148. wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.po +43 -3
  149. wagtail/contrib/search_promotions/static/wagtailsearchpromotions/js/query-chooser-modal.js +1 -1
  150. wagtail/contrib/search_promotions/views/settings.py +2 -2
  151. wagtail/contrib/settings/locale/cs/LC_MESSAGES/django.mo +0 -0
  152. wagtail/contrib/settings/locale/cs/LC_MESSAGES/django.po +6 -1
  153. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +1 -1
  154. wagtail/contrib/settings/locale/nl/LC_MESSAGES/django.mo +0 -0
  155. wagtail/contrib/settings/locale/nl/LC_MESSAGES/django.po +6 -2
  156. wagtail/contrib/settings/locale/ru/LC_MESSAGES/django.mo +0 -0
  157. wagtail/contrib/settings/locale/ru/LC_MESSAGES/django.po +6 -1
  158. wagtail/contrib/settings/tests/site_specific/test_admin.py +40 -6
  159. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  160. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +1 -1
  161. wagtail/contrib/styleguide/templates/wagtailstyleguide/base.html +5 -5
  162. wagtail/contrib/table_block/blocks.py +1 -0
  163. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +5 -1
  164. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  165. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +1 -1
  166. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  167. wagtail/coreutils.py +5 -5
  168. wagtail/documents/forms.py +18 -1
  169. wagtail/documents/locale/en/LC_MESSAGES/django.po +10 -10
  170. wagtail/documents/locale/nl/LC_MESSAGES/django.mo +0 -0
  171. wagtail/documents/locale/nl/LC_MESSAGES/django.po +9 -0
  172. wagtail/documents/locale/ru/LC_MESSAGES/django.mo +0 -0
  173. wagtail/documents/locale/ru/LC_MESSAGES/django.po +9 -0
  174. wagtail/documents/models.py +1 -1
  175. wagtail/documents/static/wagtaildocs/js/add-multiple.js +1 -1
  176. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  177. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  178. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  179. wagtail/documents/templates/wagtaildocs/documents/add.html +0 -34
  180. wagtail/documents/tests/test_admin_views.py +132 -26
  181. wagtail/documents/tests/test_collection_privacy.py +18 -4
  182. wagtail/documents/tests/test_form_overrides.py +1 -1
  183. wagtail/documents/tests/test_search.py +21 -8
  184. wagtail/documents/views/documents.py +1 -1
  185. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  186. wagtail/embeds/static/wagtailembeds/js/embed-chooser-modal.js +1 -1
  187. wagtail/images/forms.py +16 -1
  188. wagtail/images/locale/cs/LC_MESSAGES/django.mo +0 -0
  189. wagtail/images/locale/cs/LC_MESSAGES/django.po +12 -1
  190. wagtail/images/locale/en/LC_MESSAGES/django.po +57 -46
  191. wagtail/images/locale/nl/LC_MESSAGES/django.mo +0 -0
  192. wagtail/images/locale/nl/LC_MESSAGES/django.po +37 -14
  193. wagtail/images/locale/ru/LC_MESSAGES/django.mo +0 -0
  194. wagtail/images/locale/ru/LC_MESSAGES/django.po +20 -1
  195. wagtail/images/models.py +1 -1
  196. wagtail/images/static/wagtailimages/js/add-multiple.js +1 -1
  197. wagtail/images/static/wagtailimages/js/focal-point-chooser.js +1 -1
  198. wagtail/images/static/wagtailimages/js/image-block.js +1 -1
  199. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  200. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  201. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  202. wagtail/images/static/wagtailimages/js/image-url-generator.js +1 -1
  203. wagtail/images/static/wagtailimages/js/vendor/jquery.Jcrop.min.js +1 -1
  204. wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-image.js +1 -1
  205. wagtail/images/static/wagtailimages/js/vendor/jquery.fileupload-validate.js +1 -1
  206. wagtail/images/static/wagtailimages/js/vendor/load-image.min.js +1 -1
  207. wagtail/images/templates/wagtailimages/chooser/chooser.html +22 -13
  208. wagtail/images/templates/wagtailimages/chooser/image_preview_column_cell.html +10 -0
  209. wagtail/images/templates/wagtailimages/chooser/results.html +24 -20
  210. wagtail/images/templates/wagtailimages/chooser/title_column_cell.html +15 -0
  211. wagtail/images/templates/wagtailimages/images/add.html +0 -34
  212. wagtail/images/templates/wagtailimages/images/index.html +3 -3
  213. wagtail/images/templates/wagtailimages/images/index_results.html +1 -1
  214. wagtail/images/templates/wagtailimages/images/layout_toggle_button.html +8 -7
  215. wagtail/images/templatetags/wagtailimages_tags.py +2 -2
  216. wagtail/images/tests/test_admin_views.py +87 -0
  217. wagtail/images/tests/test_form_overrides.py +1 -1
  218. wagtail/images/tests/test_models.py +48 -9
  219. wagtail/images/views/chooser.py +66 -2
  220. wagtail/locale/en/LC_MESSAGES/django.po +55 -55
  221. wagtail/locale/is_IS/LC_MESSAGES/django.mo +0 -0
  222. wagtail/locale/is_IS/LC_MESSAGES/django.po +3 -3
  223. wagtail/locale/nl/LC_MESSAGES/django.mo +0 -0
  224. wagtail/locale/nl/LC_MESSAGES/django.po +11 -2
  225. wagtail/locale/ru/LC_MESSAGES/django.mo +0 -0
  226. wagtail/locale/ru/LC_MESSAGES/django.po +11 -1
  227. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  228. wagtail/locales/locale/nl/LC_MESSAGES/django.mo +0 -0
  229. wagtail/locales/locale/nl/LC_MESSAGES/django.po +12 -1
  230. wagtail/locales/locale/ru/LC_MESSAGES/django.mo +0 -0
  231. wagtail/locales/locale/ru/LC_MESSAGES/django.po +10 -1
  232. wagtail/locales/views.py +2 -2
  233. wagtail/models/orderable.py +10 -0
  234. wagtail/models/pages.py +9 -11
  235. wagtail/models/sites.py +1 -1
  236. wagtail/models/workflows.py +8 -5
  237. wagtail/project_template/home/tests.py +6 -7
  238. wagtail/project_template/project_name/settings/base.py +9 -9
  239. wagtail/project_template/requirements.txt +1 -1
  240. wagtail/query.py +7 -2
  241. wagtail/rich_text/rewriters.py +1 -1
  242. wagtail/search/apps.py +4 -49
  243. wagtail/search/backends/__init__.py +1 -113
  244. wagtail/search/backends/base.py +1 -547
  245. wagtail/search/backends/database/__init__.py +1 -50
  246. wagtail/search/backends/database/fallback.py +1 -253
  247. wagtail/search/backends/database/mysql/mysql.py +1 -700
  248. wagtail/search/backends/database/mysql/query.py +1 -258
  249. wagtail/search/backends/database/postgres/postgres.py +1 -749
  250. wagtail/search/backends/database/postgres/query.py +1 -83
  251. wagtail/search/backends/database/postgres/weights.py +1 -63
  252. wagtail/search/backends/database/sqlite/query.py +1 -294
  253. wagtail/search/backends/database/sqlite/sqlite.py +1 -719
  254. wagtail/search/backends/database/sqlite/utils.py +1 -35
  255. wagtail/search/backends/deprecation.py +45 -0
  256. wagtail/search/backends/elasticsearch7.py +18 -1260
  257. wagtail/search/backends/elasticsearch8.py +21 -96
  258. wagtail/search/backends/elasticsearch9.py +35 -0
  259. wagtail/search/backends/opensearch2.py +35 -0
  260. wagtail/search/backends/opensearch3.py +35 -0
  261. wagtail/search/index.py +1 -358
  262. wagtail/search/locale/en/LC_MESSAGES/django.po +2 -10
  263. wagtail/search/management/commands/update_index.py +1 -205
  264. wagtail/search/management/commands/wagtail_update_index.py +1 -4
  265. wagtail/search/models.py +32 -158
  266. wagtail/search/query.py +1 -114
  267. wagtail/search/queryset.py +1 -43
  268. wagtail/search/signal_handlers.py +1 -24
  269. wagtail/search/tasks.py +1 -10
  270. wagtail/search/tests/test_elasticsearch.py +22 -0
  271. wagtail/search/utils.py +1 -206
  272. wagtail/sites/locale/en/LC_MESSAGES/django.po +1 -1
  273. wagtail/snippets/locale/en/LC_MESSAGES/django.po +3 -3
  274. wagtail/snippets/locale/ru/LC_MESSAGES/django.mo +0 -0
  275. wagtail/snippets/locale/ru/LC_MESSAGES/django.po +8 -1
  276. wagtail/snippets/locale/tr/LC_MESSAGES/django.mo +0 -0
  277. wagtail/snippets/locale/tr/LC_MESSAGES/django.po +8 -1
  278. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  279. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  280. wagtail/snippets/tests/test_preview.py +5 -6
  281. wagtail/snippets/tests/test_reordering.py +319 -0
  282. wagtail/snippets/tests/test_snippets.py +65 -12
  283. wagtail/snippets/views/snippets.py +16 -0
  284. wagtail/test/numberformat.py +30 -0
  285. wagtail/test/settings.py +35 -12
  286. wagtail/test/testapp/fields.py +12 -0
  287. wagtail/test/testapp/migrations/0056_commentablejsonpage.py +50 -0
  288. wagtail/test/testapp/migrations/0057_featurecompletetoy_sort_order.py +23 -0
  289. wagtail/test/testapp/migrations/0058_customlocktask.py +31 -0
  290. wagtail/test/testapp/models.py +27 -0
  291. wagtail/test/testapp/urls.py +1 -0
  292. wagtail/test/testapp/views.py +18 -2
  293. wagtail/test/utils/page_tests.py +17 -17
  294. wagtail/test/utils/template_tests.py +4 -6
  295. wagtail/test/utils/wagtail_tests.py +1 -2
  296. wagtail/tests/test_blocks.py +15 -0
  297. wagtail/tests/test_page_model.py +15 -0
  298. wagtail/{search/tests → tests}/test_page_search.py +29 -2
  299. wagtail/tests/test_search_fields.py +69 -0
  300. wagtail/tests/test_tests.py +62 -6
  301. wagtail/tests/test_workflow.py +25 -1
  302. wagtail/users/locale/cs/LC_MESSAGES/django.mo +0 -0
  303. wagtail/users/locale/cs/LC_MESSAGES/django.po +3 -0
  304. wagtail/users/locale/en/LC_MESSAGES/django.po +2 -2
  305. wagtail/users/locale/nl/LC_MESSAGES/django.mo +0 -0
  306. wagtail/users/locale/nl/LC_MESSAGES/django.po +6 -3
  307. wagtail/users/locale/ru/LC_MESSAGES/django.mo +0 -0
  308. wagtail/users/locale/ru/LC_MESSAGES/django.po +5 -1
  309. wagtail/users/locale/tr/LC_MESSAGES/django.mo +0 -0
  310. wagtail/users/locale/tr/LC_MESSAGES/django.po +78 -4
  311. wagtail/users/templates/wagtailusers/users/create.html +2 -0
  312. wagtail/users/templates/wagtailusers/users/edit.html +2 -0
  313. wagtail/users/tests/test_admin_views.py +4 -0
  314. wagtail/users/views/users.py +1 -1
  315. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/METADATA +7 -6
  316. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/RECORD +322 -328
  317. wagtail/admin/templates/wagtailadmin/collection_privacy/set_privacy.html +0 -13
  318. wagtail/admin/templates/wagtailadmin/page_privacy/set_privacy.html +0 -13
  319. wagtail/search/tests/__init__.py +0 -0
  320. wagtail/search/tests/elasticsearch_common_tests.py +0 -251
  321. wagtail/search/tests/test_backends.py +0 -1215
  322. wagtail/search/tests/test_db_backend.py +0 -62
  323. wagtail/search/tests/test_elasticsearch7_backend.py +0 -1452
  324. wagtail/search/tests/test_elasticsearch8_backend.py +0 -15
  325. wagtail/search/tests/test_index_functions.py +0 -256
  326. wagtail/search/tests/test_indexed_class.py +0 -157
  327. wagtail/search/tests/test_mysql_backend.py +0 -192
  328. wagtail/search/tests/test_postgres_backend.py +0 -210
  329. wagtail/search/tests/test_queries.py +0 -332
  330. wagtail/search/tests/test_related_fields.py +0 -102
  331. wagtail/search/tests/test_sqlite_backend.py +0 -52
  332. wagtail/test/search/__init__.py +0 -0
  333. wagtail/test/search/apps.py +0 -9
  334. wagtail/test/search/fixtures/search.json +0 -545
  335. wagtail/test/search/migrations/0001_initial.py +0 -146
  336. wagtail/test/search/migrations/0002_bookunindexed.py +0 -43
  337. wagtail/test/search/migrations/0003_book_summary.py +0 -18
  338. wagtail/test/search/migrations/__init__.py +0 -0
  339. wagtail/test/search/models.py +0 -137
  340. /wagtail/admin/templates/wagtailadmin/{pages/listing/_ordering_cell.html → tables/ordering_cell.html} +0 -0
  341. /wagtail/{search/checks.py → checks.py} +0 -0
  342. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/WHEEL +0 -0
  343. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/entry_points.txt +0 -0
  344. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/licenses/LICENSE +0 -0
  345. {wagtail-7.1.1.dist-info → wagtail-7.2rc1.dist-info}/top_level.txt +0 -0
@@ -7,6 +7,7 @@ from django.urls import reverse
7
7
 
8
8
  from wagtail.admin.utils import get_keyboard_key_labels_from_request
9
9
  from wagtail.test.utils import WagtailTestUtils
10
+ from wagtail.users.models import UserProfile
10
11
 
11
12
 
12
13
  class TestGetKeyboardKeyLabelsFromRequestUtil(TestCase):
@@ -65,7 +66,8 @@ class TestGetKeyboardKeyLabelsFromRequestUtil(TestCase):
65
66
 
66
67
  class TestKeyboardShortcutsDialog(WagtailTestUtils, TestCase):
67
68
  def setUp(self):
68
- self.login()
69
+ self.test_user = self.create_test_user()
70
+ self.login(user=self.test_user)
69
71
 
70
72
  def test_keyboard_shortcuts_trigger_in_sidebar(self):
71
73
  response = self.client.get(reverse("wagtailadmin_home"))
@@ -83,7 +85,8 @@ class TestKeyboardShortcutsDialog(WagtailTestUtils, TestCase):
83
85
  "role": "button",
84
86
  "data-a11y-dialog-show": "keyboard-shortcuts-dialog",
85
87
  "data-action": "w-action#noop:prevent:stop",
86
- "data-controller": "w-action",
88
+ "data-controller": "w-kbd w-action",
89
+ "data-w-kbd-key-value": "?",
87
90
  }
88
91
  ),
89
92
  sidebar_data,
@@ -151,6 +154,53 @@ class TestKeyboardShortcutsDialog(WagtailTestUtils, TestCase):
151
154
  self.assertNotIn("comments", shortcuts_dialog.prettify())
152
155
  self.assertNotIn("Ctrl + Alt + m", all_shortcuts_text)
153
156
 
157
+ def test_account_link_in_modal(self):
158
+ """
159
+ Test that the 'account' link fragment is correctly rendered in the
160
+ keyboard shortcuts modal.
161
+ """
162
+ response = self.client.get(reverse("wagtailadmin_home"))
163
+ self.assertEqual(response.status_code, 200)
164
+
165
+ soup = self.get_soup(response.content)
166
+ shortcuts_dialog = soup.select_one("#keyboard-shortcuts-dialog")
167
+ self.assertIsNotNone(shortcuts_dialog)
168
+
169
+ account_link = shortcuts_dialog.select_one("a[href$='account/']")
170
+ self.assertIsNotNone(account_link)
171
+ self.assertEqual(account_link.text.strip(), "account")
172
+ self.assertIn("w-underline", account_link.get("class", []))
173
+
174
+ def test_modal_shows_disabled_info_when_keyboard_shortcuts_disabled(self):
175
+ """
176
+ Modal should open and show warning if keyboard shortcuts are disabled.
177
+ """
178
+ profile = UserProfile.get_for_user(self.test_user)
179
+ profile.keyboard_shortcuts = False
180
+ profile.save()
181
+
182
+ response = self.client.get(reverse("wagtailadmin_home"))
183
+
184
+ soup = self.get_soup(response.content)
185
+ shortcuts_dialog = soup.select_one("#keyboard-shortcuts-dialog")
186
+ self.assertIn(
187
+ "Keyboard shortcuts are currently disabled", shortcuts_dialog.prettify()
188
+ )
189
+
190
+ def test_modal_shows_enabled_info_when_shortcuts_enabled(self):
191
+ """
192
+ Modal should show normal info when keyboard shortcuts are enabled.
193
+ """
194
+ profile = UserProfile.get_for_user(self.test_user)
195
+ response = self.client.get(reverse("wagtailadmin_home"))
196
+ soup = self.get_soup(response.content)
197
+ shortcuts_dialog = soup.select_one("#keyboard-shortcuts-dialog")
198
+
199
+ self.assertTrue(profile.keyboard_shortcuts)
200
+ self.assertIn(
201
+ "Keyboard shortcuts are currently enabled", shortcuts_dialog.prettify()
202
+ )
203
+
154
204
 
155
205
  class TestMacKeyboardShortcutsDialog(WagtailTestUtils, TestCase):
156
206
  def setUp(self):
@@ -411,11 +411,9 @@ class TestMenuRendering(WagtailTestUtils, TestCase):
411
411
  "important-pages-generic-setting",
412
412
  "redirects",
413
413
  "important-pages-site-setting",
414
- "workflow-tasks",
415
414
  "promoted-search-results",
416
415
  "icon-generic-setting",
417
416
  "file-site-setting",
418
- "workflows",
419
417
  "file-generic-setting",
420
418
  }
421
419
 
@@ -69,14 +69,14 @@ class TestSetPrivacyView(WagtailTestUtils, TestCase):
69
69
  """
70
70
  This tests that a blank form is returned when a user opens the set_privacy view on a public page
71
71
  """
72
- response = self.client.get(
73
- reverse("wagtailadmin_pages:set_privacy", args=(self.public_page.id,))
74
- )
72
+ url = reverse("wagtailadmin_pages:set_privacy", args=(self.public_page.id,))
73
+ response = self.client.get(url)
75
74
 
76
75
  # Check response
77
76
  self.assertEqual(response.status_code, 200)
78
- self.assertTemplateUsed(response, "wagtailadmin/page_privacy/set_privacy.html")
79
- self.assertEqual(response.context["page"].specific, self.public_page)
77
+ self.assertTemplateUsed(response, "wagtailadmin/shared/set_privacy.html")
78
+ self.assertEqual(response.context["object"].specific, self.public_page)
79
+ self.assertEqual(response.context["action_url"], url)
80
80
 
81
81
  # Check form attributes
82
82
  self.assertEqual(response.context["form"]["restriction_type"].value(), "none")
@@ -86,14 +86,14 @@ class TestSetPrivacyView(WagtailTestUtils, TestCase):
86
86
  This tests that the restriction type and password fields as set correctly
87
87
  when a user opens the set_privacy view on a public page
88
88
  """
89
- response = self.client.get(
90
- reverse("wagtailadmin_pages:set_privacy", args=(self.private_page.id,))
91
- )
89
+ url = reverse("wagtailadmin_pages:set_privacy", args=(self.private_page.id,))
90
+ response = self.client.get(url)
92
91
 
93
92
  # Check response
94
93
  self.assertEqual(response.status_code, 200)
95
- self.assertTemplateUsed(response, "wagtailadmin/page_privacy/set_privacy.html")
96
- self.assertEqual(response.context["page"].specific, self.private_page)
94
+ self.assertTemplateUsed(response, "wagtailadmin/shared/set_privacy.html")
95
+ self.assertEqual(response.context["object"].specific, self.private_page)
96
+ self.assertEqual(response.context["action_url"], url)
97
97
 
98
98
  # Check form attributes
99
99
  self.assertEqual(
@@ -254,16 +254,16 @@ class TestSetPrivacyView(WagtailTestUtils, TestCase):
254
254
  """
255
255
  This tests that the restriction type and group fields as set correctly when a user opens the set_privacy view on a public page
256
256
  """
257
- response = self.client.get(
258
- reverse(
259
- "wagtailadmin_pages:set_privacy", args=(self.private_groups_page.id,)
260
- )
257
+ url = reverse(
258
+ "wagtailadmin_pages:set_privacy", args=(self.private_groups_page.id,)
261
259
  )
260
+ response = self.client.get(url)
262
261
 
263
262
  # Check response
264
263
  self.assertEqual(response.status_code, 200)
265
- self.assertTemplateUsed(response, "wagtailadmin/page_privacy/set_privacy.html")
266
- self.assertEqual(response.context["page"].specific, self.private_groups_page)
264
+ self.assertTemplateUsed(response, "wagtailadmin/shared/set_privacy.html")
265
+ self.assertEqual(response.context["object"].specific, self.private_groups_page)
266
+ self.assertEqual(response.context["action_url"], url)
267
267
 
268
268
  # Check form attributes
269
269
  self.assertEqual(response.context["form"]["restriction_type"].value(), "groups")
@@ -4,6 +4,7 @@ from datetime import datetime, timedelta
4
4
  from datetime import timezone as dt_timezone
5
5
  from unittest import mock
6
6
 
7
+ from django import forms
7
8
  from django.conf import settings
8
9
  from django.template import Context, Template, TemplateSyntaxError
9
10
  from django.test import SimpleTestCase, TestCase
@@ -1084,3 +1085,139 @@ class ThemeColorSchemeTest(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
1084
1085
  meta_tag = soup.find("meta", {"name": "color-scheme"})
1085
1086
  self.assertIsNotNone(meta_tag)
1086
1087
  self.assertEqual(meta_tag["content"], "light")
1088
+
1089
+
1090
+ class FormattedfieldTagTestCase(WagtailTestUtils, SimpleTestCase):
1091
+ def render_template(self, template, field_name="title", **context):
1092
+ class BasicForm(forms.Form):
1093
+ title = forms.CharField()
1094
+
1095
+ form = BasicForm(data={})
1096
+
1097
+ html = Template("{% load wagtailadmin_tags %}" + template).render(
1098
+ Context(
1099
+ {
1100
+ "field": form[field_name],
1101
+ **context,
1102
+ }
1103
+ )
1104
+ )
1105
+
1106
+ # Parse the rendered HTML for the first DOM element in the output
1107
+ return self.get_soup(html).find()
1108
+
1109
+ def test_basic_usage_with_full_html(self):
1110
+ soup = self.render_template("{% formattedfield field=field %}")
1111
+
1112
+ # check the outer wrapper attributes
1113
+ self.assertEqual(
1114
+ {"class": ["w-field__wrapper"], "data-field-wrapper": ""},
1115
+ soup.attrs,
1116
+ )
1117
+
1118
+ # check the label is correct
1119
+ label = soup.find("label")
1120
+ self.assertIsNotNone(label)
1121
+ self.assertEqual(label["for"], "id_title")
1122
+ self.assertEqual(label["id"], "id_title-label")
1123
+ self.assertEqual(label.get_text().strip(), "Title*")
1124
+
1125
+ # check there is an error container
1126
+ errors = soup.find("div", {"data-field-errors": ""})
1127
+ self.assertIsNotNone(errors)
1128
+ self.assertEqual(errors["class"], ["w-field__errors"])
1129
+ self.assertEqual(errors.get_text().strip(), "This field is required.")
1130
+
1131
+ # check there is a help container that is empty
1132
+ help_text = soup.find("div", {"data-field-help": ""})
1133
+ self.assertIsNotNone(help_text)
1134
+ self.assertEqual(help_text["class"], ["w-field__help"])
1135
+ self.assertEqual(help_text.get_text().strip(), "")
1136
+
1137
+ # check there is an input
1138
+ input = soup.find("input")
1139
+ self.assertIsNotNone(input)
1140
+ self.assertEqual(input["id"], "id_title")
1141
+ self.assertEqual(input["name"], "title")
1142
+ self.assertEqual(input["type"], "text")
1143
+ self.assertEqual(input["required"], "")
1144
+
1145
+ # check the input container & input siblings
1146
+ input_container = input.parent
1147
+ self.assertIsNone(input.find("svg")) # there should be no icon
1148
+ self.assertEqual(
1149
+ input_container.attrs, {"class": ["w-field__input"], "data-field-input": ""}
1150
+ ) # validate input container
1151
+
1152
+ def test_complex_usage_with_full_html(self):
1153
+ soup = self.render_template(
1154
+ """{% formattedfield field=field wrapper_id="__CUSTOM_ID__" classname="extra-custom-class" sr_only_label=True icon='search' help_text='This is a help text.' show_add_comment_button=True %}"""
1155
+ )
1156
+
1157
+ # check the outer wrapper attributes
1158
+ self.assertEqual(
1159
+ {
1160
+ "class": ["w-field__wrapper", "extra-custom-class"],
1161
+ "id": "__CUSTOM_ID__",
1162
+ "data-field-wrapper": "",
1163
+ },
1164
+ soup.attrs,
1165
+ )
1166
+
1167
+ # check the label is correct
1168
+ label = soup.find("label")
1169
+ self.assertIsNotNone(label)
1170
+ self.assertEqual(label["for"], "id_title")
1171
+ self.assertEqual(label["id"], "id_title-label")
1172
+ self.assertEqual(label.get_text().strip(), "Title*")
1173
+
1174
+ # check there is an error container
1175
+ errors = soup.find("div", {"data-field-errors": ""})
1176
+ self.assertIsNotNone(errors)
1177
+ self.assertEqual(errors["class"], ["w-field__errors"])
1178
+ self.assertEqual(errors.get_text().strip(), "This field is required.")
1179
+
1180
+ # check there is a help container that is empty
1181
+ help_text = soup.find("div", {"data-field-help": ""})
1182
+ self.assertIsNotNone(help_text)
1183
+ self.assertEqual(help_text["class"], ["w-field__help"])
1184
+ self.assertEqual(help_text.get_text().strip(), "This is a help text.")
1185
+
1186
+ # check there is an input
1187
+ input = soup.find("input")
1188
+ self.assertIsNotNone(input)
1189
+ self.assertEqual(input["id"], "id_title")
1190
+ self.assertEqual(input["name"], "title")
1191
+ self.assertEqual(input["type"], "text")
1192
+ self.assertEqual(input["required"], "")
1193
+
1194
+ # check the input container & input siblings
1195
+ input_container = input.parent
1196
+ self.assertEqual(
1197
+ input_container.find("svg").find("use")["href"], "#icon-search"
1198
+ ) # there should be an icon
1199
+ self.assertEqual(
1200
+ input_container.attrs, {"class": ["w-field__input"], "data-field-input": ""}
1201
+ ) # validate input container
1202
+
1203
+ def test_attrs_rendering(self):
1204
+ soup = self.render_template(
1205
+ """{% formattedfield field=field wrapper_id="__CUSTOM_ID__" classname="extra-custom-class" attrs=attrs %}""",
1206
+ attrs={
1207
+ "data-custom-attr": "custom-value",
1208
+ "data-controller": "w-example w-other",
1209
+ "data-field-wrapper": "__CANNOT_OVERRIDE_DEFAULT__",
1210
+ },
1211
+ )
1212
+
1213
+ self.assertEqual(
1214
+ {
1215
+ "class": ["w-field__wrapper", "extra-custom-class"],
1216
+ "id": "__CUSTOM_ID__",
1217
+ "data-custom-attr": "custom-value",
1218
+ "data-controller": "w-example w-other",
1219
+ # wrapper attribute should be preserved
1220
+ "data-field-wrapper": "",
1221
+ },
1222
+ soup.attrs,
1223
+ )
@@ -1,6 +1,5 @@
1
1
  import json
2
2
 
3
- from django.conf import settings
4
3
  from django.contrib.auth.models import AnonymousUser, Permission
5
4
  from django.template import Context, Template
6
5
  from django.test import TestCase, override_settings
@@ -9,7 +8,7 @@ from django.utils import translation
9
8
  from django.utils.translation import gettext
10
9
 
11
10
  from wagtail import hooks
12
- from wagtail.admin.templatetags.wagtailadmin_tags import absolute_static
11
+ from wagtail.admin.staticfiles import versioned_static
13
12
  from wagtail.admin.userbar import AccessibilityItem, Userbar
14
13
  from wagtail.coreutils import get_dummy_request
15
14
  from wagtail.models import PAGE_TEMPLATE_VAR, Locale, Page, Site
@@ -67,7 +66,7 @@ class TestUserbarTag(WagtailTestUtils, TestCase):
67
66
  # Wagtail admin core CSS should be linked with absolute URL to
68
67
  # ensure it works when loaded from a different domain
69
68
  # (e.g. headless frontend)
70
- absolute_static("wagtailadmin/css/core.css"),
69
+ f"http://localhost{versioned_static('wagtailadmin/css/core.css')}",
71
70
  # Custom CSS must be changed appropriately if necessary
72
71
  "/path/to/my/custom.css",
73
72
  ],
@@ -80,8 +79,8 @@ class TestUserbarTag(WagtailTestUtils, TestCase):
80
79
  # Wagtail vendor and userbar JS should be linked with absolute
81
80
  # URL to ensure it works when loaded from a different domain
82
81
  # (e.g. headless frontend)
83
- absolute_static("wagtailadmin/js/vendor.js"),
84
- absolute_static("wagtailadmin/js/userbar.js"),
82
+ f"http://localhost{versioned_static('wagtailadmin/js/vendor.js')}",
83
+ f"http://localhost{versioned_static('wagtailadmin/js/userbar.js')}",
85
84
  ],
86
85
  )
87
86
 
@@ -136,9 +135,7 @@ class TestUserbarTag(WagtailTestUtils, TestCase):
136
135
  # Should render the "Go to Wagtail admin" link using an absolute URL
137
136
  soup = self.get_soup(content)
138
137
  admin_url = reverse("wagtailadmin_home")
139
- admin_link = soup.select_one(
140
- f"a[href='{settings.WAGTAILADMIN_BASE_URL}{admin_url}']"
141
- )
138
+ admin_link = soup.select_one(f"a[href='http://localhost{admin_url}']")
142
139
  self.assertIsNotNone(admin_link)
143
140
  self.assertEqual(admin_link.text.strip(), "Go to Wagtail admin")
144
141
 
@@ -156,16 +153,12 @@ class TestUserbarTag(WagtailTestUtils, TestCase):
156
153
  soup = self.get_soup(content)
157
154
 
158
155
  edit_url = reverse("wagtailadmin_pages:edit", args=(self.homepage.id,))
159
- edit_link = soup.select_one(
160
- f"a[href='{settings.WAGTAILADMIN_BASE_URL}{edit_url}']"
161
- )
156
+ edit_link = soup.select_one(f"a[href='http://localhost{edit_url}']")
162
157
  self.assertIsNotNone(edit_link)
163
158
  self.assertEqual(edit_link.text.strip(), "Edit this page")
164
159
 
165
160
  explore_url = reverse("wagtailadmin_explore", args=(self.parent_page.id,))
166
- explore_link = soup.select_one(
167
- f"a[href='{settings.WAGTAILADMIN_BASE_URL}{explore_url}']"
168
- )
161
+ explore_link = soup.select_one(f"a[href='http://localhost{explore_url}']")
169
162
  self.assertIsNotNone(explore_link)
170
163
  self.assertEqual(explore_link.text.strip(), "Show in Explorer")
171
164
 
@@ -185,16 +178,12 @@ class TestUserbarTag(WagtailTestUtils, TestCase):
185
178
  soup = self.get_soup(content)
186
179
 
187
180
  edit_url = reverse("wagtailadmin_pages:edit", args=(self.homepage.id,))
188
- edit_link = soup.select_one(
189
- f"a[href='{settings.WAGTAILADMIN_BASE_URL}{edit_url}']"
190
- )
181
+ edit_link = soup.select_one(f"a[href='http://localhost{edit_url}']")
191
182
  self.assertIsNotNone(edit_link)
192
183
  self.assertEqual(edit_link.text.strip(), "Edit this page")
193
184
 
194
185
  explore_url = reverse("wagtailadmin_explore", args=(self.parent_page.id,))
195
- explore_link = soup.select_one(
196
- f"a[href='{settings.WAGTAILADMIN_BASE_URL}{explore_url}']"
197
- )
186
+ explore_link = soup.select_one(f"a[href='http://localhost{explore_url}']")
198
187
  self.assertIsNotNone(explore_link)
199
188
  self.assertEqual(explore_link.text.strip(), "Show in Explorer")
200
189
 
@@ -221,9 +210,7 @@ class TestUserbarTag(WagtailTestUtils, TestCase):
221
210
  # The explore link should still be visible
222
211
  soup = self.get_soup(content)
223
212
  explore_url = reverse("wagtailadmin_explore", args=(self.parent_page.id,))
224
- explore_link = soup.select_one(
225
- f"a[href='{settings.WAGTAILADMIN_BASE_URL}{explore_url}']"
226
- )
213
+ explore_link = soup.select_one(f"a[href='http://localhost{explore_url}']")
227
214
  self.assertIsNotNone(explore_link)
228
215
  self.assertEqual(explore_link.text.strip(), "Show in Explorer")
229
216
 
@@ -718,7 +705,7 @@ class TestUserbarAddLink(WagtailTestUtils, TestCase):
718
705
  self.assertEqual(response.status_code, 200)
719
706
 
720
707
  # page allows subpages, so the 'add page' button should show
721
- expected_url = settings.WAGTAILADMIN_BASE_URL + (
708
+ expected_url = self.request.build_absolute_uri(
722
709
  reverse("wagtailadmin_pages:add_subpage", args=(self.event_index.id,))
723
710
  )
724
711
  needle = f"""
@@ -762,7 +749,7 @@ class TestUserbarComponent(WagtailTestUtils, TestCase):
762
749
  items = soup.select("li")
763
750
  self.assertEqual(len(items), 2)
764
751
 
765
- admin_url = f"{settings.WAGTAILADMIN_BASE_URL}{reverse('wagtailadmin_home')}"
752
+ admin_url = f"http://localhost{reverse('wagtailadmin_home')}"
766
753
  admin_item = items[0]
767
754
  admin_link = admin_item.select_one("a")
768
755
  self.assertIsNotNone(admin_link)
@@ -780,11 +767,18 @@ class TestUserbarComponent(WagtailTestUtils, TestCase):
780
767
  def test_render_no_request(self):
781
768
  rendered = Userbar().render_html({})
782
769
  soup = self.get_soup(rendered)
770
+ # Without a request provided, URLs should fall back to the
771
+ # WAGTAILADMIN_BASE_URL setting
772
+ base_url = "http://testserver"
773
+
774
+ userbar = soup.select_one("[data-wagtail-userbar]")
775
+ self.assertIsNotNone(userbar)
776
+ self.assertEqual(userbar.get("data-wagtail-userbar-origin"), base_url)
783
777
 
784
778
  items = soup.select("li")
785
779
  self.assertEqual(len(items), 2)
786
780
 
787
- admin_url = f"{settings.WAGTAILADMIN_BASE_URL}{reverse('wagtailadmin_home')}"
781
+ admin_url = f"{base_url}{reverse('wagtailadmin_home')}"
788
782
  admin_item = items[0]
789
783
  admin_link = admin_item.select_one("a")
790
784
  self.assertIsNotNone(admin_link)
@@ -799,21 +793,44 @@ class TestUserbarComponent(WagtailTestUtils, TestCase):
799
793
  "Issues found | Accessibility",
800
794
  )
801
795
 
796
+ css_links = soup.select("link[rel='stylesheet']")
797
+ self.assertEqual(
798
+ [link.get("href") for link in css_links],
799
+ [
800
+ # Wagtail admin core CSS should be linked with absolute URL to
801
+ # ensure it works when loaded from a different domain
802
+ # (e.g. headless frontend)
803
+ f"{base_url}{versioned_static('wagtailadmin/css/core.css')}",
804
+ # Custom CSS must be changed appropriately if necessary
805
+ "/path/to/my/custom.css",
806
+ ],
807
+ )
808
+
809
+ scripts = soup.select("script[src]")
810
+ self.assertEqual(
811
+ [script.get("src") for script in scripts],
812
+ [
813
+ # Wagtail vendor and userbar JS should be linked with absolute
814
+ # URL to ensure it works when loaded from a different domain
815
+ # (e.g. headless frontend)
816
+ f"{base_url}{versioned_static('wagtailadmin/js/vendor.js')}",
817
+ f"{base_url}{versioned_static('wagtailadmin/js/userbar.js')}",
818
+ ],
819
+ )
820
+
802
821
  def test_render_minimal(self):
803
822
  rendered = Userbar().render_html({"request": self.request})
804
823
  soup = self.get_soup(rendered)
805
824
 
806
825
  userbar = soup.select_one("[data-wagtail-userbar]")
807
826
  self.assertIsNotNone(userbar)
808
- self.assertEqual(
809
- userbar.get("data-wagtail-userbar-origin"),
810
- settings.WAGTAILADMIN_BASE_URL,
811
- )
827
+ # The origin should be based on the request's scheme and host information
828
+ self.assertEqual(userbar.get("data-wagtail-userbar-origin"), "http://localhost")
812
829
 
813
830
  items = soup.select("li")
814
831
  self.assertEqual(len(items), 2)
815
832
 
816
- admin_url = f"{settings.WAGTAILADMIN_BASE_URL}{reverse('wagtailadmin_home')}"
833
+ admin_url = f"http://localhost{reverse('wagtailadmin_home')}"
817
834
  admin_item = items[0]
818
835
  admin_link = admin_item.select_one("a")
819
836
  self.assertIsNotNone(admin_link)
@@ -844,7 +861,7 @@ class TestUserbarComponent(WagtailTestUtils, TestCase):
844
861
  self.assertEqual(len(links), 4)
845
862
  self.assertEqual(
846
863
  [link.get("href") for link in links],
847
- [f"{settings.WAGTAILADMIN_BASE_URL}{url}" for url in expected_urls],
864
+ [f"http://localhost{url}" for url in expected_urls],
848
865
  )
849
866
 
850
867
  accessibility_button = soup.select_one("li button")
@@ -855,8 +872,8 @@ class TestUserbarComponent(WagtailTestUtils, TestCase):
855
872
  )
856
873
 
857
874
  @override_settings(WAGTAILADMIN_BASE_URL=None)
858
- def test_render_without_admin_base_url_setting(self):
859
- rendered = Userbar().render_html({"request": self.request})
875
+ def test_render_without_request_and_admin_base_url_setting(self):
876
+ rendered = Userbar().render_html({})
860
877
  soup = self.get_soup(rendered)
861
878
 
862
879
  userbar = soup.select_one("[data-wagtail-userbar]")
@@ -868,7 +885,8 @@ class TestUserbarComponent(WagtailTestUtils, TestCase):
868
885
  items = soup.select("li")
869
886
  self.assertEqual(len(items), 2)
870
887
 
871
- # Becomes the root-relative URL since WAGTAILADMIN_BASE_URL is None
888
+ # Becomes the root-relative URL since no request is provided
889
+ # and WAGTAILADMIN_BASE_URL is None
872
890
  admin_url = reverse("wagtailadmin_home")
873
891
  admin_item = items[0]
874
892
  admin_link = admin_item.select_one("a")
@@ -883,3 +901,25 @@ class TestUserbarComponent(WagtailTestUtils, TestCase):
883
901
  button.get_text(separator=" | ", strip=True).strip(),
884
902
  "Issues found | Accessibility",
885
903
  )
904
+
905
+ css_links = soup.select("link[rel='stylesheet']")
906
+ self.assertEqual(
907
+ [link.get("href") for link in css_links],
908
+ [
909
+ # Cannot build absolute URL without a request and
910
+ # WAGTAILADMIN_BASE_URL, so should be root-relative
911
+ versioned_static("wagtailadmin/css/core.css"),
912
+ "/path/to/my/custom.css",
913
+ ],
914
+ )
915
+
916
+ scripts = soup.select("script[src]")
917
+ self.assertEqual(
918
+ [script.get("src") for script in scripts],
919
+ [
920
+ # Cannot build absolute URL without a request and
921
+ # WAGTAILADMIN_BASE_URL, so should be root-relative
922
+ versioned_static("wagtailadmin/js/vendor.js"),
923
+ versioned_static("wagtailadmin/js/userbar.js"),
924
+ ],
925
+ )
@@ -2,6 +2,7 @@ from django.contrib.admin.utils import quote
2
2
  from django.test import TestCase
3
3
  from django.urls import reverse
4
4
 
5
+ from wagtail.test.testapp.models import ModelWithStringTypePrimaryKey
5
6
  from wagtail.test.utils import WagtailTestUtils
6
7
 
7
8
 
@@ -37,6 +38,39 @@ class TestGenericIndexViewWithoutModel(WagtailTestUtils, TestCase):
37
38
  self.assertEqual(response_object_count, 4)
38
39
 
39
40
 
41
+ class TestGenericCreateView(WagtailTestUtils, TestCase):
42
+ fixtures = ["test.json"]
43
+
44
+ def get(self, params={}):
45
+ return self.client.get(reverse("testapp_generic_create"), params)
46
+
47
+ def test_get_create_view(self):
48
+ response = self.get()
49
+ self.assertEqual(response.status_code, 200)
50
+ soup = self.get_soup(response.content)
51
+ h2 = soup.select_one("main h2")
52
+ self.assertIsNotNone(h2)
53
+ self.assertEqual(h2.text.strip(), "Model with string type primary key")
54
+ form = soup.select_one("main form")
55
+ self.assertIsNotNone(form)
56
+ id_input = form.select_one("input[name='custom_id']")
57
+ self.assertIsNotNone(id_input)
58
+ self.assertEqual(id_input.get("type"), "text")
59
+ content_input = form.select_one("input[name='content']")
60
+ self.assertIsNotNone(content_input)
61
+
62
+ def test_post_create_view(self):
63
+ post_data = {
64
+ "custom_id": "string-pk-3",
65
+ "content": "third modelwithstringtypeprimarykey model",
66
+ }
67
+ response = self.client.post(reverse("testapp_generic_create"), post_data)
68
+ self.assertEqual(response.status_code, 302) # Redirect to index view
69
+ self.assertTrue(
70
+ ModelWithStringTypePrimaryKey.objects.filter(pk="string-pk-3").exists()
71
+ )
72
+
73
+
40
74
  class TestGenericEditView(WagtailTestUtils, TestCase):
41
75
  fixtures = ["test.json"]
42
76
 
@@ -26,6 +26,7 @@ from wagtail.admin.utils import (
26
26
  get_latest_str,
27
27
  get_user_display_name,
28
28
  )
29
+ from wagtail.locks import BasicLock
29
30
  from wagtail.models import (
30
31
  GroupApprovalTask,
31
32
  GroupPagePermission,
@@ -41,6 +42,8 @@ from wagtail.models import (
41
42
  )
42
43
  from wagtail.signals import page_published, published
43
44
  from wagtail.test.testapp.models import (
45
+ CustomLockTask,
46
+ CustomWorkflowLock,
44
47
  FullFeaturedSnippet,
45
48
  ModeratedModel,
46
49
  MultiPreviewModesPage,
@@ -4851,3 +4854,34 @@ class TestWorkflowStateEmailNotifier(BasePageWorkflowTests):
4851
4854
  with self.subTest(f"Testing with {notification}_notifications"):
4852
4855
  notifier.notification = notification
4853
4856
  self.assertSetEqual(notifier.get_valid_recipients(self.object), set())
4857
+
4858
+
4859
+ class TestCustomWorkflowLockOnTask(BasePageWorkflowTests):
4860
+ def setup_workflow_and_tasks(self):
4861
+ self.workflow = Workflow.objects.create(name="test_workflow")
4862
+ self.task_1 = CustomLockTask.objects.create(name="test_task_1")
4863
+ WorkflowTask.objects.create(
4864
+ workflow=self.workflow, task=self.task_1, sort_order=1
4865
+ )
4866
+
4867
+ def test_custom_lock_class(self):
4868
+ self.post("submit")
4869
+ response = self.client.get(self.get_url("edit"))
4870
+ self.assertContains(response, "If there is a door, there must be a key")
4871
+ self.assertIsInstance(self.object.get_lock(), CustomWorkflowLock)
4872
+
4873
+ @mock.patch.object(CustomLockTask, "lock_class", new_callable=mock.PropertyMock)
4874
+ def test_typeerror_if_custom_lock_class_inherits_basic_locks(self, mock_property):
4875
+ mock_property.return_value = BasicLock
4876
+
4877
+ self.post("submit")
4878
+
4879
+ with self.assertRaises(TypeError):
4880
+ self.client.get(self.get_url("edit"))
4881
+
4882
+
4883
+ class TestCustomWorkflowLockOnTaskWithSnippets(
4884
+ TestCustomWorkflowLockOnTask,
4885
+ BaseSnippetWorkflowTests,
4886
+ ):
4887
+ pass