wagtail 6.0.1__py3-none-any.whl → 6.1rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (512) hide show
  1. wagtail/__init__.py +1 -1
  2. wagtail/admin/checks.py +51 -0
  3. wagtail/admin/compare.py +1 -1
  4. wagtail/admin/filters.py +70 -1
  5. wagtail/admin/forms/account.py +1 -1
  6. wagtail/admin/forms/collections.py +15 -0
  7. wagtail/admin/forms/pages.py +49 -0
  8. wagtail/admin/locale/ca/LC_MESSAGES/django.mo +0 -0
  9. wagtail/admin/locale/ca/LC_MESSAGES/django.po +122 -0
  10. wagtail/admin/locale/de/LC_MESSAGES/django.mo +0 -0
  11. wagtail/admin/locale/de/LC_MESSAGES/django.po +5 -5
  12. wagtail/admin/locale/en/LC_MESSAGES/django.po +474 -385
  13. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +3 -3
  14. wagtail/admin/locale/es/LC_MESSAGES/django.mo +0 -0
  15. wagtail/admin/locale/es/LC_MESSAGES/django.po +6 -6
  16. wagtail/admin/locale/fr/LC_MESSAGES/django.mo +0 -0
  17. wagtail/admin/locale/fr/LC_MESSAGES/django.po +70 -3
  18. wagtail/admin/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  19. wagtail/admin/locale/he_IL/LC_MESSAGES/django.po +2 -6
  20. wagtail/admin/locale/he_IL/LC_MESSAGES/djangojs.mo +0 -0
  21. wagtail/admin/locale/he_IL/LC_MESSAGES/djangojs.po +2 -2
  22. wagtail/admin/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  23. wagtail/admin/locale/hr_HR/LC_MESSAGES/django.po +4 -0
  24. wagtail/admin/locale/hu/LC_MESSAGES/django.mo +0 -0
  25. wagtail/admin/locale/hu/LC_MESSAGES/django.po +142 -2
  26. wagtail/admin/locale/it/LC_MESSAGES/django.mo +0 -0
  27. wagtail/admin/locale/it/LC_MESSAGES/django.po +80 -8
  28. wagtail/admin/locale/it/LC_MESSAGES/djangojs.mo +0 -0
  29. wagtail/admin/locale/it/LC_MESSAGES/djangojs.po +14 -2
  30. wagtail/admin/locale/lv/LC_MESSAGES/django.mo +0 -0
  31. wagtail/admin/locale/lv/LC_MESSAGES/django.po +154 -1
  32. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  33. wagtail/admin/locale/pt_PT/LC_MESSAGES/django.po +73 -2
  34. wagtail/admin/locale/ro/LC_MESSAGES/django.mo +0 -0
  35. wagtail/admin/locale/ro/LC_MESSAGES/django.po +3 -3
  36. wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
  37. wagtail/admin/locale/sl/LC_MESSAGES/django.po +145 -2
  38. wagtail/admin/locale/sv/LC_MESSAGES/django.mo +0 -0
  39. wagtail/admin/locale/sv/LC_MESSAGES/django.po +77 -3
  40. wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
  41. wagtail/admin/locale/zh_Hant/LC_MESSAGES/django.po +17 -1
  42. wagtail/admin/panels/comment_panel.py +1 -1
  43. wagtail/admin/panels/field_panel.py +1 -1
  44. wagtail/admin/rich_text/converters/editor_html.py +3 -1
  45. wagtail/admin/rich_text/editors/draftail/__init__.py +28 -2
  46. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  47. wagtail/admin/static/wagtailadmin/css/panels/draftail.css +1 -1
  48. wagtail/admin/static/wagtailadmin/images/favicon.ico +0 -0
  49. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  50. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  51. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  52. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  53. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  54. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  55. wagtail/admin/static/wagtailadmin/js/core.js.LICENSE.txt +1 -1
  56. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  57. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  58. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +1 -1
  59. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  60. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  61. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  62. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  63. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  64. wagtail/admin/static/wagtailadmin/js/preview-panel.js +1 -1
  65. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  66. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  67. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  68. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  69. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  70. wagtail/admin/static/wagtailadmin/js/telepath/telepath.js +1 -1
  71. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  72. wagtail/admin/static/wagtailadmin/js/userbar.js +1 -1
  73. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  74. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +4 -4
  75. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  76. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  77. wagtail/admin/staticfiles.py +1 -0
  78. wagtail/admin/templates/wagtailadmin/admin_base.html +1 -0
  79. wagtail/admin/templates/wagtailadmin/base.html +1 -0
  80. wagtail/admin/templates/wagtailadmin/collection_privacy/set_privacy.html +3 -1
  81. wagtail/admin/templates/wagtailadmin/collections/edit.html +0 -1
  82. wagtail/admin/templates/wagtailadmin/collections/index_results.html +10 -0
  83. wagtail/admin/templates/wagtailadmin/generic/base.html +1 -9
  84. wagtail/admin/templates/wagtailadmin/generic/form.html +4 -2
  85. wagtail/admin/templates/wagtailadmin/generic/history/action_cell.html +27 -0
  86. wagtail/admin/templates/wagtailadmin/generic/index_results.html +8 -0
  87. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +3 -4
  88. wagtail/admin/templates/wagtailadmin/icons/keyboard.svg +1 -0
  89. wagtail/admin/templates/wagtailadmin/page_privacy/set_privacy.html +3 -1
  90. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -15
  91. wagtail/admin/templates/wagtailadmin/pages/action_menu/save_draft.html +3 -1
  92. wagtail/admin/templates/wagtailadmin/pages/choose_parent.html +17 -0
  93. wagtail/admin/templates/wagtailadmin/pages/explorable_index.html +8 -0
  94. wagtail/admin/templates/wagtailadmin/pages/history.html +1 -61
  95. wagtail/admin/templates/wagtailadmin/pages/index.html +1 -5
  96. wagtail/admin/templates/wagtailadmin/pages/listing/_locked_indicator.html +2 -2
  97. wagtail/admin/templates/wagtailadmin/pages/listing/_page_title_column_header.html +25 -27
  98. wagtail/admin/templates/wagtailadmin/pages/page_listing_header.html +2 -1
  99. wagtail/admin/templates/wagtailadmin/panels/multi_field_panel_child.html +1 -1
  100. wagtail/admin/templates/wagtailadmin/panels/publishing/schedule_publishing_panel.html +3 -1
  101. wagtail/admin/templates/wagtailadmin/panels/tabbed_interface.html +1 -1
  102. wagtail/admin/templates/wagtailadmin/shared/active_filters.html +2 -1
  103. wagtail/admin/templates/wagtailadmin/shared/breadcrumbs.html +8 -0
  104. wagtail/admin/templates/wagtailadmin/shared/forms/single_checkbox.html +1 -1
  105. wagtail/admin/templates/wagtailadmin/shared/headers/page_edit_header.html +1 -1
  106. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +21 -9
  107. wagtail/admin/templates/wagtailadmin/shared/human_readable_date.html +1 -1
  108. wagtail/admin/templates/wagtailadmin/shared/keyboard_shortcuts_dialog.html +29 -0
  109. wagtail/admin/templates/wagtailadmin/shared/side_panel_toggle.html +2 -1
  110. wagtail/admin/templates/wagtailadmin/skeleton.html +2 -1
  111. wagtail/admin/templates/wagtailadmin/tables/related_objects_cell.html +9 -0
  112. wagtail/admin/templates/wagtailadmin/tables/title_cell.html +9 -7
  113. wagtail/admin/templates/wagtailadmin/widgets/draftail_rich_text_area.html +1 -1
  114. wagtail/admin/templates/wagtailadmin/workflows/create.html +6 -23
  115. wagtail/admin/templates/wagtailadmin/workflows/create_task.html +6 -15
  116. wagtail/admin/templates/wagtailadmin/workflows/edit.html +6 -23
  117. wagtail/admin/templates/wagtailadmin/workflows/edit_task.html +6 -13
  118. wagtail/admin/templates/wagtailadmin/workflows/includes/task_usage_cell.html +4 -4
  119. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_tasks_cell.html +18 -0
  120. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_title_cell.html +7 -0
  121. wagtail/admin/templates/wagtailadmin/workflows/includes/workflow_used_by_cell.html +25 -0
  122. wagtail/admin/templates/wagtailadmin/workflows/index.html +0 -99
  123. wagtail/admin/templates/wagtailadmin/workflows/index_results.html +10 -0
  124. wagtail/admin/templates/wagtailadmin/workflows/task_index.html +0 -30
  125. wagtail/admin/templates/wagtailadmin/workflows/task_index_results.html +10 -0
  126. wagtail/admin/templates/wagtailadmin/workflows/usage.html +1 -1
  127. wagtail/admin/templatetags/wagtailadmin_tags.py +116 -39
  128. wagtail/admin/tests/api/test_pages.py +26 -10
  129. wagtail/admin/tests/pages/test_create_page.py +10 -4
  130. wagtail/admin/tests/pages/test_custom_listing.py +37 -0
  131. wagtail/admin/tests/pages/test_edit_page.py +6 -6
  132. wagtail/admin/tests/pages/test_explorer_view.py +19 -18
  133. wagtail/admin/tests/pages/test_move_page.py +1 -1
  134. wagtail/admin/tests/pages/test_page_usage.py +50 -2
  135. wagtail/admin/tests/pages/test_parent_page_chooser_view.py +119 -0
  136. wagtail/admin/tests/pages/test_preview.py +18 -4
  137. wagtail/admin/tests/test_account_management.py +20 -1
  138. wagtail/admin/tests/test_audit_log.py +172 -5
  139. wagtail/admin/tests/test_checks.py +92 -0
  140. wagtail/admin/tests/test_collections_views.py +19 -5
  141. wagtail/admin/tests/test_compare.py +6 -6
  142. wagtail/admin/tests/test_dashboard.py +404 -0
  143. wagtail/admin/tests/test_dbwhitelister.py +4 -5
  144. wagtail/admin/tests/test_edit_handlers.py +2 -2
  145. wagtail/admin/tests/test_keyboard_shortcuts.py +84 -0
  146. wagtail/admin/tests/test_page_chooser.py +31 -18
  147. wagtail/admin/tests/test_privacy.py +36 -2
  148. wagtail/admin/tests/test_rich_text.py +168 -23
  149. wagtail/admin/tests/test_templatetags.py +411 -43
  150. wagtail/admin/tests/test_views.py +4 -2
  151. wagtail/admin/tests/test_workflows.py +531 -9
  152. wagtail/admin/tests/tests.py +3 -1
  153. wagtail/admin/tests/ui/test_tables.py +48 -1
  154. wagtail/admin/tests/viewsets/test_model_viewset.py +130 -23
  155. wagtail/admin/ui/side_panels.py +3 -1
  156. wagtail/admin/ui/tables/__init__.py +13 -1
  157. wagtail/admin/ui/tables/pages.py +17 -6
  158. wagtail/admin/urls/__init__.py +8 -3
  159. wagtail/admin/urls/pages.py +5 -0
  160. wagtail/admin/urls/workflows.py +10 -0
  161. wagtail/admin/views/chooser.py +20 -24
  162. wagtail/admin/views/collections.py +17 -1
  163. wagtail/admin/views/generic/base.py +34 -4
  164. wagtail/admin/views/generic/history.py +220 -51
  165. wagtail/admin/views/generic/mixins.py +7 -4
  166. wagtail/admin/views/generic/models.py +54 -47
  167. wagtail/admin/views/generic/multiple_upload.py +17 -8
  168. wagtail/admin/views/generic/usage.py +17 -11
  169. wagtail/admin/views/home.py +15 -12
  170. wagtail/admin/views/mixins.py +30 -0
  171. wagtail/admin/views/pages/choose_parent.py +73 -0
  172. wagtail/admin/views/pages/history.py +54 -66
  173. wagtail/admin/views/pages/listing.py +187 -106
  174. wagtail/admin/views/pages/usage.py +6 -1
  175. wagtail/admin/views/pages/utils.py +70 -1
  176. wagtail/admin/views/workflows.py +150 -21
  177. wagtail/admin/viewsets/model.py +2 -2
  178. wagtail/admin/viewsets/pages.py +77 -0
  179. wagtail/admin/wagtail_hooks.py +40 -2
  180. wagtail/admin/widgets/button.py +10 -10
  181. wagtail/api/v2/filters.py +1 -1
  182. wagtail/api/v2/tests/test_pages.py +1 -1
  183. wagtail/blocks/base.py +18 -9
  184. wagtail/blocks/field_block.py +9 -7
  185. wagtail/blocks/list_block.py +16 -6
  186. wagtail/blocks/static_block.py +3 -0
  187. wagtail/blocks/stream_block.py +58 -23
  188. wagtail/blocks/struct_block.py +15 -9
  189. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +39 -47
  190. wagtail/contrib/forms/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  191. wagtail/contrib/forms/locale/he_IL/LC_MESSAGES/django.po +2 -2
  192. wagtail/contrib/forms/models.py +5 -5
  193. wagtail/contrib/forms/templates/wagtailforms/list_submissions.html +44 -33
  194. wagtail/contrib/forms/templates/wagtailforms/submissions_index.html +2 -63
  195. wagtail/contrib/forms/tests/test_models.py +26 -0
  196. wagtail/contrib/forms/urls.py +6 -0
  197. wagtail/contrib/forms/views.py +52 -49
  198. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.mo +0 -0
  199. wagtail/contrib/redirects/locale/ca/LC_MESSAGES/django.po +3 -3
  200. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +34 -42
  201. wagtail/contrib/redirects/locale/fr/LC_MESSAGES/django.po +2 -2
  202. wagtail/contrib/redirects/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  203. wagtail/contrib/redirects/locale/he_IL/LC_MESSAGES/django.po +2 -2
  204. wagtail/contrib/redirects/signal_handlers.py +1 -1
  205. wagtail/contrib/redirects/templates/wagtailredirects/index.html +1 -36
  206. wagtail/contrib/redirects/templates/wagtailredirects/index_results.html +18 -0
  207. wagtail/contrib/redirects/templates/wagtailredirects/redirect_target_cell.html +8 -0
  208. wagtail/contrib/redirects/tests/test_import_command.py +1 -1
  209. wagtail/contrib/redirects/tests/test_redirects.py +79 -8
  210. wagtail/contrib/redirects/urls.py +2 -1
  211. wagtail/contrib/redirects/views.py +85 -55
  212. wagtail/contrib/search_promotions/admin_urls.py +2 -1
  213. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +41 -64
  214. wagtail/contrib/search_promotions/locale/fr/LC_MESSAGES/django.po +2 -2
  215. wagtail/contrib/search_promotions/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  216. wagtail/contrib/search_promotions/locale/he_IL/LC_MESSAGES/django.po +2 -2
  217. wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  218. wagtail/contrib/search_promotions/locale/hr_HR/LC_MESSAGES/django.po +41 -2
  219. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.mo +0 -0
  220. wagtail/contrib/search_promotions/locale/it/LC_MESSAGES/django.po +9 -3
  221. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index.html +1 -16
  222. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/index_results.html +11 -0
  223. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/list.html +0 -51
  224. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/results.html +3 -16
  225. wagtail/contrib/search_promotions/templates/wagtailsearchpromotions/search_promotion_column.html +15 -0
  226. wagtail/contrib/search_promotions/tests.py +122 -9
  227. wagtail/contrib/search_promotions/views.py +66 -65
  228. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +3 -3
  229. wagtail/contrib/settings/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  230. wagtail/contrib/settings/locale/he_IL/LC_MESSAGES/django.po +2 -2
  231. wagtail/contrib/settings/locale/tr/LC_MESSAGES/django.mo +0 -0
  232. wagtail/contrib/settings/locale/tr/LC_MESSAGES/django.po +6 -2
  233. wagtail/contrib/settings/registry.py +10 -5
  234. wagtail/contrib/settings/tests/generic/test_admin.py +9 -0
  235. wagtail/contrib/settings/tests/site_specific/test_admin.py +10 -1
  236. wagtail/contrib/settings/tests/site_specific/test_model.py +3 -3
  237. wagtail/contrib/settings/tests/site_specific/test_templates.py +1 -1
  238. wagtail/contrib/settings/views.py +3 -1
  239. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +1 -1
  240. wagtail/contrib/simple_translation/tests/test_wagtail_hooks.py +2 -2
  241. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  242. wagtail/contrib/styleguide/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  243. wagtail/contrib/styleguide/locale/he_IL/LC_MESSAGES/django.po +2 -2
  244. wagtail/contrib/table_block/blocks.py +2 -2
  245. wagtail/contrib/table_block/locale/ca/LC_MESSAGES/django.mo +0 -0
  246. wagtail/contrib/table_block/locale/ca/LC_MESSAGES/django.po +27 -2
  247. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  248. wagtail/contrib/table_block/locale/hu/LC_MESSAGES/django.mo +0 -0
  249. wagtail/contrib/table_block/locale/hu/LC_MESSAGES/django.po +27 -2
  250. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.mo +0 -0
  251. wagtail/contrib/table_block/locale/it/LC_MESSAGES/django.po +27 -2
  252. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  253. wagtail/contrib/table_block/tests.py +6 -0
  254. wagtail/contrib/typed_table_block/locale/ca/LC_MESSAGES/django.mo +0 -0
  255. wagtail/contrib/typed_table_block/locale/ca/LC_MESSAGES/django.po +12 -2
  256. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +1 -1
  257. wagtail/contrib/typed_table_block/locale/hu/LC_MESSAGES/django.mo +0 -0
  258. wagtail/contrib/typed_table_block/locale/hu/LC_MESSAGES/django.po +12 -2
  259. wagtail/contrib/typed_table_block/locale/it/LC_MESSAGES/django.mo +0 -0
  260. wagtail/contrib/typed_table_block/locale/it/LC_MESSAGES/django.po +12 -2
  261. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  262. wagtail/coreutils.py +3 -2
  263. wagtail/documents/admin_urls.py +2 -2
  264. wagtail/documents/locale/en/LC_MESSAGES/django.po +22 -22
  265. wagtail/documents/locale/fr/LC_MESSAGES/django.po +2 -2
  266. wagtail/documents/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  267. wagtail/documents/locale/he_IL/LC_MESSAGES/django.po +2 -2
  268. wagtail/documents/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  269. wagtail/documents/locale/hr_HR/LC_MESSAGES/django.po +19 -2
  270. wagtail/documents/locale/hu/LC_MESSAGES/django.mo +0 -0
  271. wagtail/documents/locale/hu/LC_MESSAGES/django.po +16 -2
  272. wagtail/documents/locale/it/LC_MESSAGES/django.mo +0 -0
  273. wagtail/documents/locale/it/LC_MESSAGES/django.po +19 -2
  274. wagtail/documents/migrations/0013_delete_uploadeddocument.py +16 -0
  275. wagtail/documents/models.py +1 -20
  276. wagtail/documents/rich_text/__init__.py +11 -7
  277. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  278. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  279. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  280. wagtail/documents/templates/wagtaildocs/documents/index.html +0 -16
  281. wagtail/documents/tests/test_admin_views.py +155 -23
  282. wagtail/documents/tests/test_collection_privacy.py +55 -1
  283. wagtail/documents/tests/test_rich_text.py +14 -0
  284. wagtail/documents/views/documents.py +25 -22
  285. wagtail/documents/views/multiple.py +6 -7
  286. wagtail/documents/views/serve.py +16 -1
  287. wagtail/documents/wagtail_hooks.py +20 -15
  288. wagtail/embeds/blocks.py +5 -0
  289. wagtail/embeds/locale/en/LC_MESSAGES/django.po +2 -2
  290. wagtail/embeds/locale/fr/LC_MESSAGES/django.po +2 -2
  291. wagtail/embeds/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  292. wagtail/embeds/locale/he_IL/LC_MESSAGES/django.po +2 -2
  293. wagtail/embeds/rich_text/__init__.py +1 -1
  294. wagtail/embeds/tests/test_rich_text.py +14 -0
  295. wagtail/embeds/wagtail_hooks.py +4 -14
  296. wagtail/fields.py +3 -48
  297. wagtail/images/admin_urls.py +2 -2
  298. wagtail/images/check_files/wagtail.jpg +0 -0
  299. wagtail/images/check_files/wagtail.png +0 -0
  300. wagtail/images/fields.py +2 -0
  301. wagtail/images/image_operations.py +1 -1
  302. wagtail/images/locale/ca/LC_MESSAGES/django.mo +0 -0
  303. wagtail/images/locale/ca/LC_MESSAGES/django.po +12 -0
  304. wagtail/images/locale/en/LC_MESSAGES/django.po +33 -45
  305. wagtail/images/locale/fr/LC_MESSAGES/django.po +2 -2
  306. wagtail/images/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  307. wagtail/images/locale/he_IL/LC_MESSAGES/django.po +2 -2
  308. wagtail/images/locale/hu/LC_MESSAGES/django.mo +0 -0
  309. wagtail/images/locale/hu/LC_MESSAGES/django.po +28 -2
  310. wagtail/images/locale/it/LC_MESSAGES/django.mo +0 -0
  311. wagtail/images/locale/it/LC_MESSAGES/django.po +14 -2
  312. wagtail/images/locale/pt_PT/LC_MESSAGES/django.mo +0 -0
  313. wagtail/images/locale/pt_PT/LC_MESSAGES/django.po +4 -0
  314. wagtail/images/migrations/0026_delete_uploadedimage.py +16 -0
  315. wagtail/images/models.py +49 -43
  316. wagtail/images/rich_text/__init__.py +18 -8
  317. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  318. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  319. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  320. wagtail/images/templates/wagtailimages/images/image_listing_header.html +6 -0
  321. wagtail/images/templates/wagtailimages/images/index.html +11 -51
  322. wagtail/images/tests/test_admin_views.py +119 -62
  323. wagtail/images/tests/test_image_operations.py +10 -0
  324. wagtail/images/tests/test_models.py +35 -33
  325. wagtail/images/tests/test_rich_text.py +14 -0
  326. wagtail/images/tests/utils.py +1 -1
  327. wagtail/images/views/images.py +35 -64
  328. wagtail/images/views/multiple.py +6 -7
  329. wagtail/images/wagtail_hooks.py +4 -14
  330. wagtail/locale/en/LC_MESSAGES/django.po +150 -136
  331. wagtail/locale/es/LC_MESSAGES/django.mo +0 -0
  332. wagtail/locale/es/LC_MESSAGES/django.po +3 -2
  333. wagtail/locale/fr/LC_MESSAGES/django.po +2 -2
  334. wagtail/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  335. wagtail/locale/he_IL/LC_MESSAGES/django.po +2 -2
  336. wagtail/locale/it/LC_MESSAGES/django.mo +0 -0
  337. wagtail/locale/it/LC_MESSAGES/django.po +5 -5
  338. wagtail/locale/sl/LC_MESSAGES/django.mo +0 -0
  339. wagtail/locale/sl/LC_MESSAGES/django.po +27 -2
  340. wagtail/locales/locale/ar/LC_MESSAGES/django.po +1 -1
  341. wagtail/locales/locale/be/LC_MESSAGES/django.po +1 -1
  342. wagtail/locales/locale/bg/LC_MESSAGES/django.po +1 -1
  343. wagtail/locales/locale/ca/LC_MESSAGES/django.po +1 -1
  344. wagtail/locales/locale/cs/LC_MESSAGES/django.po +1 -1
  345. wagtail/locales/locale/cy/LC_MESSAGES/django.po +1 -1
  346. wagtail/locales/locale/da/LC_MESSAGES/django.po +1 -1
  347. wagtail/locales/locale/de/LC_MESSAGES/django.po +1 -1
  348. wagtail/locales/locale/el/LC_MESSAGES/django.po +1 -1
  349. wagtail/locales/locale/en/LC_MESSAGES/django.po +1 -1
  350. wagtail/locales/locale/es/LC_MESSAGES/django.po +1 -1
  351. wagtail/locales/locale/et/LC_MESSAGES/django.po +2 -2
  352. wagtail/locales/locale/fa/LC_MESSAGES/django.po +1 -1
  353. wagtail/locales/locale/fi/LC_MESSAGES/django.po +1 -1
  354. wagtail/locales/locale/fr/LC_MESSAGES/django.po +1 -1
  355. wagtail/locales/locale/gl/LC_MESSAGES/django.po +1 -1
  356. wagtail/locales/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  357. wagtail/locales/locale/he_IL/LC_MESSAGES/django.po +3 -3
  358. wagtail/locales/locale/hr_HR/LC_MESSAGES/django.po +1 -1
  359. wagtail/locales/locale/hu/LC_MESSAGES/django.po +1 -1
  360. wagtail/locales/locale/id_ID/LC_MESSAGES/django.po +1 -1
  361. wagtail/locales/locale/is_IS/LC_MESSAGES/django.po +1 -1
  362. wagtail/locales/locale/it/LC_MESSAGES/django.po +1 -1
  363. wagtail/locales/locale/ja/LC_MESSAGES/django.po +1 -1
  364. wagtail/locales/locale/ko/LC_MESSAGES/django.po +1 -1
  365. wagtail/locales/locale/lt/LC_MESSAGES/django.po +1 -1
  366. wagtail/locales/locale/lv/LC_MESSAGES/django.po +1 -1
  367. wagtail/locales/locale/mi/LC_MESSAGES/django.po +1 -1
  368. wagtail/locales/locale/mn/LC_MESSAGES/django.po +1 -1
  369. wagtail/locales/locale/my/LC_MESSAGES/django.po +1 -1
  370. wagtail/locales/locale/nb/LC_MESSAGES/django.po +1 -1
  371. wagtail/locales/locale/nl/LC_MESSAGES/django.po +1 -1
  372. wagtail/locales/locale/pl/LC_MESSAGES/django.po +1 -1
  373. wagtail/locales/locale/pt_BR/LC_MESSAGES/django.po +1 -1
  374. wagtail/locales/locale/pt_PT/LC_MESSAGES/django.po +1 -1
  375. wagtail/locales/locale/ro/LC_MESSAGES/django.po +1 -1
  376. wagtail/locales/locale/ru/LC_MESSAGES/django.po +1 -1
  377. wagtail/locales/locale/sk_SK/LC_MESSAGES/django.po +1 -1
  378. wagtail/locales/locale/sl/LC_MESSAGES/django.po +1 -1
  379. wagtail/locales/locale/sv/LC_MESSAGES/django.po +1 -1
  380. wagtail/locales/locale/tet/LC_MESSAGES/django.po +1 -1
  381. wagtail/locales/locale/th/LC_MESSAGES/django.po +1 -1
  382. wagtail/locales/locale/tr/LC_MESSAGES/django.po +1 -1
  383. wagtail/locales/locale/tr_TR/LC_MESSAGES/django.po +1 -1
  384. wagtail/locales/locale/uk/LC_MESSAGES/django.po +1 -1
  385. wagtail/locales/locale/vi/LC_MESSAGES/django.po +1 -1
  386. wagtail/locales/locale/zh/LC_MESSAGES/django.po +1 -1
  387. wagtail/locales/locale/zh_Hans/LC_MESSAGES/django.po +1 -1
  388. wagtail/locales/locale/zh_Hant/LC_MESSAGES/django.po +1 -1
  389. wagtail/locales/tests.py +18 -3
  390. wagtail/locales/views.py +0 -1
  391. wagtail/management/commands/rebuild_references_index.py +3 -1
  392. wagtail/migrations/0092_alter_collectionviewrestriction_password_and_more.py +33 -0
  393. wagtail/migrations/0093_uploadedfile.py +53 -0
  394. wagtail/models/__init__.py +147 -32
  395. wagtail/models/i18n.py +1 -1
  396. wagtail/models/{collections.py → media.py} +33 -2
  397. wagtail/models/reference_index.py +1 -1
  398. wagtail/models/view_restrictions.py +10 -3
  399. wagtail/project_template/project_name/settings/base.py +6 -0
  400. wagtail/project_template/requirements.txt +1 -1
  401. wagtail/rich_text/__init__.py +25 -8
  402. wagtail/rich_text/pages.py +19 -8
  403. wagtail/rich_text/rewriters.py +140 -68
  404. wagtail/search/backends/database/mysql/mysql.py +3 -3
  405. wagtail/search/backends/database/postgres/postgres.py +3 -3
  406. wagtail/search/backends/database/sqlite/sqlite.py +2 -2
  407. wagtail/search/backends/elasticsearch7.py +4 -0
  408. wagtail/search/locale/en/LC_MESSAGES/django.po +3 -3
  409. wagtail/search/tests/test_postgres_backend.py +50 -0
  410. wagtail/sites/locale/en/LC_MESSAGES/django.po +8 -8
  411. wagtail/sites/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  412. wagtail/sites/locale/he_IL/LC_MESSAGES/django.po +2 -2
  413. wagtail/sites/locale/ro/LC_MESSAGES/django.mo +0 -0
  414. wagtail/sites/locale/ro/LC_MESSAGES/django.po +3 -2
  415. wagtail/sites/tests.py +35 -9
  416. wagtail/sites/views.py +3 -1
  417. wagtail/snippets/locale/de/LC_MESSAGES/django.mo +0 -0
  418. wagtail/snippets/locale/de/LC_MESSAGES/django.po +7 -8
  419. wagtail/snippets/locale/en/LC_MESSAGES/django.po +16 -56
  420. wagtail/snippets/locale/fr/LC_MESSAGES/django.po +2 -2
  421. wagtail/snippets/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  422. wagtail/snippets/locale/he_IL/LC_MESSAGES/django.po +2 -2
  423. wagtail/snippets/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  424. wagtail/snippets/locale/hr_HR/LC_MESSAGES/django.po +6 -2
  425. wagtail/snippets/locale/lv/LC_MESSAGES/django.mo +0 -0
  426. wagtail/snippets/locale/lv/LC_MESSAGES/django.po +12 -0
  427. wagtail/snippets/locale/zh_Hant/LC_MESSAGES/django.mo +0 -0
  428. wagtail/snippets/locale/zh_Hant/LC_MESSAGES/django.po +4 -0
  429. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  430. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  431. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/publish.html +3 -1
  432. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/save.html +3 -1
  433. wagtail/snippets/templates/wagtailsnippets/snippets/create.html +2 -3
  434. wagtail/snippets/templates/wagtailsnippets/snippets/edit.html +2 -3
  435. wagtail/snippets/tests/test_preview.py +13 -2
  436. wagtail/snippets/tests/test_snippets.py +41 -16
  437. wagtail/snippets/tests/test_viewset.py +95 -18
  438. wagtail/snippets/tests/test_workflows.py +12 -0
  439. wagtail/snippets/views/snippets.py +1 -40
  440. wagtail/templatetags/wagtailcore_tags.py +1 -1
  441. wagtail/test/demosite/models.py +1 -1
  442. wagtail/test/middleware.py +14 -1
  443. wagtail/test/testapp/fixtures/test.json +20 -0
  444. wagtail/test/testapp/migrations/0001_squashed_0073_revisablechildmodel_secret_text.py +8 -8
  445. wagtail/test/testapp/migrations/0023_snippetchoosermodel_full_featured.py +1 -0
  446. wagtail/test/testapp/migrations/0034_custompermissionmodel.py +44 -0
  447. wagtail/test/testapp/migrations/0035_modelwithcustommanager.py +30 -0
  448. wagtail/test/testapp/migrations/0036_complexdefaultstreampage.py +28 -0
  449. wagtail/test/testapp/models.py +79 -2
  450. wagtail/test/testapp/templates/tests/custom_docs_password_required.html +10 -0
  451. wagtail/test/testapp/templates/tests/custom_page_password_required.html +10 -0
  452. wagtail/test/testapp/views.py +24 -2
  453. wagtail/test/testapp/wagtail_hooks.py +19 -0
  454. wagtail/test/utils/wagtail_tests.py +2 -2
  455. wagtail/tests/test_blocks.py +262 -1
  456. wagtail/tests/test_migrations.py +1 -1
  457. wagtail/tests/test_page_model.py +77 -0
  458. wagtail/tests/test_page_privacy.py +18 -1
  459. wagtail/tests/test_rich_text.py +95 -5
  460. wagtail/tests/test_streamfield.py +43 -0
  461. wagtail/tests/test_utils.py +8 -2
  462. wagtail/tests/test_views.py +52 -1
  463. wagtail/tests/test_whitelist.py +7 -7
  464. wagtail/users/forms.py +3 -1
  465. wagtail/users/locale/en/LC_MESSAGES/django.po +124 -96
  466. wagtail/users/locale/fr/LC_MESSAGES/django.po +2 -2
  467. wagtail/users/locale/he_IL/LC_MESSAGES/django.mo +0 -0
  468. wagtail/users/locale/he_IL/LC_MESSAGES/django.po +2 -2
  469. wagtail/users/locale/hr_HR/LC_MESSAGES/django.mo +0 -0
  470. wagtail/users/locale/hr_HR/LC_MESSAGES/django.po +13 -2
  471. wagtail/users/migrations/0013_userprofile_density.py +23 -0
  472. wagtail/users/models.py +14 -3
  473. wagtail/users/templates/wagtailusers/groups/create.html +1 -7
  474. wagtail/users/templates/wagtailusers/groups/edit.html +1 -13
  475. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +46 -2
  476. wagtail/users/templates/wagtailusers/groups/includes/group_form_js.html +0 -3
  477. wagtail/users/templates/wagtailusers/users/create.html +1 -14
  478. wagtail/users/templates/wagtailusers/users/edit.html +1 -14
  479. wagtail/users/templates/wagtailusers/users/index.html +2 -5
  480. wagtail/users/templates/wagtailusers/users/index_results.html +3 -13
  481. wagtail/users/templates/wagtailusers/users/user_cell.html +9 -0
  482. wagtail/users/templatetags/wagtailusers_tags.py +107 -20
  483. wagtail/users/tests/test_admin_views.py +669 -90
  484. wagtail/users/views/groups.py +58 -61
  485. wagtail/users/views/users.py +211 -92
  486. wagtail/users/wagtail_hooks.py +6 -38
  487. wagtail/users/widgets.py +3 -5
  488. wagtail/utils/text.py +1 -1
  489. wagtail/views.py +5 -9
  490. wagtail/whitelist.py +1 -1
  491. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/METADATA +5 -6
  492. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/RECORD +496 -477
  493. wagtail/admin/static/wagtailadmin/js/page-editor.js +0 -1
  494. wagtail/admin/static/wagtailadmin/js/vendor/mousetrap.min.js +0 -1
  495. wagtail/admin/static/wagtailadmin/js/vendor/urlify.js +0 -1
  496. wagtail/admin/static/wagtailadmin/js/vendor/xregexp.min.js +0 -1
  497. wagtail/admin/templates/wagtailadmin/collections/index.html +0 -34
  498. wagtail/admin/templates/wagtailadmin/pages/revisions/_actions.html +0 -22
  499. wagtail/admin/templates/wagtailadmin/shared/page_breadcrumbs.html +0 -55
  500. wagtail/admin/tests/pages/test_dashboard.py +0 -172
  501. wagtail/contrib/redirects/templates/wagtailredirects/results.html +0 -23
  502. wagtail/documents/templates/wagtaildocs/documents/list.html +0 -2
  503. wagtail/search/tests/test_postgres_stemming.py +0 -40
  504. wagtail/sites/templates/wagtailsites/create.html +0 -7
  505. wagtail/sites/templates/wagtailsites/edit.html +0 -7
  506. wagtail/snippets/templates/wagtailsnippets/snippets/revisions/_actions.html +0 -36
  507. wagtail/users/templates/wagtailusers/users/list.html +0 -62
  508. wagtail/users/urls/users.py +0 -12
  509. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/LICENSE +0 -0
  510. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/WHEEL +0 -0
  511. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/entry_points.txt +0 -0
  512. {wagtail-6.0.1.dist-info → wagtail-6.1rc1.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,20 @@
1
+ from warnings import warn
2
+
1
3
  from django.contrib.auth.models import Group
2
- from django.urls import re_path
4
+ from django.shortcuts import get_object_or_404, redirect
5
+ from django.urls import re_path, reverse
6
+ from django.utils.functional import cached_property
7
+ from django.utils.translation import gettext
3
8
  from django.utils.translation import gettext_lazy as _
4
9
 
5
10
  from wagtail import hooks
6
11
  from wagtail.admin.ui.tables import TitleColumn
12
+ from wagtail.admin.utils import set_query_params
7
13
  from wagtail.admin.views import generic
8
14
  from wagtail.admin.viewsets.model import ModelViewSet
15
+ from wagtail.admin.widgets.button import HeaderButton
9
16
  from wagtail.users.forms import GroupForm, GroupPagePermissionFormSet
10
- from wagtail.users.views.users import Index
17
+ from wagtail.utils.deprecation import RemovedInWagtail70Warning
11
18
 
12
19
  _permission_panel_classes = None
13
20
 
@@ -45,11 +52,30 @@ class PermissionPanelFormsMixin:
45
52
  for cls in get_permission_panel_classes()
46
53
  ]
47
54
 
55
+ def process_form(self):
56
+ form = self.get_form()
57
+ permission_panels = self.get_permission_panel_forms()
58
+ if form.is_valid() and all(panel.is_valid() for panel in permission_panels):
59
+ response = self.form_valid(form)
60
+
61
+ for panel in permission_panels:
62
+ panel.save()
63
+
64
+ return response
65
+ else:
66
+ return self.form_invalid(form)
67
+
48
68
  def get_context_data(self, **kwargs):
49
69
  if "permission_panels" not in kwargs:
50
70
  kwargs["permission_panels"] = self.get_permission_panel_forms()
51
71
 
52
- return super().get_context_data(**kwargs)
72
+ context = super().get_context_data(**kwargs)
73
+
74
+ # Add js/css media from the formsets to the existing media
75
+ for panel in context["permission_panels"]:
76
+ context["media"] += panel.media
77
+
78
+ return context
53
79
 
54
80
 
55
81
  class IndexView(generic.IndexView):
@@ -74,9 +100,6 @@ class CreateView(PermissionPanelFormsMixin, generic.CreateView):
74
100
  page_title = _("Add group")
75
101
  success_message = _("Group '%(object)s' created.")
76
102
 
77
- def get_page_subtitle(self):
78
- return ""
79
-
80
103
  def post(self, request, *args, **kwargs):
81
104
  """
82
105
  Handle POST requests: instantiate a form instance with the passed
@@ -84,29 +107,7 @@ class CreateView(PermissionPanelFormsMixin, generic.CreateView):
84
107
  """
85
108
  # Create an object now so that the permission panel forms have something to link them against
86
109
  self.object = Group()
87
-
88
- form = self.get_form()
89
- permission_panels = self.get_permission_panel_forms()
90
- if form.is_valid() and all(panel.is_valid() for panel in permission_panels):
91
- response = self.form_valid(form)
92
-
93
- for panel in permission_panels:
94
- panel.save()
95
-
96
- return response
97
- else:
98
- return self.form_invalid(form)
99
-
100
- def get_context_data(self, **kwargs):
101
- context = super().get_context_data(**kwargs)
102
-
103
- # add a 'form_media' variable for the collected js/css media from the form and all formsets
104
- form_media = context["form"].media
105
- for panel in context["permission_panels"]:
106
- form_media += panel.media
107
- context["form_media"] = form_media
108
-
109
- return context
110
+ return self.process_form()
110
111
 
111
112
 
112
113
  class EditView(PermissionPanelFormsMixin, generic.EditView):
@@ -115,35 +116,26 @@ class EditView(PermissionPanelFormsMixin, generic.EditView):
115
116
  delete_item_label = _("Delete group")
116
117
  context_object_name = "group"
117
118
 
119
+ @cached_property
120
+ def header_buttons(self):
121
+ return [
122
+ HeaderButton(
123
+ gettext("View users in this group"),
124
+ url=set_query_params(
125
+ reverse("wagtailusers_users:index"),
126
+ {"group": self.object.pk},
127
+ ),
128
+ icon_name="user",
129
+ )
130
+ ]
131
+
118
132
  def post(self, request, *args, **kwargs):
119
133
  """
120
134
  Handle POST requests: instantiate a form instance with the passed
121
135
  POST variables and then check if it's valid.
122
136
  """
123
137
  self.object = self.get_object()
124
-
125
- form = self.get_form()
126
- permission_panels = self.get_permission_panel_forms()
127
- if form.is_valid() and all(panel.is_valid() for panel in permission_panels):
128
- response = self.form_valid(form)
129
-
130
- for panel in permission_panels:
131
- panel.save()
132
-
133
- return response
134
- else:
135
- return self.form_invalid(form)
136
-
137
- def get_context_data(self, **kwargs):
138
- context = super().get_context_data(**kwargs)
139
-
140
- # add a 'form_media' variable for the collected js/css media from the form and all formsets
141
- form_media = context["form"].media
142
- for panel in context["permission_panels"]:
143
- form_media += panel.media
144
- context["form_media"] = form_media
145
-
146
- return context
138
+ return self.process_form()
147
139
 
148
140
 
149
141
  class DeleteView(generic.DeleteView):
@@ -157,7 +149,6 @@ class GroupViewSet(ModelViewSet):
157
149
  model = Group
158
150
  ordering = ["name"]
159
151
  add_to_reference_index = False
160
- _show_breadcrumbs = False
161
152
 
162
153
  index_view_class = IndexView
163
154
  add_view_class = CreateView
@@ -168,16 +159,25 @@ class GroupViewSet(ModelViewSet):
168
159
 
169
160
  @property
170
161
  def users_view(self):
171
- return Index.as_view()
162
+ def view(request, pk):
163
+ legacy_url = reverse(self.get_url_name("users"), args=(pk,))
164
+ new_url = set_query_params(
165
+ reverse("wagtailusers_users:index"),
166
+ {"group": get_object_or_404(Group, pk=pk).pk},
167
+ )
172
168
 
173
- @property
174
- def users_results_view(self):
175
- return Index.as_view(results_only=True)
169
+ warn(
170
+ f"Accessing the list of users in a group via {legacy_url} is "
171
+ f"deprecated, use {new_url} instead.",
172
+ RemovedInWagtail70Warning,
173
+ )
174
+ return redirect(new_url)
175
+
176
+ return view
176
177
 
177
178
  def get_common_view_kwargs(self, **kwargs):
178
179
  return super().get_common_view_kwargs(
179
180
  **{
180
- "history_url_name": None,
181
181
  "usage_url_name": None,
182
182
  **kwargs,
183
183
  }
@@ -189,7 +189,4 @@ class GroupViewSet(ModelViewSet):
189
189
  def get_urlpatterns(self):
190
190
  return super().get_urlpatterns() + [
191
191
  re_path(r"(\d+)/users/$", self.users_view, name="users"),
192
- re_path(
193
- r"(\d+)/users/results/$", self.users_results_view, name="users_results"
194
- ),
195
192
  ]
@@ -1,18 +1,40 @@
1
+ from warnings import warn
2
+
3
+ import django_filters
1
4
  from django.conf import settings
2
5
  from django.contrib.auth import get_user_model, update_session_auth_hash
3
6
  from django.contrib.auth.models import Group
4
- from django.core.exceptions import PermissionDenied
7
+ from django.core.exceptions import FieldDoesNotExist, PermissionDenied
5
8
  from django.db.models import Q
6
- from django.shortcuts import get_object_or_404
7
- from django.urls import reverse
9
+ from django.forms import CheckboxSelectMultiple
10
+ from django.template import RequestContext
11
+ from django.utils.functional import cached_property
8
12
  from django.utils.translation import gettext as _
9
13
  from django.utils.translation import gettext_lazy
10
14
 
15
+ from wagtail import hooks
16
+ from wagtail.admin.filters import DateRangePickerWidget, WagtailFilterSet
17
+ from wagtail.admin.ui.tables import (
18
+ BulkActionsCheckboxColumn,
19
+ Column,
20
+ DateColumn,
21
+ StatusTagColumn,
22
+ TitleColumn,
23
+ )
24
+ from wagtail.admin.utils import get_user_display_name
11
25
  from wagtail.admin.views.generic import CreateView, DeleteView, EditView, IndexView
26
+ from wagtail.admin.views.generic.history import HistoryView
27
+ from wagtail.admin.viewsets.model import ModelViewSet
28
+ from wagtail.admin.widgets.boolean_radio_select import BooleanRadioSelect
29
+ from wagtail.admin.widgets.button import (
30
+ BaseDropdownMenuButton,
31
+ ButtonWithDropdown,
32
+ )
12
33
  from wagtail.compat import AUTH_USER_APP_LABEL, AUTH_USER_MODEL_NAME
13
- from wagtail.permission_policies import ModelPermissionPolicy
34
+ from wagtail.coreutils import accepts_kwarg
14
35
  from wagtail.users.forms import UserCreationForm, UserEditForm
15
36
  from wagtail.users.utils import user_can_delete_user
37
+ from wagtail.utils.deprecation import RemovedInWagtail70Warning
16
38
  from wagtail.utils.loading import get_custom_form
17
39
 
18
40
  User = get_user_model()
@@ -64,6 +86,45 @@ def get_users_filter_query(q, model_fields):
64
86
  return conditions
65
87
 
66
88
 
89
+ class UserColumn(TitleColumn):
90
+ cell_template_name = "wagtailusers/users/user_cell.html"
91
+
92
+
93
+ class UserFilterSet(WagtailFilterSet):
94
+ is_superuser = django_filters.BooleanFilter(
95
+ label=gettext_lazy("Administrator"),
96
+ widget=BooleanRadioSelect,
97
+ )
98
+ last_login = django_filters.DateFromToRangeFilter(
99
+ label=gettext_lazy("Last login"),
100
+ widget=DateRangePickerWidget,
101
+ )
102
+ group = django_filters.ModelMultipleChoiceFilter(
103
+ field_name="groups",
104
+ queryset=Group.objects.all(),
105
+ label=gettext_lazy("Group"),
106
+ widget=CheckboxSelectMultiple,
107
+ )
108
+
109
+ def __init__(self, data=None, queryset=None, *, request=None, prefix=None):
110
+ super().__init__(data, queryset, request=request, prefix=prefix)
111
+ try:
112
+ self._meta.model._meta.get_field("is_active")
113
+ except FieldDoesNotExist:
114
+ pass
115
+ else:
116
+ self.filters["is_active"] = django_filters.BooleanFilter(
117
+ field_name="is_active",
118
+ label=gettext_lazy("Active"),
119
+ widget=BooleanRadioSelect,
120
+ )
121
+ self.filters.move_to_end("is_active", last=False)
122
+
123
+ class Meta:
124
+ model = User
125
+ fields = []
126
+
127
+
67
128
  class Index(IndexView):
68
129
  """
69
130
  Lists the users for management within the admin.
@@ -71,72 +132,129 @@ class Index(IndexView):
71
132
 
72
133
  template_name = "wagtailusers/users/index.html"
73
134
  results_template_name = "wagtailusers/users/index_results.html"
74
- any_permission_required = ["add", "change", "delete"]
75
- permission_policy = ModelPermissionPolicy(User)
76
- model = User
77
- header_icon = "user"
78
- add_item_label = _("Add a user")
135
+ add_item_label = gettext_lazy("Add a user")
79
136
  context_object_name = "users"
80
- index_url_name = "wagtailusers_users:index"
81
- add_url_name = "wagtailusers_users:add"
82
- edit_url_name = "wagtailusers_users:edit"
83
- default_ordering = "name"
84
- paginate_by = 20
85
137
  is_searchable = True
86
138
  page_title = gettext_lazy("Users")
87
139
  show_other_searches = True
88
- model_fields = [f.name for f in User._meta.get_fields()]
89
-
90
- def setup(self, request, *args, **kwargs):
91
- super().setup(request, *args, **kwargs)
92
- self.group = get_object_or_404(Group, id=args[0]) if args else None
93
- self.group_filter = Q(groups=self.group) if self.group else Q()
94
140
 
95
- def get_index_results_url(self):
96
- if self.group:
97
- return reverse("wagtailusers_groups:users_results", args=[self.group.pk])
98
- else:
99
- return reverse("wagtailusers_users:index_results")
141
+ @cached_property
142
+ def columns(self):
143
+ _UserColumn = self._get_title_column_class(UserColumn)
144
+ return [
145
+ BulkActionsCheckboxColumn("bulk_actions", obj_type="user"),
146
+ _UserColumn(
147
+ "name",
148
+ accessor=lambda u: get_user_display_name(u),
149
+ label=gettext_lazy("Name"),
150
+ sort_key="name"
151
+ if self.model_fields.issuperset({"first_name", "last_name"})
152
+ else None,
153
+ get_url=self.get_edit_url,
154
+ classname="name",
155
+ ),
156
+ Column(
157
+ self.model.USERNAME_FIELD,
158
+ accessor="get_username",
159
+ label=gettext_lazy("Username"),
160
+ sort_key=self.model.USERNAME_FIELD,
161
+ classname="username",
162
+ width="20%",
163
+ ),
164
+ Column(
165
+ "is_superuser",
166
+ accessor=lambda u: gettext_lazy("Admin") if u.is_superuser else None,
167
+ label=gettext_lazy("Access level"),
168
+ sort_key="is_superuser",
169
+ classname="level",
170
+ width="10%",
171
+ ),
172
+ StatusTagColumn(
173
+ "is_active",
174
+ accessor=lambda u: gettext_lazy("Active")
175
+ if u.is_active
176
+ else gettext_lazy("Inactive"),
177
+ primary=lambda u: u.is_active,
178
+ label=gettext_lazy("Status"),
179
+ sort_key="is_active" if "is_active" in self.model_fields else None,
180
+ classname="status",
181
+ width="10%",
182
+ ),
183
+ DateColumn(
184
+ "last_login",
185
+ label=gettext_lazy("Last login"),
186
+ sort_key="last_login",
187
+ classname="last-login",
188
+ width="15%",
189
+ ),
190
+ ]
191
+
192
+ @cached_property
193
+ def model_fields(self):
194
+ return {f.name for f in User._meta.get_fields()}
195
+
196
+ def get_delete_url(self, instance):
197
+ if user_can_delete_user(self.request.user, instance):
198
+ return super().get_delete_url(instance)
199
+
200
+ def get_list_buttons(self, instance):
201
+ more_buttons = self.get_list_more_buttons(instance)
202
+ list_buttons = []
203
+
204
+ for hook in hooks.get_hooks("register_user_listing_buttons"):
205
+ if accepts_kwarg(hook, "request_user"):
206
+ hook_buttons = hook(user=instance, request_user=self.request.user)
207
+ else:
208
+ # old-style hook that accepts a context argument instead of request_user
209
+ hook_buttons = hook(RequestContext(self.request), instance)
210
+ warn(
211
+ "`register_user_listing_buttons` hook functions should accept a `request_user` argument instead of `context` -"
212
+ f" {hook.__module__}.{hook.__name__} needs to be updated",
213
+ category=RemovedInWagtail70Warning,
214
+ )
215
+
216
+ for button in hook_buttons:
217
+ if isinstance(button, BaseDropdownMenuButton):
218
+ # If the button is a dropdown menu, add it to the top-level
219
+ # because we do not support nested dropdowns
220
+ list_buttons.append(button)
221
+ else:
222
+ # Otherwise, add it to the default "More" dropdown
223
+ more_buttons.append(button)
224
+
225
+ list_buttons.append(
226
+ ButtonWithDropdown(
227
+ buttons=sorted(more_buttons),
228
+ icon_name="dots-horizontal",
229
+ attrs={
230
+ "aria-label": _("More options for '%(title)s'")
231
+ % {"title": str(instance)},
232
+ },
233
+ )
234
+ )
100
235
 
101
- def get_valid_orderings(self):
102
- return ["name", "username"]
236
+ return sorted(list_buttons)
103
237
 
104
- def get_queryset(self):
105
- model_fields = set(self.model_fields)
106
- if self.is_searching:
107
- conditions = get_users_filter_query(self.search_query, model_fields)
108
- users = User.objects.filter(self.group_filter & conditions)
109
- else:
110
- users = User.objects.filter(self.group_filter)
111
-
112
- if self.locale:
113
- users = users.filter(locale=self.locale)
238
+ def get_base_queryset(self):
239
+ users = User._default_manager.all()
114
240
 
115
- if "wagtail_userprofile" in model_fields:
241
+ if "wagtail_userprofile" in self.model_fields:
116
242
  users = users.select_related("wagtail_userprofile")
117
243
 
118
- if "last_name" in model_fields and "first_name" in model_fields:
119
- users = users.order_by("last_name", "first_name")
120
-
121
- if self.ordering == "username":
122
- users = users.order_by(User.USERNAME_FIELD)
123
-
124
244
  return users
125
245
 
126
- def get_context_data(self, *args, object_list=None, **kwargs):
127
- context_data = super().get_context_data(
128
- *args, object_list=object_list, **kwargs
129
- )
130
- context_data["ordering"] = self.ordering
131
- context_data["group"] = self.group
246
+ def order_queryset(self, queryset):
247
+ if self.ordering == "name":
248
+ return queryset.order_by("last_name", "first_name")
249
+ if self.ordering == "-name":
250
+ return queryset.order_by("-last_name", "-first_name")
251
+ return super().order_queryset(queryset)
132
252
 
133
- context_data.update(
134
- {
135
- "app_label": User._meta.app_label,
136
- "model_name": User._meta.model_name,
137
- }
138
- )
139
- return context_data
253
+ def search_queryset(self, queryset):
254
+ if self.is_searching:
255
+ conditions = get_users_filter_query(self.search_query, self.model_fields)
256
+ return queryset.filter(conditions)
257
+ return queryset
140
258
 
141
259
 
142
260
  class Create(CreateView):
@@ -144,15 +262,6 @@ class Create(CreateView):
144
262
  Provide the ability to create a user within the admin.
145
263
  """
146
264
 
147
- permission_policy = ModelPermissionPolicy(User)
148
- permission_required = "add"
149
- model = User
150
- form_class = get_user_creation_form()
151
- template_name = "wagtailusers/users/create.html"
152
- header_icon = "user"
153
- add_url_name = "wagtailusers_users:add"
154
- index_url_name = "wagtailusers_users:index"
155
- edit_url_name = "wagtailusers_users:edit"
156
265
  success_message = gettext_lazy("User '%(object)s' created.")
157
266
  page_title = gettext_lazy("Add user")
158
267
 
@@ -169,25 +278,13 @@ class Create(CreateView):
169
278
  self.object,
170
279
  )
171
280
 
172
- def get_add_url(self):
173
- return None
174
-
175
281
 
176
282
  class Edit(EditView):
177
283
  """
178
284
  Provide the ability to edit a user within the admin.
179
285
  """
180
286
 
181
- model = User
182
- permission_policy = ModelPermissionPolicy(User)
183
- form_class = get_user_edit_form()
184
- header_icon = "user"
185
- template_name = "wagtailusers/users/edit.html"
186
- index_url_name = "wagtailusers_users:index"
187
- edit_url_name = "wagtailusers_users:edit"
188
- delete_url_name = "wagtailusers_users:delete"
189
287
  success_message = gettext_lazy("User '%(object)s' updated.")
190
- context_object_name = "user"
191
288
  error_message = gettext_lazy("The user could not be saved due to errors.")
192
289
 
193
290
  def setup(self, request, *args, **kwargs):
@@ -226,15 +323,11 @@ class Edit(EditView):
226
323
  self.object,
227
324
  )
228
325
 
229
- def get_edit_url(self):
230
- return reverse(self.edit_url_name, args=(self.object.pk,))
231
-
232
- def get_delete_url(self):
233
- return reverse(self.delete_url_name, args=(self.object.pk,))
326
+ def get_page_subtitle(self):
327
+ return get_user_display_name(self.object)
234
328
 
235
329
  def get_context_data(self, **kwargs):
236
330
  context = super().get_context_data(**kwargs)
237
- context.pop("action_url")
238
331
  context["can_delete"] = self.can_delete
239
332
  return context
240
333
 
@@ -244,15 +337,7 @@ class Delete(DeleteView):
244
337
  Provide the ability to delete a user within the admin.
245
338
  """
246
339
 
247
- permission_policy = ModelPermissionPolicy(User)
248
- permission_required = "delete"
249
- model = User
250
- template_name = "wagtailusers/users/confirm_delete.html"
251
- delete_url_name = "wagtailusers_users:delete"
252
- edit_url_name = "wagtailusers_users:edit"
253
- index_url_name = "wagtailusers_users:index"
254
340
  page_title = gettext_lazy("Delete user")
255
- context_object_name = "user"
256
341
  success_message = gettext_lazy("User '%(object)s' deleted.")
257
342
 
258
343
  def dispatch(self, request, *args, **kwargs):
@@ -274,3 +359,37 @@ class Delete(DeleteView):
274
359
  self.request,
275
360
  self.object,
276
361
  )
362
+
363
+
364
+ class History(HistoryView):
365
+ def get_page_subtitle(self):
366
+ return get_user_display_name(self.object)
367
+
368
+
369
+ class UserViewSet(ModelViewSet):
370
+ icon = "user"
371
+ model = User
372
+ ordering = "name"
373
+ add_to_reference_index = False
374
+ filterset_class = UserFilterSet
375
+
376
+ index_view_class = Index
377
+ add_view_class = Create
378
+ edit_view_class = Edit
379
+ delete_view_class = Delete
380
+ history_view_class = History
381
+
382
+ template_prefix = "wagtailusers/users/"
383
+
384
+ def get_common_view_kwargs(self, **kwargs):
385
+ return super().get_common_view_kwargs(
386
+ **{
387
+ "usage_url_name": None,
388
+ **kwargs,
389
+ }
390
+ )
391
+
392
+ def get_form_class(self, for_update=False):
393
+ if for_update:
394
+ return get_user_edit_form()
395
+ return get_user_creation_form()
@@ -3,7 +3,7 @@ from django.contrib.auth import get_user_model
3
3
  from django.contrib.auth.models import Permission
4
4
  from django.core.exceptions import ImproperlyConfigured
5
5
  from django.db.models import Q
6
- from django.urls import include, path, reverse
6
+ from django.urls import reverse
7
7
  from django.utils.module_loading import import_string
8
8
  from django.utils.translation import gettext_lazy as _
9
9
 
@@ -14,24 +14,14 @@ from wagtail.admin.admin_url_finder import (
14
14
  )
15
15
  from wagtail.admin.menu import MenuItem
16
16
  from wagtail.admin.search import SearchArea
17
- from wagtail.admin.utils import get_user_display_name
18
17
  from wagtail.compat import AUTH_USER_APP_LABEL, AUTH_USER_MODEL_NAME
19
18
  from wagtail.permission_policies import ModelPermissionPolicy
20
- from wagtail.users.urls import users
21
- from wagtail.users.utils import user_can_delete_user
22
19
  from wagtail.users.views.bulk_actions import (
23
20
  AssignRoleBulkAction,
24
21
  DeleteBulkAction,
25
22
  SetActiveStateBulkAction,
26
23
  )
27
- from wagtail.users.widgets import UserListingButton
28
-
29
-
30
- @hooks.register("register_admin_urls")
31
- def register_admin_urls():
32
- return [
33
- path("users/", include(users, namespace="wagtailusers_users")),
34
- ]
24
+ from wagtail.users.views.users import UserViewSet
35
25
 
36
26
 
37
27
  def get_group_viewset_cls(app_config):
@@ -50,7 +40,10 @@ def get_group_viewset_cls(app_config):
50
40
  def register_viewset():
51
41
  app_config = apps.get_app_config("wagtailusers")
52
42
  group_viewset_cls = get_group_viewset_cls(app_config)
53
- return group_viewset_cls("wagtailusers_groups", url_prefix="groups")
43
+ return [
44
+ UserViewSet("wagtailusers_users", url_prefix="users"),
45
+ group_viewset_cls("wagtailusers_groups", url_prefix="groups"),
46
+ ]
54
47
 
55
48
 
56
49
  # Typically we would check the permission 'auth.change_user' (and 'auth.add_user' /
@@ -143,31 +136,6 @@ def register_users_search_area():
143
136
  )
144
137
 
145
138
 
146
- @hooks.register("register_user_listing_buttons")
147
- def user_listing_buttons(context, user):
148
- yield UserListingButton(
149
- _("Edit"),
150
- reverse("wagtailusers_users:edit", args=[user.pk]),
151
- classname="button-secondary",
152
- attrs={
153
- "aria-label": _("Edit user '%(name)s'")
154
- % {"name": get_user_display_name(user)}
155
- },
156
- priority=10,
157
- )
158
- if user_can_delete_user(context.request.user, user):
159
- yield UserListingButton(
160
- _("Delete"),
161
- reverse("wagtailusers_users:delete", args=[user.pk]),
162
- classname="no",
163
- attrs={
164
- "aria-label": _("Delete user '%(name)s'")
165
- % {"name": get_user_display_name(user)}
166
- },
167
- priority=20,
168
- )
169
-
170
-
171
139
  User = get_user_model()
172
140
 
173
141
 
wagtail/users/widgets.py CHANGED
@@ -1,7 +1,5 @@
1
- from wagtail.admin.widgets import Button
1
+ from wagtail.admin.widgets import ListingButton
2
2
 
3
3
 
4
- class UserListingButton(Button):
5
- def __init__(self, label, url, classname="", **kwargs):
6
- classname = f"{classname} button button-small".strip()
7
- super().__init__(label, url, classname=classname, **kwargs)
4
+ class UserListingButton(ListingButton):
5
+ pass
wagtail/utils/text.py CHANGED
@@ -4,4 +4,4 @@ from django.utils.encoding import force_str
4
4
 
5
5
  def text_from_html(val):
6
6
  # Return the unescaped text content of an HTML string
7
- return BeautifulSoup(force_str(val), "html5lib").getText()
7
+ return BeautifulSoup(force_str(val), "html.parser").getText().strip()