wagtail 6.1.2__py3-none-any.whl → 6.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 (644) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/actions/copy_for_translation.py +15 -1
  3. wagtail/admin/checks.py +20 -30
  4. wagtail/admin/forms/pages.py +10 -0
  5. wagtail/admin/icons.py +43 -0
  6. wagtail/admin/locale/ar/LC_MESSAGES/django.po +2 -2
  7. wagtail/admin/locale/be/LC_MESSAGES/django.po +7 -7
  8. wagtail/admin/locale/be/LC_MESSAGES/djangojs.po +3 -3
  9. wagtail/admin/locale/ca/LC_MESSAGES/django.mo +0 -0
  10. wagtail/admin/locale/ca/LC_MESSAGES/django.po +51 -50
  11. wagtail/admin/locale/cs/LC_MESSAGES/django.po +10 -8
  12. wagtail/admin/locale/de/LC_MESSAGES/django.po +49 -47
  13. wagtail/admin/locale/dv/LC_MESSAGES/django.po +8 -8
  14. wagtail/admin/locale/el/LC_MESSAGES/django.po +4 -4
  15. wagtail/admin/locale/en/LC_MESSAGES/django.po +405 -295
  16. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +21 -3
  17. wagtail/admin/locale/es/LC_MESSAGES/django.po +40 -39
  18. wagtail/admin/locale/es_419/LC_MESSAGES/django.po +8 -8
  19. wagtail/admin/locale/et/LC_MESSAGES/django.po +14 -12
  20. wagtail/admin/locale/fa/LC_MESSAGES/django.mo +0 -0
  21. wagtail/admin/locale/fa/LC_MESSAGES/django.po +93 -19
  22. wagtail/admin/locale/fa/LC_MESSAGES/djangojs.mo +0 -0
  23. wagtail/admin/locale/fa/LC_MESSAGES/djangojs.po +5 -1
  24. wagtail/admin/locale/fi/LC_MESSAGES/django.po +18 -18
  25. wagtail/admin/locale/fr/LC_MESSAGES/django.mo +0 -0
  26. wagtail/admin/locale/fr/LC_MESSAGES/django.po +134 -46
  27. wagtail/admin/locale/gl/LC_MESSAGES/django.po +55 -54
  28. wagtail/admin/locale/hr_HR/LC_MESSAGES/django.po +13 -12
  29. wagtail/admin/locale/hr_HR/LC_MESSAGES/djangojs.po +2 -2
  30. wagtail/admin/locale/hu/LC_MESSAGES/django.po +45 -42
  31. wagtail/admin/locale/id_ID/LC_MESSAGES/django.po +6 -6
  32. wagtail/admin/locale/is_IS/LC_MESSAGES/django.po +53 -51
  33. wagtail/admin/locale/it/LC_MESSAGES/django.po +56 -55
  34. wagtail/admin/locale/ja/LC_MESSAGES/django.po +2 -2
  35. wagtail/admin/locale/ko/LC_MESSAGES/django.po +18 -17
  36. wagtail/admin/locale/lt/LC_MESSAGES/django.po +4 -4
  37. wagtail/admin/locale/nb/LC_MESSAGES/django.po +22 -22
  38. wagtail/admin/locale/nl/LC_MESSAGES/django.po +44 -41
  39. wagtail/admin/locale/pl/LC_MESSAGES/django.po +49 -47
  40. wagtail/admin/locale/pl/LC_MESSAGES/djangojs.po +3 -3
  41. wagtail/admin/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  42. wagtail/admin/locale/pt_BR/LC_MESSAGES/django.po +46 -26
  43. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.po +21 -20
  44. wagtail/admin/locale/ro/LC_MESSAGES/django.po +51 -50
  45. wagtail/admin/locale/ru/LC_MESSAGES/django.po +60 -59
  46. wagtail/admin/locale/ru/LC_MESSAGES/djangojs.po +3 -3
  47. wagtail/admin/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  48. wagtail/admin/locale/sk_SK/LC_MESSAGES/django.po +15 -5
  49. wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
  50. wagtail/admin/locale/sl/LC_MESSAGES/django.po +86 -39
  51. wagtail/admin/locale/sl/LC_MESSAGES/djangojs.po +2 -2
  52. wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
  53. wagtail/admin/locale/sv/LC_MESSAGES/django.po +117 -41
  54. wagtail/admin/locale/sv/LC_MESSAGES/djangojs.mo +0 -0
  55. wagtail/admin/locale/sv/LC_MESSAGES/djangojs.po +5 -2
  56. wagtail/admin/locale/tet/LC_MESSAGES/django.po +2 -2
  57. wagtail/admin/locale/th/LC_MESSAGES/django.po +4 -4
  58. wagtail/admin/locale/tr/LC_MESSAGES/django.po +4 -4
  59. wagtail/admin/locale/tr_TR/LC_MESSAGES/django.po +4 -4
  60. wagtail/admin/locale/uk/LC_MESSAGES/django.po +11 -10
  61. wagtail/admin/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  62. wagtail/admin/locale/zh_Hans/LC_MESSAGES/django.po +84 -13
  63. wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.po +2 -2
  64. wagtail/admin/menu.py +2 -2
  65. wagtail/admin/migrations/0004_editingsession.py +57 -0
  66. wagtail/admin/migrations/0005_editingsession_is_editing.py +18 -0
  67. wagtail/admin/models.py +36 -3
  68. wagtail/admin/rich_text/editors/draftail/__init__.py +2 -20
  69. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  70. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  71. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  72. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  73. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  74. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  75. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  76. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  77. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  78. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +1 -1
  79. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  80. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  81. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  82. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  83. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  84. wagtail/admin/static/wagtailadmin/js/preview-panel.js +2 -1
  85. wagtail/admin/static/wagtailadmin/js/preview-panel.js.LICENSE.txt +11 -0
  86. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  87. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  88. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  89. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  90. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  91. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  92. wagtail/admin/static/wagtailadmin/js/userbar.js +2 -1
  93. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +11 -0
  94. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  95. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +0 -12
  96. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  97. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  98. wagtail/admin/templates/wagtailadmin/collection_privacy/ancestor_privacy.html +2 -6
  99. wagtail/admin/templates/wagtailadmin/generic/index_results.html +1 -17
  100. wagtail/admin/templates/wagtailadmin/generic/listing_results.html +20 -1
  101. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +2 -11
  102. wagtail/admin/templates/wagtailadmin/page_privacy/ancestor_privacy.html +2 -6
  103. wagtail/admin/templates/wagtailadmin/page_privacy/no_privacy.html +2 -0
  104. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -1
  105. wagtail/admin/templates/wagtailadmin/pages/action_menu/menu.html +1 -1
  106. wagtail/admin/templates/wagtailadmin/reports/aging_pages_results.html +54 -0
  107. wagtail/admin/templates/wagtailadmin/reports/base_page_report.html +1 -17
  108. wagtail/admin/templates/wagtailadmin/reports/base_page_report_results.html +10 -0
  109. wagtail/admin/templates/wagtailadmin/reports/base_report.html +1 -40
  110. wagtail/admin/templates/wagtailadmin/reports/base_report_results.html +1 -0
  111. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_report.html +21 -27
  112. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_types_usage.html +48 -54
  113. wagtail/admin/templates/wagtailadmin/reports/{locked_pages.html → locked_pages_results.html} +3 -3
  114. wagtail/admin/templates/wagtailadmin/reports/page_types_usage_results.html +10 -0
  115. wagtail/admin/templates/wagtailadmin/reports/site_history_results.html +53 -0
  116. wagtail/admin/templates/wagtailadmin/reports/workflow_results.html +74 -0
  117. wagtail/admin/templates/wagtailadmin/reports/workflow_tasks_results.html +56 -0
  118. wagtail/admin/templates/wagtailadmin/shared/_workflow_init.html +8 -44
  119. wagtail/admin/templates/wagtailadmin/shared/avatar.html +11 -1
  120. wagtail/admin/templates/wagtailadmin/shared/dialog/dialog.html +5 -4
  121. wagtail/admin/templates/wagtailadmin/shared/dropdown/dropdown_button.html +2 -1
  122. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/list.html +132 -0
  123. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/module.html +44 -0
  124. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +7 -1
  125. wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +1 -1
  126. wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html +32 -16
  127. wagtail/admin/templates/wagtailadmin/skeleton.html +1 -1
  128. wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +9 -11
  129. wagtail/admin/templatetags/wagtailadmin_tags.py +13 -2
  130. wagtail/admin/tests/formats/en/__init__.py +0 -0
  131. wagtail/admin/tests/formats/en/formats.py +1 -0
  132. wagtail/admin/tests/pages/test_create_page.py +47 -0
  133. wagtail/admin/tests/pages/test_edit_page.py +10 -8
  134. wagtail/admin/tests/pages/test_parent_page_chooser_view.py +45 -1
  135. wagtail/admin/tests/test_checks.py +53 -3
  136. wagtail/admin/tests/test_collections_views.py +62 -1
  137. wagtail/admin/tests/test_edit_handlers.py +37 -0
  138. wagtail/admin/tests/test_editing_sessions.py +1336 -0
  139. wagtail/admin/tests/test_icon_sprite.py +12 -21
  140. wagtail/admin/tests/test_page_chooser.py +309 -7
  141. wagtail/admin/tests/test_privacy.py +82 -0
  142. wagtail/admin/tests/test_reports_views.py +464 -70
  143. wagtail/admin/tests/test_userbar.py +93 -6
  144. wagtail/admin/tests/test_workflows.py +223 -33
  145. wagtail/admin/tests/viewsets/test_model_viewset.py +151 -2
  146. wagtail/admin/ui/editing_sessions.py +57 -0
  147. wagtail/admin/urls/__init__.py +9 -15
  148. wagtail/admin/urls/editing_sessions.py +17 -0
  149. wagtail/admin/urls/reports.py +33 -1
  150. wagtail/admin/userbar.py +77 -20
  151. wagtail/admin/views/chooser.py +49 -22
  152. wagtail/admin/views/collections.py +0 -11
  153. wagtail/admin/views/editing_sessions.py +193 -0
  154. wagtail/admin/views/generic/__init__.py +1 -0
  155. wagtail/admin/views/generic/base.py +3 -2
  156. wagtail/admin/views/generic/history.py +9 -3
  157. wagtail/admin/views/generic/mixins.py +44 -3
  158. wagtail/admin/views/generic/models.py +46 -72
  159. wagtail/admin/views/generic/permissions.py +20 -10
  160. wagtail/admin/views/home.py +2 -31
  161. wagtail/admin/views/page_privacy.py +20 -5
  162. wagtail/admin/views/pages/choose_parent.py +62 -0
  163. wagtail/admin/views/pages/edit.py +28 -0
  164. wagtail/admin/views/reports/aging_pages.py +6 -10
  165. wagtail/admin/views/reports/audit_logging.py +13 -42
  166. wagtail/admin/views/reports/base.py +31 -4
  167. wagtail/admin/views/reports/locked_pages.py +5 -8
  168. wagtail/admin/views/reports/page_types_usage.py +6 -10
  169. wagtail/admin/views/reports/workflows.py +36 -12
  170. wagtail/admin/viewsets/base.py +8 -3
  171. wagtail/admin/viewsets/chooser.py +1 -1
  172. wagtail/admin/viewsets/model.py +26 -1
  173. wagtail/admin/wagtail_hooks.py +2 -1
  174. wagtail/api/v2/filters.py +6 -0
  175. wagtail/api/v2/tests/test_documents.py +1 -1
  176. wagtail/api/v2/tests/test_images.py +1 -1
  177. wagtail/api/v2/tests/test_pages.py +11 -1
  178. wagtail/api/v2/utils.py +2 -2
  179. wagtail/blocks/base.py +35 -12
  180. wagtail/blocks/definition_lookup.py +85 -0
  181. wagtail/blocks/list_block.py +12 -0
  182. wagtail/blocks/migrations/migrate_operation.py +2 -0
  183. wagtail/blocks/stream_block.py +19 -0
  184. wagtail/blocks/struct_block.py +19 -0
  185. wagtail/contrib/forms/locale/be/LC_MESSAGES/django.po +3 -3
  186. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +1 -1
  187. wagtail/contrib/forms/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  188. wagtail/contrib/forms/locale/pl/LC_MESSAGES/django.po +3 -3
  189. wagtail/contrib/forms/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  190. wagtail/contrib/forms/locale/pt_BR/LC_MESSAGES/django.po +2 -2
  191. wagtail/contrib/forms/locale/ru/LC_MESSAGES/django.po +3 -3
  192. wagtail/contrib/forms/locale/sl/LC_MESSAGES/django.po +2 -2
  193. wagtail/contrib/frontend_cache/backends/__init__.py +5 -0
  194. wagtail/contrib/frontend_cache/backends/azure.py +179 -0
  195. wagtail/contrib/frontend_cache/backends/base.py +28 -0
  196. wagtail/contrib/frontend_cache/backends/cloudflare.py +114 -0
  197. wagtail/contrib/frontend_cache/backends/cloudfront.py +99 -0
  198. wagtail/contrib/frontend_cache/backends/http.py +64 -0
  199. wagtail/contrib/frontend_cache/tests.py +59 -17
  200. wagtail/contrib/frontend_cache/utils.py +26 -8
  201. wagtail/contrib/redirects/filters.py +15 -1
  202. wagtail/contrib/redirects/locale/ar/LC_MESSAGES/django.po +4 -4
  203. wagtail/contrib/redirects/locale/be/LC_MESSAGES/django.po +3 -3
  204. wagtail/contrib/redirects/locale/bg/LC_MESSAGES/django.po +4 -4
  205. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.po +4 -4
  206. wagtail/contrib/redirects/locale/cs/LC_MESSAGES/django.po +4 -4
  207. wagtail/contrib/redirects/locale/cy/LC_MESSAGES/django.po +6 -6
  208. wagtail/contrib/redirects/locale/de/LC_MESSAGES/django.po +5 -4
  209. wagtail/contrib/redirects/locale/el/LC_MESSAGES/django.po +7 -7
  210. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +37 -72
  211. wagtail/contrib/redirects/locale/es/LC_MESSAGES/django.po +6 -6
  212. wagtail/contrib/redirects/locale/et/LC_MESSAGES/django.po +4 -4
  213. wagtail/contrib/redirects/locale/fa/LC_MESSAGES/django.mo +0 -0
  214. wagtail/contrib/redirects/locale/fa/LC_MESSAGES/django.po +18 -5
  215. wagtail/contrib/redirects/locale/fi/LC_MESSAGES/django.po +6 -6
  216. wagtail/contrib/redirects/locale/fr/LC_MESSAGES/django.po +4 -4
  217. wagtail/contrib/redirects/locale/gl/LC_MESSAGES/django.po +4 -4
  218. wagtail/contrib/redirects/locale/hr_HR/LC_MESSAGES/django.po +8 -8
  219. wagtail/contrib/redirects/locale/hu/LC_MESSAGES/django.po +6 -6
  220. wagtail/contrib/redirects/locale/id_ID/LC_MESSAGES/django.po +4 -4
  221. wagtail/contrib/redirects/locale/is_IS/LC_MESSAGES/django.po +6 -6
  222. wagtail/contrib/redirects/locale/it/LC_MESSAGES/django.po +4 -4
  223. wagtail/contrib/redirects/locale/ja/LC_MESSAGES/django.po +2 -2
  224. wagtail/contrib/redirects/locale/ko/LC_MESSAGES/django.po +4 -4
  225. wagtail/contrib/redirects/locale/lt/LC_MESSAGES/django.po +4 -4
  226. wagtail/contrib/redirects/locale/mn/LC_MESSAGES/django.po +4 -4
  227. wagtail/contrib/redirects/locale/nb/LC_MESSAGES/django.po +4 -4
  228. wagtail/contrib/redirects/locale/nl/LC_MESSAGES/django.po +4 -4
  229. wagtail/contrib/redirects/locale/pl/LC_MESSAGES/django.po +9 -9
  230. wagtail/contrib/redirects/locale/pt_BR/LC_MESSAGES/django.po +5 -5
  231. wagtail/contrib/redirects/locale/pt_PT/LC_MESSAGES/django.po +4 -4
  232. wagtail/contrib/redirects/locale/ro/LC_MESSAGES/django.po +4 -4
  233. wagtail/contrib/redirects/locale/ru/LC_MESSAGES/django.po +7 -7
  234. wagtail/contrib/redirects/locale/sk_SK/LC_MESSAGES/django.po +4 -4
  235. wagtail/contrib/redirects/locale/sl/LC_MESSAGES/django.po +6 -6
  236. wagtail/contrib/redirects/locale/sr/LC_MESSAGES/django.po +2 -2
  237. wagtail/contrib/redirects/locale/sv/LC_MESSAGES/django.po +4 -4
  238. wagtail/contrib/redirects/locale/tet/LC_MESSAGES/django.po +4 -4
  239. wagtail/contrib/redirects/locale/th/LC_MESSAGES/django.po +4 -4
  240. wagtail/contrib/redirects/locale/tr/LC_MESSAGES/django.po +4 -4
  241. wagtail/contrib/redirects/locale/tr_TR/LC_MESSAGES/django.po +4 -4
  242. wagtail/contrib/redirects/locale/uk/LC_MESSAGES/django.po +4 -4
  243. wagtail/contrib/redirects/locale/zh/LC_MESSAGES/django.po +4 -4
  244. wagtail/contrib/redirects/locale/zh_Hans/LC_MESSAGES/django.po +4 -4
  245. wagtail/contrib/redirects/locale/zh_Hant/LC_MESSAGES/django.po +4 -4
  246. wagtail/contrib/redirects/models.py +6 -5
  247. wagtail/contrib/redirects/templates/wagtailredirects/edit.html +1 -38
  248. wagtail/contrib/redirects/tests/test_redirects.py +141 -1
  249. wagtail/contrib/redirects/urls.py +1 -2
  250. wagtail/contrib/redirects/views.py +39 -80
  251. wagtail/contrib/routable_page/models.py +6 -4
  252. wagtail/contrib/routable_page/tests.py +11 -0
  253. wagtail/contrib/search_promotions/locale/ar/LC_MESSAGES/django.po +4 -4
  254. wagtail/contrib/search_promotions/locale/be/LC_MESSAGES/django.po +7 -7
  255. wagtail/contrib/search_promotions/locale/ca/LC_MESSAGES/django.po +8 -8
  256. wagtail/contrib/search_promotions/locale/cs/LC_MESSAGES/django.po +2 -2
  257. wagtail/contrib/search_promotions/locale/cy/LC_MESSAGES/django.po +4 -4
  258. wagtail/contrib/search_promotions/locale/de/LC_MESSAGES/django.po +6 -6
  259. wagtail/contrib/search_promotions/locale/el/LC_MESSAGES/django.po +4 -4
  260. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +1 -1
  261. wagtail/contrib/search_promotions/locale/es/LC_MESSAGES/django.po +6 -6
  262. wagtail/contrib/search_promotions/locale/et/LC_MESSAGES/django.po +4 -4
  263. wagtail/contrib/search_promotions/locale/fa/LC_MESSAGES/django.po +4 -4
  264. wagtail/contrib/search_promotions/locale/fi/LC_MESSAGES/django.po +6 -6
  265. wagtail/contrib/search_promotions/locale/fr/LC_MESSAGES/django.mo +0 -0
  266. wagtail/contrib/search_promotions/locale/fr/LC_MESSAGES/django.po +9 -6
  267. wagtail/contrib/search_promotions/locale/gl/LC_MESSAGES/django.po +6 -6
  268. wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.po +8 -8
  269. wagtail/contrib/search_promotions/locale/hu/LC_MESSAGES/django.po +4 -4
  270. wagtail/contrib/search_promotions/locale/id_ID/LC_MESSAGES/django.po +4 -4
  271. wagtail/contrib/search_promotions/locale/is_IS/LC_MESSAGES/django.po +8 -8
  272. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +6 -6
  273. wagtail/contrib/search_promotions/locale/ja/LC_MESSAGES/django.po +4 -4
  274. wagtail/contrib/search_promotions/locale/ko/LC_MESSAGES/django.po +4 -4
  275. wagtail/contrib/search_promotions/locale/lt/LC_MESSAGES/django.po +6 -6
  276. wagtail/contrib/search_promotions/locale/nb/LC_MESSAGES/django.po +4 -4
  277. wagtail/contrib/search_promotions/locale/nl/LC_MESSAGES/django.po +6 -6
  278. wagtail/contrib/search_promotions/locale/pl/LC_MESSAGES/django.po +9 -9
  279. wagtail/contrib/search_promotions/locale/pt_BR/LC_MESSAGES/django.po +4 -4
  280. wagtail/contrib/search_promotions/locale/pt_PT/LC_MESSAGES/django.po +4 -4
  281. wagtail/contrib/search_promotions/locale/ro/LC_MESSAGES/django.po +4 -4
  282. wagtail/contrib/search_promotions/locale/ru/LC_MESSAGES/django.po +9 -9
  283. wagtail/contrib/search_promotions/locale/sk_SK/LC_MESSAGES/django.po +4 -4
  284. wagtail/contrib/search_promotions/locale/sl/LC_MESSAGES/django.mo +0 -0
  285. wagtail/contrib/search_promotions/locale/sl/LC_MESSAGES/django.po +62 -8
  286. wagtail/contrib/search_promotions/locale/sr/LC_MESSAGES/django.po +2 -2
  287. wagtail/contrib/search_promotions/locale/sv/LC_MESSAGES/django.mo +0 -0
  288. wagtail/contrib/search_promotions/locale/sv/LC_MESSAGES/django.po +9 -6
  289. wagtail/contrib/search_promotions/locale/th/LC_MESSAGES/django.po +4 -4
  290. wagtail/contrib/search_promotions/locale/tr/LC_MESSAGES/django.po +4 -4
  291. wagtail/contrib/search_promotions/locale/tr_TR/LC_MESSAGES/django.po +4 -4
  292. wagtail/contrib/search_promotions/locale/uk/LC_MESSAGES/django.po +4 -4
  293. wagtail/contrib/search_promotions/locale/zh_Hans/LC_MESSAGES/django.po +2 -2
  294. wagtail/contrib/search_promotions/locale/zh_Hant/LC_MESSAGES/django.po +2 -2
  295. wagtail/contrib/settings/locale/be/LC_MESSAGES/django.po +3 -3
  296. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +4 -4
  297. wagtail/contrib/settings/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  298. wagtail/contrib/settings/locale/pl/LC_MESSAGES/django.po +3 -3
  299. wagtail/contrib/settings/locale/pt_BR/LC_MESSAGES/django.po +1 -1
  300. wagtail/contrib/settings/locale/ru/LC_MESSAGES/django.po +3 -3
  301. wagtail/contrib/settings/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  302. wagtail/contrib/settings/locale/sk_SK/LC_MESSAGES/django.po +10 -1
  303. wagtail/contrib/settings/locale/sl/LC_MESSAGES/django.po +2 -2
  304. wagtail/contrib/settings/locale/sr/LC_MESSAGES/django.po +2 -2
  305. wagtail/contrib/simple_translation/locale/be/LC_MESSAGES/django.po +3 -3
  306. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +5 -1
  307. wagtail/contrib/simple_translation/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  308. wagtail/contrib/simple_translation/locale/pl/LC_MESSAGES/django.po +3 -3
  309. wagtail/contrib/simple_translation/locale/pt_BR/LC_MESSAGES/django.po +1 -1
  310. wagtail/contrib/simple_translation/locale/ru/LC_MESSAGES/django.po +3 -3
  311. wagtail/contrib/simple_translation/locale/sl/LC_MESSAGES/django.po +2 -2
  312. wagtail/contrib/simple_translation/models.py +2 -1
  313. wagtail/contrib/styleguide/locale/be/LC_MESSAGES/django.po +3 -3
  314. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  315. wagtail/contrib/styleguide/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  316. wagtail/contrib/styleguide/locale/pl/LC_MESSAGES/django.po +3 -3
  317. wagtail/contrib/styleguide/locale/ru/LC_MESSAGES/django.po +3 -3
  318. wagtail/contrib/styleguide/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  319. wagtail/contrib/styleguide/locale/sk_SK/LC_MESSAGES/django.po +5 -1
  320. wagtail/contrib/styleguide/locale/sl/LC_MESSAGES/django.po +2 -2
  321. wagtail/contrib/styleguide/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  322. wagtail/contrib/styleguide/locale/zh_Hans/LC_MESSAGES/django.po +3 -0
  323. wagtail/contrib/table_block/locale/be/LC_MESSAGES/django.po +3 -3
  324. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  325. wagtail/contrib/table_block/locale/fr/LC_MESSAGES/django.mo +0 -0
  326. wagtail/contrib/table_block/locale/fr/LC_MESSAGES/django.po +27 -1
  327. wagtail/contrib/table_block/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  328. wagtail/contrib/table_block/locale/pl/LC_MESSAGES/django.po +3 -3
  329. wagtail/contrib/table_block/locale/ru/LC_MESSAGES/django.po +3 -3
  330. wagtail/contrib/table_block/locale/sl/LC_MESSAGES/django.mo +0 -0
  331. wagtail/contrib/table_block/locale/sl/LC_MESSAGES/django.po +29 -3
  332. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  333. wagtail/contrib/typed_table_block/blocks.py +19 -0
  334. wagtail/contrib/typed_table_block/locale/be/LC_MESSAGES/django.po +3 -3
  335. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  336. wagtail/contrib/typed_table_block/locale/fr/LC_MESSAGES/django.mo +0 -0
  337. wagtail/contrib/typed_table_block/locale/fr/LC_MESSAGES/django.po +12 -1
  338. wagtail/contrib/typed_table_block/locale/pl/LC_MESSAGES/django.po +3 -3
  339. wagtail/contrib/typed_table_block/locale/pt_BR/LC_MESSAGES/django.po +1 -1
  340. wagtail/contrib/typed_table_block/locale/ru/LC_MESSAGES/django.po +3 -3
  341. wagtail/contrib/typed_table_block/locale/sl/LC_MESSAGES/django.mo +0 -0
  342. wagtail/contrib/typed_table_block/locale/sl/LC_MESSAGES/django.po +14 -4
  343. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  344. wagtail/contrib/typed_table_block/tests.py +38 -0
  345. wagtail/coreutils.py +1 -1
  346. wagtail/documents/__init__.py +1 -1
  347. wagtail/documents/locale/ar/LC_MESSAGES/django.po +8 -8
  348. wagtail/documents/locale/be/LC_MESSAGES/django.po +11 -11
  349. wagtail/documents/locale/bg/LC_MESSAGES/django.po +4 -4
  350. wagtail/documents/locale/ca/LC_MESSAGES/django.mo +0 -0
  351. wagtail/documents/locale/ca/LC_MESSAGES/django.po +12 -11
  352. wagtail/documents/locale/cs/LC_MESSAGES/django.po +6 -6
  353. wagtail/documents/locale/de/LC_MESSAGES/django.po +10 -8
  354. wagtail/documents/locale/el/LC_MESSAGES/django.po +8 -8
  355. wagtail/documents/locale/en/LC_MESSAGES/django.po +8 -8
  356. wagtail/documents/locale/es/LC_MESSAGES/django.po +8 -8
  357. wagtail/documents/locale/et/LC_MESSAGES/django.po +6 -6
  358. wagtail/documents/locale/fa/LC_MESSAGES/django.mo +0 -0
  359. wagtail/documents/locale/fa/LC_MESSAGES/django.po +17 -8
  360. wagtail/documents/locale/fi/LC_MESSAGES/django.po +10 -10
  361. wagtail/documents/locale/fr/LC_MESSAGES/django.po +6 -6
  362. wagtail/documents/locale/gl/LC_MESSAGES/django.po +8 -8
  363. wagtail/documents/locale/hr_HR/LC_MESSAGES/django.po +14 -14
  364. wagtail/documents/locale/hu/LC_MESSAGES/django.po +12 -12
  365. wagtail/documents/locale/id_ID/LC_MESSAGES/django.po +6 -6
  366. wagtail/documents/locale/is_IS/LC_MESSAGES/django.po +8 -8
  367. wagtail/documents/locale/it/LC_MESSAGES/django.po +8 -8
  368. wagtail/documents/locale/ja/LC_MESSAGES/django.po +8 -8
  369. wagtail/documents/locale/ko/LC_MESSAGES/django.po +8 -8
  370. wagtail/documents/locale/lt/LC_MESSAGES/django.po +8 -8
  371. wagtail/documents/locale/mi/LC_MESSAGES/django.po +10 -10
  372. wagtail/documents/locale/mn/LC_MESSAGES/django.po +5 -4
  373. wagtail/documents/locale/nb/LC_MESSAGES/django.po +6 -6
  374. wagtail/documents/locale/nl/LC_MESSAGES/django.po +8 -8
  375. wagtail/documents/locale/pl/LC_MESSAGES/django.po +11 -11
  376. wagtail/documents/locale/pt_BR/LC_MESSAGES/django.po +8 -8
  377. wagtail/documents/locale/pt_PT/LC_MESSAGES/django.po +8 -8
  378. wagtail/documents/locale/ro/LC_MESSAGES/django.po +8 -8
  379. wagtail/documents/locale/ru/LC_MESSAGES/django.po +9 -9
  380. wagtail/documents/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  381. wagtail/documents/locale/sk_SK/LC_MESSAGES/django.po +55 -12
  382. wagtail/documents/locale/sl/LC_MESSAGES/django.mo +0 -0
  383. wagtail/documents/locale/sl/LC_MESSAGES/django.po +30 -10
  384. wagtail/documents/locale/sr/LC_MESSAGES/django.po +2 -2
  385. wagtail/documents/locale/sv/LC_MESSAGES/django.po +6 -6
  386. wagtail/documents/locale/th/LC_MESSAGES/django.po +8 -8
  387. wagtail/documents/locale/tr/LC_MESSAGES/django.po +8 -8
  388. wagtail/documents/locale/tr_TR/LC_MESSAGES/django.po +8 -8
  389. wagtail/documents/locale/uk/LC_MESSAGES/django.po +8 -8
  390. wagtail/documents/locale/zh/LC_MESSAGES/django.po +2 -2
  391. wagtail/documents/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  392. wagtail/documents/locale/zh_Hans/LC_MESSAGES/django.po +11 -5
  393. wagtail/documents/locale/zh_Hant/LC_MESSAGES/django.po +4 -4
  394. wagtail/documents/models.py +5 -1
  395. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  396. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  397. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  398. wagtail/documents/tests/test_models.py +5 -1
  399. wagtail/embeds/apps.py +2 -0
  400. wagtail/embeds/embeds.py +12 -14
  401. wagtail/embeds/finders/__init__.py +2 -0
  402. wagtail/embeds/finders/facebook.py +17 -33
  403. wagtail/embeds/finders/instagram.py +19 -16
  404. wagtail/embeds/locale/be/LC_MESSAGES/django.po +3 -3
  405. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  406. wagtail/embeds/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  407. wagtail/embeds/locale/pl/LC_MESSAGES/django.po +3 -3
  408. wagtail/embeds/locale/ru/LC_MESSAGES/django.po +3 -3
  409. wagtail/embeds/locale/sl/LC_MESSAGES/django.po +2 -2
  410. wagtail/embeds/signal_handlers.py +13 -0
  411. wagtail/embeds/tests/test_embeds.py +7 -7
  412. wagtail/fields.py +58 -14
  413. wagtail/images/__init__.py +1 -1
  414. wagtail/images/locale/ar/LC_MESSAGES/django.po +6 -6
  415. wagtail/images/locale/be/LC_MESSAGES/django.po +3 -3
  416. wagtail/images/locale/ca/LC_MESSAGES/django.po +8 -8
  417. wagtail/images/locale/cs/LC_MESSAGES/django.po +8 -8
  418. wagtail/images/locale/cy/LC_MESSAGES/django.po +8 -8
  419. wagtail/images/locale/de/LC_MESSAGES/django.po +6 -6
  420. wagtail/images/locale/el/LC_MESSAGES/django.po +8 -8
  421. wagtail/images/locale/en/LC_MESSAGES/django.po +34 -34
  422. wagtail/images/locale/es/LC_MESSAGES/django.po +10 -10
  423. wagtail/images/locale/et/LC_MESSAGES/django.po +8 -8
  424. wagtail/images/locale/fa/LC_MESSAGES/django.mo +0 -0
  425. wagtail/images/locale/fa/LC_MESSAGES/django.po +28 -9
  426. wagtail/images/locale/fi/LC_MESSAGES/django.po +8 -8
  427. wagtail/images/locale/fr/LC_MESSAGES/django.po +6 -6
  428. wagtail/images/locale/gl/LC_MESSAGES/django.po +8 -8
  429. wagtail/images/locale/hr_HR/LC_MESSAGES/django.po +8 -8
  430. wagtail/images/locale/hu/LC_MESSAGES/django.po +8 -8
  431. wagtail/images/locale/id_ID/LC_MESSAGES/django.po +6 -6
  432. wagtail/images/locale/is_IS/LC_MESSAGES/django.po +8 -8
  433. wagtail/images/locale/it/LC_MESSAGES/django.po +8 -8
  434. wagtail/images/locale/ja/LC_MESSAGES/django.po +8 -8
  435. wagtail/images/locale/ko/LC_MESSAGES/django.po +6 -6
  436. wagtail/images/locale/lt/LC_MESSAGES/django.po +8 -8
  437. wagtail/images/locale/nb/LC_MESSAGES/django.po +8 -8
  438. wagtail/images/locale/nl/LC_MESSAGES/django.po +8 -8
  439. wagtail/images/locale/pl/LC_MESSAGES/django.po +11 -11
  440. wagtail/images/locale/pt_BR/LC_MESSAGES/django.po +8 -8
  441. wagtail/images/locale/pt_PT/LC_MESSAGES/django.po +8 -8
  442. wagtail/images/locale/ro/LC_MESSAGES/django.po +8 -8
  443. wagtail/images/locale/ru/LC_MESSAGES/django.po +11 -11
  444. wagtail/images/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  445. wagtail/images/locale/sk_SK/LC_MESSAGES/django.po +23 -8
  446. wagtail/images/locale/sl/LC_MESSAGES/django.mo +0 -0
  447. wagtail/images/locale/sl/LC_MESSAGES/django.po +44 -12
  448. wagtail/images/locale/sv/LC_MESSAGES/django.mo +0 -0
  449. wagtail/images/locale/sv/LC_MESSAGES/django.po +20 -8
  450. wagtail/images/locale/tet/LC_MESSAGES/django.po +4 -4
  451. wagtail/images/locale/th/LC_MESSAGES/django.po +8 -8
  452. wagtail/images/locale/tr/LC_MESSAGES/django.po +6 -6
  453. wagtail/images/locale/tr_TR/LC_MESSAGES/django.po +6 -6
  454. wagtail/images/locale/uk/LC_MESSAGES/django.po +8 -8
  455. wagtail/images/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  456. wagtail/images/locale/zh_Hans/LC_MESSAGES/django.po +49 -5
  457. wagtail/images/locale/zh_Hant/LC_MESSAGES/django.po +4 -4
  458. wagtail/images/models.py +3 -0
  459. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  460. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  461. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  462. wagtail/images/templates/wagtailimages/images/edit.html +4 -4
  463. wagtail/images/tests/test_admin_views.py +58 -2
  464. wagtail/images/tests/test_image_operations.py +12 -0
  465. wagtail/images/tests/tests.py +27 -2
  466. wagtail/images/views/chooser.py +6 -1
  467. wagtail/images/views/images.py +7 -3
  468. wagtail/images/views/serve.py +1 -0
  469. wagtail/locale/be/LC_MESSAGES/django.po +3 -3
  470. wagtail/locale/en/LC_MESSAGES/django.po +84 -80
  471. wagtail/locale/fa/LC_MESSAGES/django.mo +0 -0
  472. wagtail/locale/fa/LC_MESSAGES/django.po +3 -0
  473. wagtail/locale/fr/LC_MESSAGES/django.mo +0 -0
  474. wagtail/locale/fr/LC_MESSAGES/django.po +23 -4
  475. wagtail/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  476. wagtail/locale/pl/LC_MESSAGES/django.po +3 -3
  477. wagtail/locale/pt_BR/LC_MESSAGES/django.po +1 -1
  478. wagtail/locale/ru/LC_MESSAGES/django.po +3 -3
  479. wagtail/locale/sl/LC_MESSAGES/django.po +2 -2
  480. wagtail/locale/sv/LC_MESSAGES/django.mo +0 -0
  481. wagtail/locale/sv/LC_MESSAGES/django.po +11 -2
  482. wagtail/locales/locale/be/LC_MESSAGES/django.po +3 -3
  483. wagtail/locales/locale/en/LC_MESSAGES/django.po +2 -2
  484. wagtail/locales/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  485. wagtail/locales/locale/pl/LC_MESSAGES/django.po +3 -3
  486. wagtail/locales/locale/ru/LC_MESSAGES/django.po +3 -3
  487. wagtail/locales/locale/sl/LC_MESSAGES/django.po +2 -2
  488. wagtail/locales/tests.py +16 -0
  489. wagtail/locales/wagtail_hooks.py +0 -9
  490. wagtail/migrations/0094_alter_page_locale.py +19 -0
  491. wagtail/models/__init__.py +11 -5
  492. wagtail/models/i18n.py +6 -1
  493. wagtail/project_template/requirements.txt +1 -1
  494. wagtail/search/locale/be/LC_MESSAGES/django.po +3 -3
  495. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  496. wagtail/search/locale/fa/LC_MESSAGES/django.mo +0 -0
  497. wagtail/search/locale/fa/LC_MESSAGES/django.po +24 -0
  498. wagtail/search/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  499. wagtail/search/locale/pl/LC_MESSAGES/django.po +3 -3
  500. wagtail/search/locale/pt_BR/LC_MESSAGES/django.mo +0 -0
  501. wagtail/search/locale/pt_BR/LC_MESSAGES/django.po +2 -2
  502. wagtail/search/locale/ru/LC_MESSAGES/django.po +3 -3
  503. wagtail/search/locale/sl/LC_MESSAGES/django.po +2 -2
  504. wagtail/search/tests/test_queries.py +24 -0
  505. wagtail/search/utils.py +6 -12
  506. wagtail/signals.py +4 -0
  507. wagtail/sites/locale/be/LC_MESSAGES/django.po +3 -3
  508. wagtail/sites/locale/en/LC_MESSAGES/django.po +2 -2
  509. wagtail/sites/locale/hr_HR/LC_MESSAGES/django.po +2 -2
  510. wagtail/sites/locale/pl/LC_MESSAGES/django.po +3 -3
  511. wagtail/sites/locale/ru/LC_MESSAGES/django.po +3 -3
  512. wagtail/sites/locale/sl/LC_MESSAGES/django.po +2 -2
  513. wagtail/sites/locale/sr/LC_MESSAGES/django.po +2 -2
  514. wagtail/sites/tests.py +15 -0
  515. wagtail/sites/wagtail_hooks.py +0 -9
  516. wagtail/snippets/locale/be/LC_MESSAGES/django.po +3 -3
  517. wagtail/snippets/locale/ca/LC_MESSAGES/django.po +6 -6
  518. wagtail/snippets/locale/de/LC_MESSAGES/django.po +5 -5
  519. wagtail/snippets/locale/en/LC_MESSAGES/django.po +9 -9
  520. wagtail/snippets/locale/es/LC_MESSAGES/django.po +6 -6
  521. wagtail/snippets/locale/fa/LC_MESSAGES/django.mo +0 -0
  522. wagtail/snippets/locale/fa/LC_MESSAGES/django.po +17 -1
  523. wagtail/snippets/locale/fr/LC_MESSAGES/django.po +5 -5
  524. wagtail/snippets/locale/gl/LC_MESSAGES/django.po +6 -6
  525. wagtail/snippets/locale/hr_HR/LC_MESSAGES/django.po +8 -8
  526. wagtail/snippets/locale/hu/LC_MESSAGES/django.po +7 -7
  527. wagtail/snippets/locale/is_IS/LC_MESSAGES/django.po +5 -5
  528. wagtail/snippets/locale/it/LC_MESSAGES/django.po +5 -5
  529. wagtail/snippets/locale/ko/LC_MESSAGES/django.po +6 -6
  530. wagtail/snippets/locale/nb/LC_MESSAGES/django.po +6 -6
  531. wagtail/snippets/locale/nl/LC_MESSAGES/django.po +8 -8
  532. wagtail/snippets/locale/pl/LC_MESSAGES/django.po +9 -9
  533. wagtail/snippets/locale/pt_BR/LC_MESSAGES/django.po +1 -1
  534. wagtail/snippets/locale/ro/LC_MESSAGES/django.po +6 -6
  535. wagtail/snippets/locale/ru/LC_MESSAGES/django.po +8 -8
  536. wagtail/snippets/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  537. wagtail/snippets/locale/sk_SK/LC_MESSAGES/django.po +3 -0
  538. wagtail/snippets/locale/sl/LC_MESSAGES/django.po +8 -8
  539. wagtail/snippets/locale/sr/LC_MESSAGES/django.po +2 -2
  540. wagtail/snippets/locale/sv/LC_MESSAGES/django.po +6 -6
  541. wagtail/snippets/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  542. wagtail/snippets/locale/zh_Hans/LC_MESSAGES/django.po +40 -1
  543. wagtail/snippets/permissions.py +5 -3
  544. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  545. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  546. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu.html +1 -1
  547. wagtail/snippets/tests/test_snippets.py +78 -12
  548. wagtail/snippets/tests/test_viewset.py +22 -0
  549. wagtail/snippets/views/snippets.py +19 -14
  550. wagtail/snippets/wagtail_hooks.py +2 -10
  551. wagtail/templatetags/wagtailcore_tags.py +3 -0
  552. wagtail/test/dummy_external_storage.py +1 -1
  553. wagtail/test/i18n/migrations/0003_alter_clusterabletestmodel_locale_and_more.py +40 -0
  554. wagtail/test/routablepage/models.py +4 -0
  555. wagtail/test/snippets/migrations/0012_alter_translatablesnippet_locale.py +20 -0
  556. wagtail/test/testapp/migrations/0038_sociallink.py +52 -0
  557. wagtail/test/testapp/migrations/0039_alter_eventcategory_locale_and_more.py +45 -0
  558. wagtail/test/testapp/models.py +24 -0
  559. wagtail/test/testapp/views.py +1 -0
  560. wagtail/test/testapp/wagtail_hooks.py +9 -0
  561. wagtail/test/urls_multilang.py +6 -1
  562. wagtail/test/urls_multilang_non_root.py +11 -0
  563. wagtail/tests/streamfield_migrations/test_migrations.py +53 -12
  564. wagtail/tests/test_audit_log.py +72 -2
  565. wagtail/tests/test_blocks.py +103 -0
  566. wagtail/tests/test_signals.py +49 -2
  567. wagtail/tests/test_streamfield.py +153 -0
  568. wagtail/tests/test_utils.py +42 -1
  569. wagtail/tests/tests.py +5 -0
  570. wagtail/users/apps.py +1 -0
  571. wagtail/users/forms.py +7 -0
  572. wagtail/users/locale/ar/LC_MESSAGES/django.po +4 -4
  573. wagtail/users/locale/be/LC_MESSAGES/django.po +9 -9
  574. wagtail/users/locale/ca/LC_MESSAGES/django.po +8 -8
  575. wagtail/users/locale/cs/LC_MESSAGES/django.po +6 -6
  576. wagtail/users/locale/cy/LC_MESSAGES/django.po +8 -8
  577. wagtail/users/locale/de/LC_MESSAGES/django.po +8 -8
  578. wagtail/users/locale/el/LC_MESSAGES/django.po +4 -4
  579. wagtail/users/locale/en/LC_MESSAGES/django.po +55 -50
  580. wagtail/users/locale/es/LC_MESSAGES/django.po +8 -8
  581. wagtail/users/locale/et/LC_MESSAGES/django.po +8 -8
  582. wagtail/users/locale/fa/LC_MESSAGES/django.mo +0 -0
  583. wagtail/users/locale/fa/LC_MESSAGES/django.po +69 -7
  584. wagtail/users/locale/fi/LC_MESSAGES/django.po +8 -8
  585. wagtail/users/locale/fr/LC_MESSAGES/django.mo +0 -0
  586. wagtail/users/locale/fr/LC_MESSAGES/django.po +52 -11
  587. wagtail/users/locale/gl/LC_MESSAGES/django.po +8 -8
  588. wagtail/users/locale/hr_HR/LC_MESSAGES/django.po +10 -10
  589. wagtail/users/locale/hu/LC_MESSAGES/django.po +8 -8
  590. wagtail/users/locale/id_ID/LC_MESSAGES/django.po +4 -4
  591. wagtail/users/locale/is_IS/LC_MESSAGES/django.po +8 -8
  592. wagtail/users/locale/it/LC_MESSAGES/django.po +8 -8
  593. wagtail/users/locale/ja/LC_MESSAGES/django.po +4 -4
  594. wagtail/users/locale/ko/LC_MESSAGES/django.po +4 -4
  595. wagtail/users/locale/lt/LC_MESSAGES/django.po +4 -4
  596. wagtail/users/locale/lv/LC_MESSAGES/django.po +4 -4
  597. wagtail/users/locale/mn/LC_MESSAGES/django.po +5 -4
  598. wagtail/users/locale/nb/LC_MESSAGES/django.po +8 -8
  599. wagtail/users/locale/nl/LC_MESSAGES/django.po +6 -6
  600. wagtail/users/locale/pl/LC_MESSAGES/django.po +9 -9
  601. wagtail/users/locale/pt_BR/LC_MESSAGES/django.po +9 -9
  602. wagtail/users/locale/pt_PT/LC_MESSAGES/django.po +8 -8
  603. wagtail/users/locale/ro/LC_MESSAGES/django.po +8 -8
  604. wagtail/users/locale/ru/LC_MESSAGES/django.po +11 -11
  605. wagtail/users/locale/sk_SK/LC_MESSAGES/django.mo +0 -0
  606. wagtail/users/locale/sk_SK/LC_MESSAGES/django.po +12 -5
  607. wagtail/users/locale/sl/LC_MESSAGES/django.mo +0 -0
  608. wagtail/users/locale/sl/LC_MESSAGES/django.po +56 -12
  609. wagtail/users/locale/sv/LC_MESSAGES/django.mo +0 -0
  610. wagtail/users/locale/sv/LC_MESSAGES/django.po +49 -10
  611. wagtail/users/locale/tet/LC_MESSAGES/django.po +4 -4
  612. wagtail/users/locale/th/LC_MESSAGES/django.po +4 -4
  613. wagtail/users/locale/tr/LC_MESSAGES/django.po +4 -4
  614. wagtail/users/locale/tr_TR/LC_MESSAGES/django.po +4 -4
  615. wagtail/users/locale/uk/LC_MESSAGES/django.po +8 -8
  616. wagtail/users/locale/zh_Hans/LC_MESSAGES/django.mo +0 -0
  617. wagtail/users/locale/zh_Hans/LC_MESSAGES/django.po +49 -5
  618. wagtail/users/locale/zh_Hant/LC_MESSAGES/django.po +2 -2
  619. wagtail/users/models.py +1 -0
  620. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +3 -2
  621. wagtail/users/templatetags/wagtailusers_tags.py +9 -0
  622. wagtail/users/tests/__init__.py +7 -1
  623. wagtail/users/tests/test_admin_views.py +117 -32
  624. wagtail/users/views/groups.py +4 -0
  625. wagtail/users/views/users.py +58 -14
  626. wagtail/users/wagtail_hooks.py +7 -123
  627. wagtail/utils/utils.py +27 -0
  628. wagtail/utils/version.py +5 -2
  629. wagtail-6.2rc1.dist-info/METADATA +78 -0
  630. {wagtail-6.1.2.dist-info → wagtail-6.2rc1.dist-info}/RECORD +634 -607
  631. {wagtail-6.1.2.dist-info → wagtail-6.2rc1.dist-info}/WHEEL +1 -1
  632. wagtail/admin/templates/wagtailadmin/reports/aging_pages.html +0 -58
  633. wagtail/admin/templates/wagtailadmin/reports/page_types_usage.html +0 -18
  634. wagtail/admin/templates/wagtailadmin/reports/site_history.html +0 -57
  635. wagtail/admin/templates/wagtailadmin/reports/workflow.html +0 -81
  636. wagtail/admin/templates/wagtailadmin/reports/workflow_tasks.html +0 -63
  637. wagtail/contrib/frontend_cache/backends.py +0 -400
  638. wagtail/contrib/redirects/templates/wagtailredirects/list.html +0 -43
  639. wagtail/contrib/redirects/templates/wagtailredirects/reports/redirects_report.html +0 -14
  640. wagtail/contrib/redirects/tests/test_reports_view.py +0 -82
  641. wagtail-6.1.2.dist-info/METADATA +0 -78
  642. {wagtail-6.1.2.dist-info → wagtail-6.2rc1.dist-info}/LICENSE +0 -0
  643. {wagtail-6.1.2.dist-info → wagtail-6.2rc1.dist-info}/entry_points.txt +0 -0
  644. {wagtail-6.1.2.dist-info → wagtail-6.2rc1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,193 @@
1
+ from django.apps import apps
2
+ from django.contrib.admin.utils import unquote
3
+ from django.contrib.contenttypes.models import ContentType
4
+ from django.core.exceptions import ValidationError
5
+ from django.http import Http404, JsonResponse
6
+ from django.shortcuts import get_object_or_404
7
+ from django.urls import reverse
8
+ from django.utils import timezone
9
+ from django.views.decorators.http import require_POST
10
+
11
+ from wagtail.admin.models import EditingSession
12
+ from wagtail.admin.ui.editing_sessions import EditingSessionsList
13
+ from wagtail.admin.utils import get_user_display_name
14
+ from wagtail.models import Page, Revision, RevisionMixin
15
+ from wagtail.permissions import page_permission_policy
16
+
17
+
18
+ @require_POST
19
+ def ping(request, app_label, model_name, object_id, session_id):
20
+ try:
21
+ model = apps.get_model(app_label, model_name)
22
+ except LookupError:
23
+ raise Http404
24
+
25
+ unquoted_object_id = unquote(object_id)
26
+
27
+ content_type = ContentType.objects.get_for_model(model)
28
+
29
+ obj = get_object_or_404(model, pk=unquoted_object_id)
30
+ if isinstance(obj, Page):
31
+ permission_policy = page_permission_policy
32
+ else:
33
+ try:
34
+ permission_policy = model.snippet_viewset.permission_policy
35
+ except AttributeError:
36
+ # model is neither a Page nor a snippet
37
+ raise Http404
38
+
39
+ if not permission_policy.user_has_permission_for_instance(
40
+ request.user, "change", obj
41
+ ):
42
+ raise Http404
43
+
44
+ try:
45
+ session = EditingSession.objects.get(
46
+ id=session_id,
47
+ user=request.user,
48
+ content_type=content_type,
49
+ object_id=unquoted_object_id,
50
+ )
51
+ except EditingSession.DoesNotExist:
52
+ session = EditingSession(
53
+ content_type=content_type,
54
+ object_id=unquoted_object_id,
55
+ user=request.user,
56
+ )
57
+
58
+ session.last_seen_at = timezone.now()
59
+ session.is_editing = request.POST.get("is_editing", False)
60
+ try:
61
+ session.full_clean()
62
+ except ValidationError:
63
+ return JsonResponse({"error": "Invalid data"}, status=400)
64
+ else:
65
+ session.save()
66
+
67
+ other_sessions = (
68
+ EditingSession.objects.filter(
69
+ content_type=content_type,
70
+ object_id=unquoted_object_id,
71
+ last_seen_at__gte=timezone.now() - timezone.timedelta(minutes=1),
72
+ )
73
+ .exclude(id=session.id)
74
+ .select_related("user", "user__wagtail_userprofile")
75
+ .order_by("-last_seen_at")
76
+ )
77
+
78
+ # create a lookup of sessions indexed by user ID. Multiple sessions from the same user
79
+ # are merged, such that the most recently seen one is reported, but is_editing is true
80
+ # if any session has the editing flag set (not just the latest one).
81
+ other_sessions_lookup = {}
82
+ for other_session in other_sessions:
83
+ try:
84
+ other_session_info = other_sessions_lookup[other_session.user.pk]
85
+ except KeyError:
86
+ other_sessions_lookup[other_session.user.pk] = {
87
+ "session_id": other_session.id,
88
+ "user": other_session.user,
89
+ "last_seen_at": other_session.last_seen_at,
90
+ "is_editing": other_session.is_editing,
91
+ "revision_id": None,
92
+ }
93
+ else:
94
+ if other_session.is_editing:
95
+ other_session_info["is_editing"] = True
96
+
97
+ revision_id = request.POST.get("revision_id", None)
98
+ if revision_id is not None and issubclass(model, RevisionMixin):
99
+ all_revisions = obj.revisions.defer("content")
100
+ try:
101
+ original_revision = all_revisions.get(id=revision_id)
102
+ except Revision.DoesNotExist:
103
+ raise Http404
104
+
105
+ newest_revision = (
106
+ all_revisions.filter(created_at__gt=original_revision.created_at)
107
+ .order_by("-created_at", "-pk")
108
+ .select_related("user")
109
+ .first()
110
+ )
111
+
112
+ if newest_revision:
113
+ try:
114
+ session_info = other_sessions_lookup[newest_revision.user_id]
115
+ except KeyError:
116
+ other_sessions_lookup[newest_revision.user_id] = {
117
+ "session_id": None,
118
+ "user": newest_revision.user,
119
+ "last_seen_at": newest_revision.created_at,
120
+ "is_editing": False,
121
+ "revision_id": newest_revision.id,
122
+ }
123
+ else:
124
+ session_info["revision_id"] = newest_revision.id
125
+ if newest_revision.created_at > session_info["last_seen_at"]:
126
+ session_info["last_seen_at"] = newest_revision.created_at
127
+
128
+ try:
129
+ users_other_session = other_sessions_lookup[request.user.pk]
130
+ except KeyError:
131
+ pass
132
+ else:
133
+ # If the user has a different session that is not editing and hasn't
134
+ # created the latest revision, hide it as it's not relevant.
135
+ if (
136
+ not users_other_session["is_editing"]
137
+ and not users_other_session["revision_id"]
138
+ ):
139
+ other_sessions_lookup.pop(request.user.pk)
140
+
141
+ # Sort the other sessions so that they are presented in the following order:
142
+ # 1. Prioritise any session with the latest revision. Then,
143
+ # 2. Prioritise any session that is currently editing. Then,
144
+ # 3. Prioritise any session with the smallest id, so that new sessions are
145
+ # appended to the end of the list (they're shown last). We are not sorting
146
+ # by last_seen_at to avoid shifting the order of the sessions as they
147
+ # ping the server.
148
+ other_sessions = sorted(
149
+ other_sessions_lookup.values(),
150
+ key=lambda other_session: (
151
+ # We want to sort revision_id and is_editing in descending order,
152
+ # but we want to sort session_id in ascending order. To achieve this
153
+ # in a single pass, we negate the values of revision_id and is_editing.
154
+ # We can negate revision_id because there can only be one (at most)
155
+ # session with revision_id, so we only care about the presence and
156
+ # not the ID itself, thus we can treat it as a boolean flag.
157
+ not other_session["revision_id"],
158
+ not other_session["is_editing"],
159
+ other_session["session_id"],
160
+ ),
161
+ )
162
+
163
+ return JsonResponse(
164
+ {
165
+ "session_id": session.id,
166
+ "ping_url": reverse(
167
+ "wagtailadmin_editing_sessions:ping",
168
+ args=(app_label, model_name, object_id, session.id),
169
+ ),
170
+ "release_url": reverse(
171
+ "wagtailadmin_editing_sessions:release", args=(session.id,)
172
+ ),
173
+ "other_sessions": [
174
+ {
175
+ "session_id": other_session["session_id"],
176
+ "user": get_user_display_name(other_session["user"]),
177
+ "last_seen_at": other_session["last_seen_at"].isoformat(),
178
+ "is_editing": other_session["is_editing"],
179
+ "revision_id": other_session["revision_id"],
180
+ }
181
+ for other_session in other_sessions
182
+ ],
183
+ "html": EditingSessionsList(
184
+ session, other_sessions, content_type
185
+ ).render_html(),
186
+ }
187
+ )
188
+
189
+
190
+ @require_POST
191
+ def release(request, session_id):
192
+ EditingSession.objects.filter(id=session_id, user=request.user).delete()
193
+ return JsonResponse({})
@@ -4,6 +4,7 @@ from .base import ( # noqa: F401
4
4
  BaseOperationView,
5
5
  WagtailAdminTemplateMixin,
6
6
  )
7
+ from .history import HistoryView # noqa: F401
7
8
  from .mixins import ( # noqa: F401
8
9
  BeforeAfterHookMixin,
9
10
  CreateEditViewOptionalFeaturesMixin,
@@ -23,6 +23,7 @@ from wagtail.admin import messages
23
23
  from wagtail.admin.ui.tables import Column, Table
24
24
  from wagtail.admin.utils import get_valid_next_url_from_request
25
25
  from wagtail.admin.widgets.button import ButtonWithDropdown
26
+ from wagtail.utils.utils import flatten_choices
26
27
 
27
28
 
28
29
  class WagtailAdminTemplateMixin(TemplateResponseMixin, ContextMixin):
@@ -288,7 +289,7 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
288
289
  )
289
290
  )
290
291
  elif isinstance(filter_def, MultipleChoiceFilter):
291
- choices = {str(id): label for id, label in filter_def.field.choices}
292
+ choices = flatten_choices(filter_def.field.choices)
292
293
  for item in value:
293
294
  filters.append(
294
295
  ActiveFilter(
@@ -326,7 +327,7 @@ class BaseListingView(WagtailAdminTemplateMixin, BaseListView):
326
327
  )
327
328
  )
328
329
  elif isinstance(filter_def, ChoiceFilter):
329
- choices = {str(id): label for id, label in filter_def.field.choices}
330
+ choices = flatten_choices(filter_def.field.choices)
330
331
  filters.append(
331
332
  ActiveFilter(
332
333
  bound_field.auto_id,
@@ -60,18 +60,24 @@ class HistoryFilterSet(WagtailFilterSet):
60
60
 
61
61
  def __init__(self, *args, **kwargs):
62
62
  super().__init__(*args, **kwargs)
63
- actions = get_actions_for_filter(self.queryset)
63
+ actions = self.get_action_choices()
64
64
  if not actions:
65
65
  del self.filters["action"]
66
66
  else:
67
67
  self.filters["action"].extra["choices"] = actions
68
68
 
69
- users = self.queryset.get_users()
69
+ users = self.get_users_queryset()
70
70
  if not users.exists():
71
71
  del self.filters["user"]
72
72
  else:
73
73
  self.filters["user"].extra["queryset"] = users
74
74
 
75
+ def get_action_choices(self):
76
+ return get_actions_for_filter(self.queryset)
77
+
78
+ def get_users_queryset(self):
79
+ return self.queryset.get_users()
80
+
75
81
 
76
82
  class ActionColumn(Column):
77
83
  def __init__(self, *args, object, url_names, user_can_unschedule, **kwargs):
@@ -221,7 +227,7 @@ class HistoryView(PermissionCheckedMixin, BaseObjectMixin, BaseListingView):
221
227
  },
222
228
  user_can_unschedule=self.user_can_unschedule(),
223
229
  ),
224
- LogEntryUserColumn("user", width="25%"),
230
+ LogEntryUserColumn("user", label=gettext_lazy("User"), width="25%"),
225
231
  DateColumn("timestamp", label=gettext_lazy("Date"), width="15%"),
226
232
  ]
227
233
 
@@ -2,6 +2,7 @@ import json
2
2
 
3
3
  from django.conf import settings
4
4
  from django.contrib.admin.utils import quote
5
+ from django.contrib.contenttypes.models import ContentType
5
6
  from django.db import models, transaction
6
7
  from django.forms import Media
7
8
  from django.shortcuts import get_object_or_404
@@ -16,7 +17,9 @@ from django.utils.translation import gettext as _
16
17
 
17
18
  from wagtail import hooks
18
19
  from wagtail.admin import messages
20
+ from wagtail.admin.models import EditingSession
19
21
  from wagtail.admin.templatetags.wagtailadmin_tags import user_display_name
22
+ from wagtail.admin.ui.editing_sessions import EditingSessionsModule
20
23
  from wagtail.admin.ui.tables import TitleColumn
21
24
  from wagtail.admin.utils import get_latest_str, set_query_params
22
25
  from wagtail.locks import BasicLock, ScheduledForPublishLock, WorkflowLock
@@ -287,14 +290,19 @@ class CreateEditViewOptionalFeaturesMixin:
287
290
 
288
291
  def user_has_permission(self, permission):
289
292
  user = self.request.user
290
- if user.is_superuser:
291
- return True
292
-
293
293
  # Workflow lock/unlock methods take precedence before the base
294
294
  # "lock" and "unlock" permissions -- see PagePermissionTester for reference
295
295
  if permission == "lock" and self.current_workflow_task:
296
+ # Follow the logic in PagePermissionTester.user_can_lock()
297
+ # (superusers can always lock)
298
+ if user.is_superuser:
299
+ return True
296
300
  return self.current_workflow_task.user_can_lock(self.object, user)
297
301
  if permission == "unlock":
302
+ # Follow the logic in PagePermissionTester.user_can_unlock()
303
+ # (superusers can always unlock)
304
+ if user.is_superuser:
305
+ return True
298
306
  # Allow unlocking even if the user does not have the 'unlock' permission
299
307
  # if they are the user who locked the object
300
308
  if self.object.locked_by_id == user.pk:
@@ -677,6 +685,38 @@ class CreateEditViewOptionalFeaturesMixin:
677
685
 
678
686
  return context
679
687
 
688
+ def get_editing_sessions(self):
689
+ if self.view_name == "create":
690
+ return None
691
+ EditingSession.cleanup()
692
+ content_type = ContentType.objects.get_for_model(self.model)
693
+ session = EditingSession.objects.create(
694
+ user=self.request.user,
695
+ content_type=content_type,
696
+ object_id=self.object.pk,
697
+ last_seen_at=timezone.now(),
698
+ )
699
+ revision_id = self.object.latest_revision_id if self.revision_enabled else None
700
+ return EditingSessionsModule(
701
+ session,
702
+ reverse(
703
+ "wagtailadmin_editing_sessions:ping",
704
+ args=(
705
+ self.model._meta.app_label,
706
+ self.model._meta.model_name,
707
+ quote(self.object.pk),
708
+ session.id,
709
+ ),
710
+ ),
711
+ reverse(
712
+ "wagtailadmin_editing_sessions:release",
713
+ args=(session.id,),
714
+ ),
715
+ [],
716
+ content_type,
717
+ revision_id,
718
+ )
719
+
680
720
  def get_context_data(self, **kwargs):
681
721
  context = super().get_context_data(**kwargs)
682
722
  context.update(self.get_lock_context())
@@ -691,6 +731,7 @@ class CreateEditViewOptionalFeaturesMixin:
691
731
  settings, "WAGTAIL_WORKFLOW_CANCEL_ON_PUBLISH", True
692
732
  ) and bool(self.workflow_tasks)
693
733
  context["revisions_compare_url_name"] = self.revisions_compare_url_name
734
+ context["editing_sessions"] = self.get_editing_sessions()
694
735
  return context
695
736
 
696
737
  def post(self, request, *args, **kwargs):
@@ -73,7 +73,7 @@ class IndexView(
73
73
  copy_url_name = None
74
74
  inspect_url_name = None
75
75
  delete_url_name = None
76
- any_permission_required = ["add", "change", "delete"]
76
+ any_permission_required = ["add", "change", "delete", "view"]
77
77
  search_fields = None
78
78
  search_backend_name = "default"
79
79
  is_searchable = None
@@ -249,15 +249,21 @@ class IndexView(
249
249
 
250
250
  def _get_title_column(self, field_name, column_class=TitleColumn, **kwargs):
251
251
  column_class = self._get_title_column_class(column_class)
252
+
253
+ def get_url(instance):
254
+ if edit_url := self.get_edit_url(instance):
255
+ return edit_url
256
+ return self.get_inspect_url(instance)
257
+
252
258
  if not self.model:
253
259
  return column_class(
254
260
  "name",
255
261
  label=gettext_lazy("Name"),
256
262
  accessor=str,
257
- get_url=self.get_edit_url,
263
+ get_url=get_url,
258
264
  )
259
265
  return self._get_custom_column(
260
- field_name, column_class, get_url=self.get_edit_url, **kwargs
266
+ field_name, column_class, get_url=get_url, **kwargs
261
267
  )
262
268
 
263
269
  def _get_custom_column(self, field_name, column_class=Column, **kwargs):
@@ -320,27 +326,25 @@ class IndexView(
320
326
  return columns
321
327
 
322
328
  def get_edit_url(self, instance):
323
- if self.edit_url_name:
329
+ if self.edit_url_name and self.user_has_permission("change"):
324
330
  return reverse(self.edit_url_name, args=(quote(instance.pk),))
325
331
 
326
332
  def get_copy_url(self, instance):
327
- if self.copy_url_name:
333
+ if self.copy_url_name and self.user_has_permission("add"):
328
334
  return reverse(self.copy_url_name, args=(quote(instance.pk),))
329
335
 
330
336
  def get_inspect_url(self, instance):
331
- if self.inspect_url_name:
337
+ if self.inspect_url_name and self.user_has_any_permission(
338
+ {"add", "change", "delete", "view"}
339
+ ):
332
340
  return reverse(self.inspect_url_name, args=(quote(instance.pk),))
333
341
 
334
342
  def get_delete_url(self, instance):
335
- if self.delete_url_name:
343
+ if self.delete_url_name and self.user_has_permission("delete"):
336
344
  return reverse(self.delete_url_name, args=(quote(instance.pk),))
337
345
 
338
346
  def get_add_url(self):
339
- if self.permission_policy and not self.permission_policy.user_has_permission(
340
- self.request.user, "add"
341
- ):
342
- return None
343
- if self.add_url_name:
347
+ if self.add_url_name and self.user_has_permission("add"):
344
348
  return self._set_locale_query_param(reverse(self.add_url_name))
345
349
 
346
350
  @cached_property
@@ -374,16 +378,11 @@ class IndexView(
374
378
 
375
379
  def get_list_more_buttons(self, instance):
376
380
  buttons = []
377
- edit_url = self.get_edit_url(instance)
378
- can_edit = (
379
- not self.permission_policy
380
- or self.permission_policy.user_has_permission(self.request.user, "change")
381
- )
382
- if edit_url and can_edit:
381
+ if edit_url := self.get_edit_url(instance):
383
382
  buttons.append(
384
383
  ListingButton(
385
384
  _("Edit"),
386
- url=self.get_edit_url(instance),
385
+ url=edit_url,
387
386
  icon_name="edit",
388
387
  attrs={
389
388
  "aria-label": _("Edit '%(title)s'") % {"title": str(instance)}
@@ -391,9 +390,7 @@ class IndexView(
391
390
  priority=10,
392
391
  )
393
392
  )
394
- copy_url = self.get_copy_url(instance)
395
- can_copy = self.permission_policy.user_has_permission(self.request.user, "add")
396
- if copy_url and can_copy:
393
+ if copy_url := self.get_copy_url(instance):
397
394
  buttons.append(
398
395
  ListingButton(
399
396
  _("Copy"),
@@ -405,8 +402,7 @@ class IndexView(
405
402
  priority=20,
406
403
  )
407
404
  )
408
- inspect_url = self.get_inspect_url(instance)
409
- if inspect_url:
405
+ if inspect_url := self.get_inspect_url(instance):
410
406
  buttons.append(
411
407
  ListingButton(
412
408
  _("Inspect"),
@@ -419,12 +415,7 @@ class IndexView(
419
415
  priority=20,
420
416
  )
421
417
  )
422
- delete_url = self.get_delete_url(instance)
423
- can_delete = (
424
- not self.permission_policy
425
- or self.permission_policy.user_has_permission(self.request.user, "delete")
426
- )
427
- if delete_url and can_delete:
418
+ if delete_url := self.get_delete_url(instance):
428
419
  buttons.append(
429
420
  ListingButton(
430
421
  _("Delete"),
@@ -439,17 +430,20 @@ class IndexView(
439
430
  return buttons
440
431
 
441
432
  def get_list_buttons(self, instance):
442
- buttons = self.get_list_more_buttons(instance)
443
- return [
444
- ButtonWithDropdown(
445
- buttons=buttons,
446
- icon_name="dots-horizontal",
447
- attrs={
448
- "aria-label": _("More options for '%(title)s'")
449
- % {"title": str(instance)},
450
- },
433
+ more_buttons = self.get_list_more_buttons(instance)
434
+ buttons = []
435
+ if more_buttons:
436
+ buttons.append(
437
+ ButtonWithDropdown(
438
+ buttons=more_buttons,
439
+ icon_name="dots-horizontal",
440
+ attrs={
441
+ "aria-label": _("More options for '%(title)s'")
442
+ % {"title": str(instance)},
443
+ },
444
+ )
451
445
  )
452
- ]
446
+ return buttons
453
447
 
454
448
  @cached_property
455
449
  def add_item_label(self):
@@ -462,10 +456,7 @@ class IndexView(
462
456
  def get_context_data(self, *args, **kwargs):
463
457
  context = super().get_context_data(*args, **kwargs)
464
458
 
465
- context["can_add"] = (
466
- self.permission_policy is None
467
- or self.permission_policy.user_has_permission(self.request.user, "add")
468
- )
459
+ context["can_add"] = self.user_has_permission("add")
469
460
  if context["can_add"]:
470
461
  context["add_url"] = context["header_action_url"] = self.add_url
471
462
  context["header_action_label"] = self.add_item_label
@@ -741,9 +732,9 @@ class EditView(
741
732
  return self.actions
742
733
 
743
734
  def get_object(self, queryset=None):
744
- if "pk" not in self.kwargs:
745
- self.kwargs["pk"] = self.args[0]
746
- self.kwargs["pk"] = unquote(str(self.kwargs["pk"]))
735
+ if self.pk_url_kwarg not in self.kwargs:
736
+ self.kwargs[self.pk_url_kwarg] = self.args[0]
737
+ self.kwargs[self.pk_url_kwarg] = unquote(str(self.kwargs[self.pk_url_kwarg]))
747
738
  return super().get_object(queryset)
748
739
 
749
740
  def get_page_subtitle(self):
@@ -874,11 +865,7 @@ class EditView(
874
865
  )
875
866
 
876
867
  def get_success_buttons(self):
877
- return [
878
- messages.button(
879
- reverse(self.edit_url_name, args=(quote(self.object.pk),)), _("Edit")
880
- )
881
- ]
868
+ return [messages.button(self.get_edit_url(), _("Edit"))]
882
869
 
883
870
  def get_error_message(self):
884
871
  if self.error_message is None:
@@ -917,9 +904,8 @@ class EditView(
917
904
  context["side_panels"] = side_panels
918
905
  context["media"] += side_panels.media
919
906
  context["submit_button_label"] = self.submit_button_label
920
- context["can_delete"] = (
921
- self.permission_policy is None
922
- or self.permission_policy.user_has_permission(self.request.user, "delete")
907
+ context["can_delete"] = self.user_has_permission_for_instance(
908
+ "delete", self.object
923
909
  )
924
910
  if context["can_delete"]:
925
911
  context["delete_url"] = self.get_delete_url()
@@ -1046,7 +1032,7 @@ class DeleteView(
1046
1032
 
1047
1033
 
1048
1034
  class InspectView(PermissionCheckedMixin, WagtailAdminTemplateMixin, TemplateView):
1049
- any_permission_required = ["add", "change", "delete"]
1035
+ any_permission_required = ["add", "change", "delete", "view"]
1050
1036
  template_name = "wagtailadmin/generic/inspect.html"
1051
1037
  page_title = gettext_lazy("Inspecting")
1052
1038
  model = None
@@ -1149,24 +1135,12 @@ class InspectView(PermissionCheckedMixin, WagtailAdminTemplateMixin, TemplateVie
1149
1135
  return [self.get_context_for_field(field_name) for field_name in self.fields]
1150
1136
 
1151
1137
  def get_edit_url(self):
1152
- if not self.edit_url_name or (
1153
- self.permission_policy
1154
- and not self.permission_policy.user_has_permission(
1155
- self.request.user, "change"
1156
- )
1157
- ):
1158
- return None
1159
- return reverse(self.edit_url_name, args=(quote(self.object.pk),))
1138
+ if self.edit_url_name and self.user_has_permission("change"):
1139
+ return reverse(self.edit_url_name, args=(quote(self.object.pk),))
1160
1140
 
1161
1141
  def get_delete_url(self):
1162
- if not self.delete_url_name or (
1163
- self.permission_policy
1164
- and not self.permission_policy.user_has_permission(
1165
- self.request.user, "delete"
1166
- )
1167
- ):
1168
- return None
1169
- return reverse(self.delete_url_name, args=(quote(self.object.pk),))
1142
+ if self.delete_url_name and self.user_has_permission("delete"):
1143
+ return reverse(self.delete_url_name, args=(quote(self.object.pk),))
1170
1144
 
1171
1145
  def get_context_data(self, **kwargs):
1172
1146
  context = super().get_context_data(**kwargs)
@@ -19,21 +19,31 @@ class PermissionCheckedMixin:
19
19
  any_permission_required = None
20
20
 
21
21
  def dispatch(self, request, *args, **kwargs):
22
- if self.permission_policy is not None:
23
- if self.permission_required is not None:
24
- if not self.user_has_permission(self.permission_required):
25
- raise PermissionDenied
22
+ if self.permission_required is not None:
23
+ if not self.user_has_permission(self.permission_required):
24
+ raise PermissionDenied
26
25
 
27
- if self.any_permission_required is not None:
28
- if not self.user_has_any_permission(self.any_permission_required):
29
- raise PermissionDenied
26
+ if self.any_permission_required is not None:
27
+ if not self.user_has_any_permission(self.any_permission_required):
28
+ raise PermissionDenied
30
29
 
31
30
  return super().dispatch(request, *args, **kwargs)
32
31
 
33
32
  def user_has_permission(self, permission):
34
- return self.permission_policy.user_has_permission(self.request.user, permission)
33
+ return not self.permission_policy or (
34
+ self.permission_policy.user_has_permission(self.request.user, permission)
35
+ )
36
+
37
+ def user_has_permission_for_instance(self, permission, instance):
38
+ return not self.permission_policy or (
39
+ self.permission_policy.user_has_permission_for_instance(
40
+ self.request.user, permission, instance
41
+ )
42
+ )
35
43
 
36
44
  def user_has_any_permission(self, permissions):
37
- return self.permission_policy.user_has_any_permission(
38
- self.request.user, permissions
45
+ return not self.permission_policy or (
46
+ self.permission_policy.user_has_any_permission(
47
+ self.request.user, permissions
48
+ )
39
49
  )
@@ -1,5 +1,3 @@
1
- import itertools
2
- import re
3
1
  from typing import Any, Mapping, Union
4
2
 
5
3
  from django.conf import settings
@@ -9,11 +7,11 @@ from django.db.models import Exists, IntegerField, Max, OuterRef, Q
9
7
  from django.db.models.functions import Cast
10
8
  from django.forms import Media
11
9
  from django.http import Http404, HttpResponse
12
- from django.template.loader import render_to_string
13
10
  from django.utils.translation import gettext_lazy
14
11
  from django.views.generic.base import TemplateView
15
12
 
16
13
  from wagtail import hooks
14
+ from wagtail.admin.icons import get_icons
17
15
  from wagtail.admin.navigation import get_site_for_user
18
16
  from wagtail.admin.site_summary import SiteSummaryPanel
19
17
  from wagtail.admin.ui.components import Component
@@ -350,32 +348,5 @@ def default(request):
350
348
  raise Http404
351
349
 
352
350
 
353
- icon_comment_pattern = re.compile(r"<!--.*?-->")
354
- _icons_html = None
355
-
356
-
357
- def icons():
358
- global _icons_html
359
- if _icons_html is None:
360
- icon_hooks = hooks.get_hooks("register_icons")
361
- all_icons = sorted(
362
- itertools.chain.from_iterable(hook([]) for hook in icon_hooks)
363
- )
364
- combined_icon_markup = ""
365
- for icon in all_icons:
366
- symbol = (
367
- render_to_string(icon)
368
- .replace('xmlns="http://www.w3.org/2000/svg"', "")
369
- .replace("svg", "symbol")
370
- )
371
- symbol = icon_comment_pattern.sub("", symbol)
372
- combined_icon_markup += symbol
373
-
374
- _icons_html = render_to_string(
375
- "wagtailadmin/shared/icons.html", {"icons": combined_icon_markup}
376
- )
377
- return _icons_html
378
-
379
-
380
351
  def sprite(request):
381
- return HttpResponse(icons())
352
+ return HttpResponse(get_icons(), content_type="image/svg+xml; charset=utf-8")