wagtail 6.1.3__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 (257) 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/en/LC_MESSAGES/django.po +405 -295
  7. wagtail/admin/locale/en/LC_MESSAGES/djangojs.po +21 -3
  8. wagtail/admin/locale/sl/LC_MESSAGES/django.mo +0 -0
  9. wagtail/admin/locale/sl/LC_MESSAGES/django.po +30 -0
  10. wagtail/admin/menu.py +2 -2
  11. wagtail/admin/migrations/0004_editingsession.py +57 -0
  12. wagtail/admin/migrations/0005_editingsession_is_editing.py +18 -0
  13. wagtail/admin/models.py +36 -3
  14. wagtail/admin/rich_text/editors/draftail/__init__.py +2 -20
  15. wagtail/admin/static/wagtailadmin/css/core.css +1 -1
  16. wagtail/admin/static/wagtailadmin/js/bulk-actions.js +1 -1
  17. wagtail/admin/static/wagtailadmin/js/chooser-modal.js +1 -1
  18. wagtail/admin/static/wagtailadmin/js/chooser-widget-telepath.js +1 -1
  19. wagtail/admin/static/wagtailadmin/js/chooser-widget.js +1 -1
  20. wagtail/admin/static/wagtailadmin/js/comments.js +1 -1
  21. wagtail/admin/static/wagtailadmin/js/core.js +1 -1
  22. wagtail/admin/static/wagtailadmin/js/date-time-chooser.js +1 -1
  23. wagtail/admin/static/wagtailadmin/js/draftail.js +1 -1
  24. wagtail/admin/static/wagtailadmin/js/expanding-formset.js +1 -1
  25. wagtail/admin/static/wagtailadmin/js/filtered-select.js +1 -1
  26. wagtail/admin/static/wagtailadmin/js/modal-workflow.js +1 -1
  27. wagtail/admin/static/wagtailadmin/js/page-chooser-modal.js +1 -1
  28. wagtail/admin/static/wagtailadmin/js/page-chooser-telepath.js +1 -1
  29. wagtail/admin/static/wagtailadmin/js/page-chooser.js +1 -1
  30. wagtail/admin/static/wagtailadmin/js/preview-panel.js +2 -1
  31. wagtail/admin/static/wagtailadmin/js/preview-panel.js.LICENSE.txt +11 -0
  32. wagtail/admin/static/wagtailadmin/js/privacy-switch.js +1 -1
  33. wagtail/admin/static/wagtailadmin/js/sidebar.js +1 -1
  34. wagtail/admin/static/wagtailadmin/js/task-chooser-modal.js +1 -1
  35. wagtail/admin/static/wagtailadmin/js/task-chooser.js +1 -1
  36. wagtail/admin/static/wagtailadmin/js/telepath/blocks.js +1 -1
  37. wagtail/admin/static/wagtailadmin/js/telepath/widgets.js +1 -1
  38. wagtail/admin/static/wagtailadmin/js/userbar.js +2 -1
  39. wagtail/admin/static/wagtailadmin/js/userbar.js.LICENSE.txt +11 -0
  40. wagtail/admin/static/wagtailadmin/js/vendor.js +1 -1
  41. wagtail/admin/static/wagtailadmin/js/vendor.js.LICENSE.txt +0 -12
  42. wagtail/admin/static/wagtailadmin/js/wagtailadmin.js +1 -1
  43. wagtail/admin/static/wagtailadmin/js/workflow-action.js +1 -1
  44. wagtail/admin/templates/wagtailadmin/collection_privacy/ancestor_privacy.html +2 -6
  45. wagtail/admin/templates/wagtailadmin/generic/index_results.html +1 -17
  46. wagtail/admin/templates/wagtailadmin/generic/listing_results.html +20 -1
  47. wagtail/admin/templates/wagtailadmin/home/workflow_objects_to_moderate.html +2 -11
  48. wagtail/admin/templates/wagtailadmin/page_privacy/ancestor_privacy.html +2 -6
  49. wagtail/admin/templates/wagtailadmin/page_privacy/no_privacy.html +2 -0
  50. wagtail/admin/templates/wagtailadmin/pages/_editor_js.html +0 -1
  51. wagtail/admin/templates/wagtailadmin/pages/action_menu/menu.html +1 -1
  52. wagtail/admin/templates/wagtailadmin/reports/aging_pages_results.html +54 -0
  53. wagtail/admin/templates/wagtailadmin/reports/base_page_report.html +1 -17
  54. wagtail/admin/templates/wagtailadmin/reports/base_page_report_results.html +10 -0
  55. wagtail/admin/templates/wagtailadmin/reports/base_report.html +1 -40
  56. wagtail/admin/templates/wagtailadmin/reports/base_report_results.html +1 -0
  57. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_report.html +21 -27
  58. wagtail/admin/templates/wagtailadmin/reports/listing/_list_page_types_usage.html +48 -54
  59. wagtail/admin/templates/wagtailadmin/reports/{locked_pages.html → locked_pages_results.html} +3 -3
  60. wagtail/admin/templates/wagtailadmin/reports/page_types_usage_results.html +10 -0
  61. wagtail/admin/templates/wagtailadmin/reports/site_history_results.html +53 -0
  62. wagtail/admin/templates/wagtailadmin/reports/workflow_results.html +74 -0
  63. wagtail/admin/templates/wagtailadmin/reports/workflow_tasks_results.html +56 -0
  64. wagtail/admin/templates/wagtailadmin/shared/_workflow_init.html +8 -44
  65. wagtail/admin/templates/wagtailadmin/shared/avatar.html +11 -1
  66. wagtail/admin/templates/wagtailadmin/shared/dialog/dialog.html +5 -4
  67. wagtail/admin/templates/wagtailadmin/shared/dropdown/dropdown_button.html +2 -1
  68. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/list.html +132 -0
  69. wagtail/admin/templates/wagtailadmin/shared/editing_sessions/module.html +44 -0
  70. wagtail/admin/templates/wagtailadmin/shared/headers/slim_header.html +7 -1
  71. wagtail/admin/templates/wagtailadmin/shared/page_status_tag_new.html +1 -1
  72. wagtail/admin/templates/wagtailadmin/shared/side_panels/checks.html +32 -16
  73. wagtail/admin/templates/wagtailadmin/skeleton.html +1 -1
  74. wagtail/admin/templates/wagtailadmin/userbar/item_accessibility.html +9 -11
  75. wagtail/admin/templatetags/wagtailadmin_tags.py +13 -2
  76. wagtail/admin/tests/formats/en/__init__.py +0 -0
  77. wagtail/admin/tests/formats/en/formats.py +1 -0
  78. wagtail/admin/tests/pages/test_create_page.py +47 -0
  79. wagtail/admin/tests/pages/test_edit_page.py +10 -8
  80. wagtail/admin/tests/pages/test_parent_page_chooser_view.py +45 -1
  81. wagtail/admin/tests/test_checks.py +53 -3
  82. wagtail/admin/tests/test_collections_views.py +62 -1
  83. wagtail/admin/tests/test_edit_handlers.py +37 -0
  84. wagtail/admin/tests/test_editing_sessions.py +1336 -0
  85. wagtail/admin/tests/test_icon_sprite.py +12 -21
  86. wagtail/admin/tests/test_page_chooser.py +309 -7
  87. wagtail/admin/tests/test_privacy.py +82 -0
  88. wagtail/admin/tests/test_reports_views.py +464 -70
  89. wagtail/admin/tests/test_userbar.py +93 -6
  90. wagtail/admin/tests/test_workflows.py +223 -33
  91. wagtail/admin/tests/viewsets/test_model_viewset.py +151 -2
  92. wagtail/admin/ui/editing_sessions.py +57 -0
  93. wagtail/admin/urls/__init__.py +9 -15
  94. wagtail/admin/urls/editing_sessions.py +17 -0
  95. wagtail/admin/urls/reports.py +33 -1
  96. wagtail/admin/userbar.py +77 -20
  97. wagtail/admin/views/chooser.py +49 -22
  98. wagtail/admin/views/collections.py +0 -11
  99. wagtail/admin/views/editing_sessions.py +193 -0
  100. wagtail/admin/views/generic/__init__.py +1 -0
  101. wagtail/admin/views/generic/history.py +9 -3
  102. wagtail/admin/views/generic/mixins.py +44 -3
  103. wagtail/admin/views/generic/models.py +46 -72
  104. wagtail/admin/views/generic/permissions.py +20 -10
  105. wagtail/admin/views/home.py +2 -31
  106. wagtail/admin/views/page_privacy.py +20 -5
  107. wagtail/admin/views/pages/choose_parent.py +62 -0
  108. wagtail/admin/views/pages/edit.py +28 -0
  109. wagtail/admin/views/reports/aging_pages.py +6 -10
  110. wagtail/admin/views/reports/audit_logging.py +13 -42
  111. wagtail/admin/views/reports/base.py +31 -4
  112. wagtail/admin/views/reports/locked_pages.py +5 -8
  113. wagtail/admin/views/reports/page_types_usage.py +6 -10
  114. wagtail/admin/views/reports/workflows.py +36 -12
  115. wagtail/admin/viewsets/base.py +8 -3
  116. wagtail/admin/viewsets/chooser.py +1 -1
  117. wagtail/admin/viewsets/model.py +26 -1
  118. wagtail/admin/wagtail_hooks.py +2 -1
  119. wagtail/api/v2/filters.py +6 -0
  120. wagtail/api/v2/tests/test_documents.py +1 -1
  121. wagtail/api/v2/tests/test_images.py +1 -1
  122. wagtail/api/v2/tests/test_pages.py +11 -1
  123. wagtail/api/v2/utils.py +2 -2
  124. wagtail/blocks/base.py +35 -12
  125. wagtail/blocks/definition_lookup.py +85 -0
  126. wagtail/blocks/list_block.py +12 -0
  127. wagtail/blocks/migrations/migrate_operation.py +2 -0
  128. wagtail/blocks/stream_block.py +19 -0
  129. wagtail/blocks/struct_block.py +19 -0
  130. wagtail/contrib/forms/locale/en/LC_MESSAGES/django.po +1 -1
  131. wagtail/contrib/frontend_cache/backends/__init__.py +5 -0
  132. wagtail/contrib/frontend_cache/backends/azure.py +179 -0
  133. wagtail/contrib/frontend_cache/backends/base.py +28 -0
  134. wagtail/contrib/frontend_cache/backends/cloudflare.py +114 -0
  135. wagtail/contrib/frontend_cache/backends/cloudfront.py +99 -0
  136. wagtail/contrib/frontend_cache/backends/http.py +64 -0
  137. wagtail/contrib/frontend_cache/tests.py +59 -17
  138. wagtail/contrib/frontend_cache/utils.py +26 -8
  139. wagtail/contrib/redirects/filters.py +15 -1
  140. wagtail/contrib/redirects/locale/en/LC_MESSAGES/django.po +37 -72
  141. wagtail/contrib/redirects/models.py +6 -5
  142. wagtail/contrib/redirects/templates/wagtailredirects/edit.html +1 -38
  143. wagtail/contrib/redirects/tests/test_redirects.py +141 -1
  144. wagtail/contrib/redirects/urls.py +1 -2
  145. wagtail/contrib/redirects/views.py +39 -80
  146. wagtail/contrib/routable_page/models.py +6 -4
  147. wagtail/contrib/routable_page/tests.py +11 -0
  148. wagtail/contrib/search_promotions/locale/en/LC_MESSAGES/django.po +1 -1
  149. wagtail/contrib/settings/locale/en/LC_MESSAGES/django.po +4 -4
  150. wagtail/contrib/simple_translation/locale/en/LC_MESSAGES/django.po +5 -1
  151. wagtail/contrib/simple_translation/models.py +2 -1
  152. wagtail/contrib/styleguide/locale/en/LC_MESSAGES/django.po +7 -7
  153. wagtail/contrib/table_block/locale/en/LC_MESSAGES/django.po +1 -1
  154. wagtail/contrib/table_block/static/table_block/js/table.js +1 -1
  155. wagtail/contrib/typed_table_block/blocks.py +19 -0
  156. wagtail/contrib/typed_table_block/locale/en/LC_MESSAGES/django.po +10 -10
  157. wagtail/contrib/typed_table_block/static/typed_table_block/js/typed_table_block.js +1 -1
  158. wagtail/contrib/typed_table_block/tests.py +38 -0
  159. wagtail/coreutils.py +1 -1
  160. wagtail/documents/__init__.py +1 -1
  161. wagtail/documents/locale/en/LC_MESSAGES/django.po +8 -8
  162. wagtail/documents/locale/sl/LC_MESSAGES/django.mo +0 -0
  163. wagtail/documents/locale/sl/LC_MESSAGES/django.po +20 -0
  164. wagtail/documents/models.py +5 -1
  165. wagtail/documents/static/wagtaildocs/js/document-chooser-modal.js +1 -1
  166. wagtail/documents/static/wagtaildocs/js/document-chooser-telepath.js +1 -1
  167. wagtail/documents/static/wagtaildocs/js/document-chooser.js +1 -1
  168. wagtail/documents/tests/test_models.py +5 -1
  169. wagtail/embeds/apps.py +2 -0
  170. wagtail/embeds/embeds.py +12 -14
  171. wagtail/embeds/finders/__init__.py +2 -0
  172. wagtail/embeds/finders/facebook.py +17 -33
  173. wagtail/embeds/finders/instagram.py +19 -16
  174. wagtail/embeds/locale/en/LC_MESSAGES/django.po +1 -1
  175. wagtail/embeds/signal_handlers.py +13 -0
  176. wagtail/embeds/tests/test_embeds.py +7 -7
  177. wagtail/fields.py +58 -14
  178. wagtail/images/__init__.py +1 -1
  179. wagtail/images/locale/en/LC_MESSAGES/django.po +34 -34
  180. wagtail/images/locale/sl/LC_MESSAGES/django.mo +0 -0
  181. wagtail/images/locale/sl/LC_MESSAGES/django.po +20 -0
  182. wagtail/images/models.py +2 -0
  183. wagtail/images/static/wagtailimages/js/image-chooser-modal.js +1 -1
  184. wagtail/images/static/wagtailimages/js/image-chooser-telepath.js +1 -1
  185. wagtail/images/static/wagtailimages/js/image-chooser.js +1 -1
  186. wagtail/images/templates/wagtailimages/images/edit.html +4 -4
  187. wagtail/images/tests/test_admin_views.py +26 -2
  188. wagtail/images/views/chooser.py +6 -1
  189. wagtail/locale/en/LC_MESSAGES/django.po +84 -80
  190. wagtail/locales/locale/en/LC_MESSAGES/django.po +2 -2
  191. wagtail/locales/tests.py +16 -0
  192. wagtail/locales/wagtail_hooks.py +0 -9
  193. wagtail/migrations/0094_alter_page_locale.py +19 -0
  194. wagtail/models/__init__.py +11 -5
  195. wagtail/models/i18n.py +6 -1
  196. wagtail/project_template/requirements.txt +1 -1
  197. wagtail/search/locale/en/LC_MESSAGES/django.po +1 -1
  198. wagtail/signals.py +4 -0
  199. wagtail/sites/locale/en/LC_MESSAGES/django.po +2 -2
  200. wagtail/sites/tests.py +15 -0
  201. wagtail/sites/wagtail_hooks.py +0 -9
  202. wagtail/snippets/locale/en/LC_MESSAGES/django.po +9 -9
  203. wagtail/snippets/permissions.py +5 -3
  204. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser-telepath.js +1 -1
  205. wagtail/snippets/static/wagtailsnippets/js/snippet-chooser.js +1 -1
  206. wagtail/snippets/templates/wagtailsnippets/snippets/action_menu/menu.html +1 -1
  207. wagtail/snippets/tests/test_snippets.py +78 -12
  208. wagtail/snippets/tests/test_viewset.py +22 -0
  209. wagtail/snippets/views/snippets.py +19 -14
  210. wagtail/snippets/wagtail_hooks.py +2 -10
  211. wagtail/templatetags/wagtailcore_tags.py +3 -0
  212. wagtail/test/dummy_external_storage.py +1 -1
  213. wagtail/test/i18n/migrations/0003_alter_clusterabletestmodel_locale_and_more.py +40 -0
  214. wagtail/test/routablepage/models.py +4 -0
  215. wagtail/test/snippets/migrations/0012_alter_translatablesnippet_locale.py +20 -0
  216. wagtail/test/testapp/migrations/0038_sociallink.py +52 -0
  217. wagtail/test/testapp/migrations/0039_alter_eventcategory_locale_and_more.py +45 -0
  218. wagtail/test/testapp/models.py +24 -0
  219. wagtail/test/testapp/views.py +1 -0
  220. wagtail/test/testapp/wagtail_hooks.py +9 -0
  221. wagtail/test/urls_multilang.py +6 -1
  222. wagtail/test/urls_multilang_non_root.py +11 -0
  223. wagtail/tests/streamfield_migrations/test_migrations.py +53 -12
  224. wagtail/tests/test_audit_log.py +72 -2
  225. wagtail/tests/test_blocks.py +103 -0
  226. wagtail/tests/test_signals.py +49 -2
  227. wagtail/tests/test_streamfield.py +153 -0
  228. wagtail/tests/test_utils.py +14 -0
  229. wagtail/tests/tests.py +5 -0
  230. wagtail/users/apps.py +1 -0
  231. wagtail/users/forms.py +7 -0
  232. wagtail/users/locale/en/LC_MESSAGES/django.po +55 -50
  233. wagtail/users/models.py +1 -0
  234. wagtail/users/templates/wagtailusers/groups/includes/formatted_permissions.html +3 -2
  235. wagtail/users/templatetags/wagtailusers_tags.py +9 -0
  236. wagtail/users/tests/__init__.py +7 -1
  237. wagtail/users/tests/test_admin_views.py +117 -32
  238. wagtail/users/views/groups.py +4 -0
  239. wagtail/users/views/users.py +58 -14
  240. wagtail/users/wagtail_hooks.py +7 -123
  241. wagtail/utils/utils.py +1 -0
  242. wagtail/utils/version.py +5 -2
  243. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/METADATA +3 -3
  244. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/RECORD +248 -222
  245. wagtail/admin/templates/wagtailadmin/reports/aging_pages.html +0 -58
  246. wagtail/admin/templates/wagtailadmin/reports/page_types_usage.html +0 -18
  247. wagtail/admin/templates/wagtailadmin/reports/site_history.html +0 -57
  248. wagtail/admin/templates/wagtailadmin/reports/workflow.html +0 -81
  249. wagtail/admin/templates/wagtailadmin/reports/workflow_tasks.html +0 -63
  250. wagtail/contrib/frontend_cache/backends.py +0 -400
  251. wagtail/contrib/redirects/templates/wagtailredirects/list.html +0 -43
  252. wagtail/contrib/redirects/templates/wagtailredirects/reports/redirects_report.html +0 -14
  253. wagtail/contrib/redirects/tests/test_reports_view.py +0 -82
  254. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/LICENSE +0 -0
  255. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/WHEEL +0 -0
  256. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/entry_points.txt +0 -0
  257. {wagtail-6.1.3.dist-info → wagtail-6.2rc1.dist-info}/top_level.txt +0 -0
wagtail/users/forms.py CHANGED
@@ -1,4 +1,5 @@
1
1
  from itertools import groupby
2
+ from warnings import warn
2
3
 
3
4
  from django import forms
4
5
  from django.conf import settings
@@ -21,6 +22,7 @@ from wagtail.models import (
21
22
  GroupPagePermission,
22
23
  Page,
23
24
  )
25
+ from wagtail.utils.deprecation import RemovedInWagtail70Warning
24
26
 
25
27
  User = get_user_model()
26
28
 
@@ -29,6 +31,11 @@ standard_fields = {"email", "first_name", "last_name", "is_superuser", "groups"}
29
31
  # Custom fields
30
32
  if hasattr(settings, "WAGTAIL_USER_CUSTOM_FIELDS"):
31
33
  custom_fields = set(settings.WAGTAIL_USER_CUSTOM_FIELDS)
34
+ warn(
35
+ "The `WAGTAIL_USER_CUSTOM_FIELDS` setting is deprecated. Use a custom "
36
+ "`UserViewSet` subclass and override `get_form_class()` instead.",
37
+ RemovedInWagtail70Warning,
38
+ )
32
39
  else:
33
40
  custom_fields = set()
34
41
 
@@ -8,7 +8,7 @@ msgid ""
8
8
  msgstr ""
9
9
  "Project-Id-Version: PACKAGE VERSION\n"
10
10
  "Report-Msgid-Bugs-To: \n"
11
- "POT-Creation-Date: 2024-04-18 17:28+0100\n"
11
+ "POT-Creation-Date: 2024-07-19 16:26+0100\n"
12
12
  "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
13
13
  "Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
14
14
  "Language-Team: LANGUAGE <LL@li.org>\n"
@@ -22,63 +22,63 @@ msgstr ""
22
22
  msgid "Wagtail users"
23
23
  msgstr ""
24
24
 
25
- #: forms.py:48
25
+ #: forms.py:55
26
26
  msgid "Required. Letters, digits and @/./+/-/_ only."
27
27
  msgstr ""
28
28
 
29
- #: forms.py:53
29
+ #: forms.py:60
30
30
  msgid "This value may contain only letters, numbers and @/./+/-/_ characters."
31
31
  msgstr ""
32
32
 
33
- #: forms.py:79
33
+ #: forms.py:86
34
34
  msgid "A user with that username already exists."
35
35
  msgstr ""
36
36
 
37
- #: forms.py:80
37
+ #: forms.py:87
38
38
  msgid "The two password fields didn't match."
39
39
  msgstr ""
40
40
 
41
- #: forms.py:83
41
+ #: forms.py:90
42
42
  msgid "Email"
43
43
  msgstr ""
44
44
 
45
- #: forms.py:84
45
+ #: forms.py:91
46
46
  msgid "First Name"
47
47
  msgstr ""
48
48
 
49
- #: forms.py:85
49
+ #: forms.py:92
50
50
  msgid "Last Name"
51
51
  msgstr ""
52
52
 
53
- #: forms.py:88
53
+ #: forms.py:95
54
54
  msgid "Password"
55
55
  msgstr ""
56
56
 
57
- #: forms.py:91
57
+ #: forms.py:98
58
58
  msgid "Leave blank if not changing."
59
59
  msgstr ""
60
60
 
61
- #: forms.py:95
61
+ #: forms.py:102
62
62
  msgid "Password confirmation"
63
63
  msgstr ""
64
64
 
65
- #: forms.py:98
65
+ #: forms.py:105
66
66
  msgid "Enter the same password as above, for verification."
67
67
  msgstr ""
68
68
 
69
- #: forms.py:103 forms.py:240 views/users.py:95
69
+ #: forms.py:110 forms.py:247 views/users.py:109
70
70
  msgid "Administrator"
71
71
  msgstr ""
72
72
 
73
- #: forms.py:106 forms.py:242
73
+ #: forms.py:113 forms.py:249
74
74
  msgid "Administrators have full access to manage any object or setting."
75
75
  msgstr ""
76
76
 
77
- #: forms.py:236
77
+ #: forms.py:243
78
78
  msgid "A group with that name already exists."
79
79
  msgstr ""
80
80
 
81
- #: forms.py:371
81
+ #: forms.py:378
82
82
  msgid "You cannot have multiple permission records for the same page."
83
83
  msgstr ""
84
84
 
@@ -160,15 +160,16 @@ msgstr ""
160
160
  msgid "Snug"
161
161
  msgstr ""
162
162
 
163
- #: models.py:92
163
+ #. Translators: "Density" is the term used to describe the amount of space between elements in the user interface
164
+ #: models.py:93
164
165
  msgid "density"
165
166
  msgstr ""
166
167
 
167
- #: models.py:112
168
+ #: models.py:113
168
169
  msgid "user profile"
169
170
  msgstr ""
170
171
 
171
- #: models.py:113
172
+ #: models.py:114
172
173
  msgid "user profiles"
173
174
  msgstr ""
174
175
 
@@ -305,8 +306,8 @@ msgid "Object permissions"
305
306
  msgstr ""
306
307
 
307
308
  #: templates/wagtailusers/groups/includes/formatted_permissions.html:29
308
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:166
309
- #: views/groups.py:92 views/users.py:149
309
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:167
310
+ #: views/groups.py:92 views/users.py:163
310
311
  msgid "Name"
311
312
  msgstr ""
312
313
 
@@ -335,40 +336,40 @@ msgstr ""
335
336
  msgid "Custom permissions"
336
337
  msgstr ""
337
338
 
338
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:112
339
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:113
339
340
  msgid "Toggle all"
340
341
  msgstr ""
341
342
 
342
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:115
343
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:116
343
344
  msgid "Toggle all add permissions"
344
345
  msgstr ""
345
346
 
346
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:119
347
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:120
347
348
  msgid "Toggle all change permissions"
348
349
  msgstr ""
349
350
 
350
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:123
351
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:124
351
352
  msgid "Toggle all delete permissions"
352
353
  msgstr ""
353
354
 
354
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:128
355
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:129
355
356
  msgid "Toggle all publish permissions"
356
357
  msgstr ""
357
358
 
358
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:134
359
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:135
359
360
  msgid "Toggle all lock permissions"
360
361
  msgstr ""
361
362
 
362
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:140
363
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:141
363
364
  msgid "Toggle all unlock permissions"
364
365
  msgstr ""
365
366
 
366
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:146
367
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:147
367
368
  msgid "Toggle all custom permissions"
368
369
  msgstr ""
369
370
 
370
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:156
371
- #: templates/wagtailusers/groups/includes/formatted_permissions.html:159
371
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:157
372
+ #: templates/wagtailusers/groups/includes/formatted_permissions.html:160
372
373
  msgid "Other permissions"
373
374
  msgstr ""
374
375
 
@@ -411,12 +412,12 @@ msgstr ""
411
412
  msgid "Roles"
412
413
  msgstr ""
413
414
 
414
- #: templates/wagtailusers/users/create.html:61 views/users.py:266
415
+ #: templates/wagtailusers/users/create.html:61 views/users.py:280
415
416
  msgid "Add user"
416
417
  msgstr ""
417
418
 
418
419
  #: templates/wagtailusers/users/edit.html:50
419
- #: templates/wagtailusers/users/edit.html:71 views/users.py:340
420
+ #: templates/wagtailusers/users/edit.html:71 views/users.py:355
420
421
  msgid "Delete user"
421
422
  msgstr ""
422
423
 
@@ -435,6 +436,10 @@ msgid ""
435
436
  "href=\"%(wagtailusers_add_url)s\">add some</a>?"
436
437
  msgstr ""
437
438
 
439
+ #: templatetags/wagtailusers_tags.py:64
440
+ msgid "Can view"
441
+ msgstr ""
442
+
438
443
  #: utils.py:53
439
444
  #, python-format
440
445
  msgid "user %(id)s (deleted)"
@@ -462,12 +467,12 @@ msgid_plural "%(num_parent_objects)d users have been deleted"
462
467
  msgstr[0] ""
463
468
  msgstr[1] ""
464
469
 
465
- #: views/bulk_actions/set_active_state.py:11 views/users.py:118
466
- #: views/users.py:174
470
+ #: views/bulk_actions/set_active_state.py:11 views/users.py:132
471
+ #: views/users.py:188
467
472
  msgid "Active"
468
473
  msgstr ""
469
474
 
470
- #: views/bulk_actions/set_active_state.py:11 views/users.py:176
475
+ #: views/bulk_actions/set_active_state.py:11 views/users.py:190
471
476
  msgid "Inactive"
472
477
  msgstr ""
473
478
 
@@ -493,7 +498,7 @@ msgid_plural "%(num_parent_objects)d users have been marked as inactive"
493
498
  msgstr[0] ""
494
499
  msgstr[1] ""
495
500
 
496
- #: views/groups.py:82 wagtail_hooks.py:93
501
+ #: views/groups.py:82 views/groups.py:153
497
502
  msgid "Groups"
498
503
  msgstr ""
499
504
 
@@ -528,58 +533,58 @@ msgstr ""
528
533
  msgid "Group '%(object)s' deleted."
529
534
  msgstr ""
530
535
 
531
- #: views/users.py:99 views/users.py:185
536
+ #: views/users.py:113 views/users.py:199
532
537
  msgid "Last login"
533
538
  msgstr ""
534
539
 
535
- #: views/users.py:105
540
+ #: views/users.py:119
536
541
  msgid "Group"
537
542
  msgstr ""
538
543
 
539
- #: views/users.py:135
544
+ #: views/users.py:149
540
545
  msgid "Add a user"
541
546
  msgstr ""
542
547
 
543
- #: views/users.py:138 wagtail_hooks.py:73 wagtail_hooks.py:131
548
+ #: views/users.py:152 views/users.py:392 views/users.py:429
544
549
  msgid "Users"
545
550
  msgstr ""
546
551
 
547
- #: views/users.py:159
552
+ #: views/users.py:173
548
553
  msgid "Username"
549
554
  msgstr ""
550
555
 
551
- #: views/users.py:166
556
+ #: views/users.py:180
552
557
  msgid "Admin"
553
558
  msgstr ""
554
559
 
555
- #: views/users.py:167
560
+ #: views/users.py:181
556
561
  msgid "Access level"
557
562
  msgstr ""
558
563
 
559
- #: views/users.py:178
564
+ #: views/users.py:192
560
565
  msgid "Status"
561
566
  msgstr ""
562
567
 
563
- #: views/users.py:230
568
+ #: views/users.py:244
564
569
  #, python-format
565
570
  msgid "More options for '%(title)s'"
566
571
  msgstr ""
567
572
 
568
- #: views/users.py:265
573
+ #: views/users.py:279
569
574
  #, python-format
570
575
  msgid "User '%(object)s' created."
571
576
  msgstr ""
572
577
 
573
- #: views/users.py:287
578
+ #: views/users.py:301
574
579
  #, python-format
575
580
  msgid "User '%(object)s' updated."
576
581
  msgstr ""
577
582
 
578
- #: views/users.py:288
583
+ #: views/users.py:302
579
584
  msgid "The user could not be saved due to errors."
580
585
  msgstr ""
581
586
 
582
- #: views/users.py:341
587
+ #: views/users.py:356
583
588
  #, python-format
584
589
  msgid "User '%(object)s' deleted."
585
590
  msgstr ""
wagtail/users/models.py CHANGED
@@ -89,6 +89,7 @@ class UserProfile(models.Model):
89
89
  SNUG = "snug", _("Snug")
90
90
 
91
91
  density = models.CharField(
92
+ # Translators: "Density" is the term used to describe the amount of space between elements in the user interface
92
93
  verbose_name=_("density"),
93
94
  choices=AdminDensityThemes.choices,
94
95
  default=AdminDensityThemes.DEFAULT,
@@ -96,7 +96,8 @@
96
96
  <fieldset class="w-p-0">
97
97
  <legend class="w-sr-only">{% trans "Custom permissions" %}</legend>
98
98
  {% for custom_perm in content_perms_dict.custom %}
99
- {% include "wagtailadmin/shared/forms/single_checkbox.html" with name="permissions" value=custom_perm.perm.id checked=custom_perm.selected text=custom_perm.name attrs=custom_perm.attrs %}
99
+ {% trans custom_perm.name as custom_perm_label %}
100
+ {% include "wagtailadmin/shared/forms/single_checkbox.html" with name="permissions" value=custom_perm.perm.id checked=custom_perm.selected text=custom_perm_label attrs=custom_perm.attrs %}
100
101
  {% endfor %}
101
102
  </fieldset>
102
103
  {% endif %}
@@ -170,7 +171,7 @@
170
171
  <tbody>
171
172
  {% for perm_tuple in other_perms %}
172
173
  <tr>
173
- <td class="title"><label for="{{ perm_tuple.1.id_for_label }}" class="w-label-3">{{ perm_tuple.0.name }}</label></td>
174
+ <td class="title"><label for="{{ perm_tuple.1.id_for_label }}" class="w-label-3">{% trans perm_tuple.0.name %}</label></td>
174
175
  <td>
175
176
  {{ perm_tuple.1.tag }}
176
177
  </td>
@@ -6,6 +6,7 @@ from django.contrib.auth import get_permission_codename
6
6
  from django.contrib.auth.models import Permission
7
7
  from django.contrib.contenttypes.models import ContentType
8
8
  from django.utils.text import camel_case_to_spaces
9
+ from django.utils.translation import gettext_noop
9
10
 
10
11
  from wagtail import hooks
11
12
  from wagtail.admin.models import Admin
@@ -55,6 +56,14 @@ def normalize_permission_label(permission: Permission):
55
56
  return label
56
57
 
57
58
 
59
+ # normalize_permission_label will return "Can view" for Django's standard "Can view X" permission.
60
+ # formatted_permissions.html passes these labels through {% trans %} - since this is a variable
61
+ # within the template it will not be picked up by makemessages, so we define a translation here
62
+ # instead.
63
+
64
+ VIEW_PERMISSION_LABEL = gettext_noop("Can view")
65
+
66
+
58
67
  @register.inclusion_tag("wagtailusers/groups/includes/formatted_permissions.html")
59
68
  def format_permissions(permission_bound_field):
60
69
  """
@@ -2,6 +2,12 @@ from .test_admin_views import (
2
2
  CustomGroupViewSet,
3
3
  CustomUserCreationForm,
4
4
  CustomUserEditForm,
5
+ CustomUserViewSet,
5
6
  )
6
7
 
7
- __all__ = ["CustomGroupViewSet", "CustomUserCreationForm", "CustomUserEditForm"]
8
+ __all__ = [
9
+ "CustomGroupViewSet",
10
+ "CustomUserViewSet",
11
+ "CustomUserCreationForm",
12
+ "CustomUserEditForm",
13
+ ]
@@ -33,12 +33,16 @@ from wagtail.models import (
33
33
  )
34
34
  from wagtail.test.utils import WagtailTestUtils
35
35
  from wagtail.test.utils.template_tests import AdminTemplateTestUtils
36
- from wagtail.users.forms import UserCreationForm, UserEditForm
36
+ from wagtail.users.forms import GroupForm, UserCreationForm, UserEditForm
37
37
  from wagtail.users.models import UserProfile
38
38
  from wagtail.users.permission_order import register as register_permission_order
39
39
  from wagtail.users.views.groups import GroupViewSet
40
- from wagtail.users.views.users import get_user_creation_form, get_user_edit_form
41
- from wagtail.users.wagtail_hooks import get_group_viewset_cls
40
+ from wagtail.users.views.users import (
41
+ UserViewSet,
42
+ get_user_creation_form,
43
+ get_user_edit_form,
44
+ )
45
+ from wagtail.users.wagtail_hooks import get_viewset_cls
42
46
  from wagtail.users.widgets import UserListingButton
43
47
  from wagtail.utils.deprecation import RemovedInWagtail70Warning
44
48
 
@@ -53,6 +57,10 @@ def test_avatar_provider(user, default, size=50):
53
57
  return "/nonexistent/path/to/avatar.png"
54
58
 
55
59
 
60
+ class CustomGroupForm(GroupForm):
61
+ pass
62
+
63
+
56
64
  class CustomUserCreationForm(UserCreationForm):
57
65
  country = forms.CharField(required=True, label="Country")
58
66
  attachment = forms.FileField(required=True, label="Attachment")
@@ -66,6 +74,18 @@ class CustomUserEditForm(UserEditForm):
66
74
  class CustomGroupViewSet(GroupViewSet):
67
75
  icon = "custom-icon"
68
76
 
77
+ def get_form_class(self, for_update=False):
78
+ return CustomGroupForm
79
+
80
+
81
+ class CustomUserViewSet(UserViewSet):
82
+ icon = "custom-icon"
83
+
84
+ def get_form_class(self, for_update=False):
85
+ if for_update:
86
+ return CustomUserEditForm
87
+ return CustomUserCreationForm
88
+
69
89
 
70
90
  class TestUserFormHelpers(TestCase):
71
91
  def test_get_user_edit_form_with_default_form(self):
@@ -80,25 +100,45 @@ class TestUserFormHelpers(TestCase):
80
100
  WAGTAIL_USER_CREATION_FORM="wagtail.users.tests.CustomUserCreationForm"
81
101
  )
82
102
  def test_get_user_creation_form_with_custom_form(self):
83
- user_form = get_user_creation_form()
103
+ with self.assertWarnsMessage(
104
+ RemovedInWagtail70Warning,
105
+ "The `WAGTAIL_USER_CREATION_FORM` setting is deprecated. Use a custom "
106
+ "`UserViewSet` subclass and override `get_form_class()` instead.",
107
+ ):
108
+ user_form = get_user_creation_form()
84
109
  self.assertIs(user_form, CustomUserCreationForm)
85
110
 
86
111
  @override_settings(WAGTAIL_USER_EDIT_FORM="wagtail.users.tests.CustomUserEditForm")
87
112
  def test_get_user_edit_form_with_custom_form(self):
88
- user_form = get_user_edit_form()
113
+ with self.assertWarnsMessage(
114
+ RemovedInWagtail70Warning,
115
+ "The `WAGTAIL_USER_EDIT_FORM` setting is deprecated. Use a custom "
116
+ "`UserViewSet` subclass and override `get_form_class()` instead.",
117
+ ):
118
+ user_form = get_user_edit_form()
89
119
  self.assertIs(user_form, CustomUserEditForm)
90
120
 
91
121
  @override_settings(
92
122
  WAGTAIL_USER_CREATION_FORM="wagtail.users.tests.CustomUserCreationFormDoesNotExist"
93
123
  )
94
124
  def test_get_user_creation_form_with_invalid_form(self):
95
- self.assertRaises(ImproperlyConfigured, get_user_creation_form)
125
+ with self.assertWarnsMessage(
126
+ RemovedInWagtail70Warning,
127
+ "The `WAGTAIL_USER_CREATION_FORM` setting is deprecated. Use a custom "
128
+ "`UserViewSet` subclass and override `get_form_class()` instead.",
129
+ ):
130
+ self.assertRaises(ImproperlyConfigured, get_user_creation_form)
96
131
 
97
132
  @override_settings(
98
133
  WAGTAIL_USER_EDIT_FORM="wagtail.users.tests.CustomUserEditFormDoesNotExist"
99
134
  )
100
135
  def test_get_user_edit_form_with_invalid_form(self):
101
- self.assertRaises(ImproperlyConfigured, get_user_edit_form)
136
+ with self.assertWarnsMessage(
137
+ RemovedInWagtail70Warning,
138
+ "The `WAGTAIL_USER_EDIT_FORM` setting is deprecated. Use a custom "
139
+ "`UserViewSet` subclass and override `get_form_class()` instead.",
140
+ ):
141
+ self.assertRaises(ImproperlyConfigured, get_user_edit_form)
102
142
 
103
143
 
104
144
  class TestGroupUsersView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
@@ -466,7 +506,6 @@ class TestUserCreateView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
466
506
  )
467
507
  @override_settings(
468
508
  WAGTAIL_USER_CREATION_FORM="wagtail.users.tests.CustomUserCreationForm",
469
- WAGTAIL_USER_CUSTOM_FIELDS=["country", "document"],
470
509
  )
471
510
  def test_create_with_custom_form(self):
472
511
  response = self.post(
@@ -1625,7 +1664,7 @@ class TestGroupIndexView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
1625
1664
  self.assertEqual(names, ["Editors", "Moderators", "Photographers"])
1626
1665
 
1627
1666
 
1628
- class TestGroupIndexResultsView(WagtailTestUtils, TestCase):
1667
+ class TestGroupIndexResultsView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
1629
1668
  def setUp(self):
1630
1669
  self.login()
1631
1670
 
@@ -1637,7 +1676,7 @@ class TestGroupIndexResultsView(WagtailTestUtils, TestCase):
1637
1676
  self.assertEqual(response.status_code, 200)
1638
1677
  self.assertTemplateUsed(response, "wagtailadmin/generic/listing_results.html")
1639
1678
  # response should not contain page furniture
1640
- self.assertNotContains(response, "Add a group")
1679
+ self.assertBreadcrumbsNotRendered(response.content)
1641
1680
 
1642
1681
  def test_search(self):
1643
1682
  response = self.get({"q": "Hello"})
@@ -2397,7 +2436,7 @@ class TestGroupEditView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
2397
2436
 
2398
2437
  def test_is_custom_permission_checked(self):
2399
2438
  # Add a permission from the 'custom permission' column to the user's group
2400
- custom_permission = Permission.objects.get(codename="view_fancysnippet")
2439
+ custom_permission = Permission.objects.get(codename="view_fullfeaturedsnippet")
2401
2440
  self.test_group.permissions.add(custom_permission)
2402
2441
 
2403
2442
  response = self.get()
@@ -2566,44 +2605,76 @@ class TestGroupHistoryView(WagtailTestUtils, TestCase):
2566
2605
 
2567
2606
 
2568
2607
  class TestGroupViewSet(TestCase):
2608
+ app_config_attr = "group_viewset"
2609
+ default_viewset_cls = GroupViewSet
2610
+ custom_viewset_cls = CustomGroupViewSet
2611
+ create_form_cls = CustomGroupForm
2612
+ edit_form_cls = CustomGroupForm
2613
+
2569
2614
  def setUp(self):
2570
2615
  self.app_config = apps.get_app_config("wagtailusers")
2571
2616
 
2572
- def test_get_group_viewset_cls(self):
2573
- self.assertIs(get_group_viewset_cls(self.app_config), GroupViewSet)
2617
+ def test_get_viewset_cls(self):
2618
+ self.assertIs(
2619
+ get_viewset_cls(self.app_config, self.app_config_attr),
2620
+ self.default_viewset_cls,
2621
+ )
2574
2622
 
2575
- def test_get_group_viewset_cls_with_custom_form(self):
2623
+ def test_get_viewset_cls_with_custom_form(self):
2576
2624
  with unittest.mock.patch.object(
2577
2625
  self.app_config,
2578
- "group_viewset",
2579
- new="wagtail.users.tests.CustomGroupViewSet",
2626
+ self.app_config_attr,
2627
+ new=f"wagtail.users.tests.{self.custom_viewset_cls.__name__}",
2580
2628
  ):
2581
- group_viewset = get_group_viewset_cls(self.app_config)
2582
- self.assertIs(group_viewset, CustomGroupViewSet)
2629
+ group_viewset = get_viewset_cls(self.app_config, self.app_config_attr)
2630
+ self.assertIs(group_viewset, self.custom_viewset_cls)
2583
2631
  self.assertEqual(group_viewset.icon, "custom-icon")
2632
+ viewset = group_viewset()
2633
+ self.assertIs(viewset.get_form_class(for_update=False), self.create_form_cls)
2634
+ self.assertIs(viewset.get_form_class(for_update=True), self.edit_form_cls)
2584
2635
 
2585
- def test_get_group_viewset_cls_custom_form_invalid_value(self):
2636
+ def test_get_viewset_cls_custom_form_invalid_value(self):
2586
2637
  with unittest.mock.patch.object(
2587
- self.app_config, "group_viewset", new="asdfasdf"
2638
+ self.app_config, self.app_config_attr, new="asdfasdf"
2588
2639
  ):
2589
- with self.assertRaises(ImproperlyConfigured) as exc_info:
2590
- get_group_viewset_cls(self.app_config)
2591
- self.assertIn(
2592
- "asdfasdf doesn't look like a module path", str(exc_info.exception)
2593
- )
2640
+ with self.assertRaisesMessage(
2641
+ ImproperlyConfigured,
2642
+ f"Invalid setting for WagtailUsersAppConfig.{self.app_config_attr}: "
2643
+ "asdfasdf doesn't look like a module path",
2644
+ ):
2645
+ get_viewset_cls(self.app_config, self.app_config_attr)
2594
2646
 
2595
- def test_get_group_viewset_cls_custom_form_does_not_exist(self):
2647
+ def test_get_viewset_cls_custom_form_does_not_exist(self):
2596
2648
  with unittest.mock.patch.object(
2597
2649
  self.app_config,
2598
- "group_viewset",
2650
+ self.app_config_attr,
2599
2651
  new="wagtail.users.tests.CustomClassDoesNotExist",
2600
2652
  ):
2601
- with self.assertRaises(ImproperlyConfigured) as exc_info:
2602
- get_group_viewset_cls(self.app_config)
2603
- self.assertIn(
2653
+ with self.assertRaisesMessage(
2654
+ ImproperlyConfigured,
2655
+ f"Invalid setting for WagtailUsersAppConfig.{self.app_config_attr}: "
2604
2656
  'Module "wagtail.users.tests" does not define a "CustomClassDoesNotExist" attribute/class',
2605
- str(exc_info.exception),
2606
- )
2657
+ ):
2658
+ get_viewset_cls(self.app_config, self.app_config_attr)
2659
+
2660
+
2661
+ class TestUserViewSet(TestGroupViewSet):
2662
+ app_config_attr = "user_viewset"
2663
+ default_viewset_cls = UserViewSet
2664
+ custom_viewset_cls = CustomUserViewSet
2665
+ create_form_cls = CustomUserCreationForm
2666
+ edit_form_cls = CustomUserEditForm
2667
+
2668
+ def test_registered_permissions(self):
2669
+ group_ct = ContentType.objects.get_for_model(Group)
2670
+ qs = Permission.objects.none()
2671
+ for fn in hooks.get_hooks("register_permissions"):
2672
+ qs |= fn()
2673
+ registered_user_permissions = qs.filter(content_type=group_ct)
2674
+ self.assertEqual(
2675
+ set(registered_user_permissions.values_list("codename", flat=True)),
2676
+ {"add_group", "change_group", "delete_group"},
2677
+ )
2607
2678
 
2608
2679
 
2609
2680
  class TestAuthorisationIndexView(WagtailTestUtils, TestCase):
@@ -2957,3 +3028,17 @@ class TestTemplateTags(WagtailTestUtils, TestCase):
2957
3028
  top_level_custom_button.text.strip(),
2958
3029
  "Show profile",
2959
3030
  )
3031
+
3032
+
3033
+ class TestAdminPermissions(WagtailTestUtils, TestCase):
3034
+ def test_registered_user_permissions(self):
3035
+ user_ct = ContentType.objects.get_for_model(User)
3036
+ model_name = User._meta.model_name
3037
+ qs = Permission.objects.none()
3038
+ for fn in hooks.get_hooks("register_permissions"):
3039
+ qs |= fn()
3040
+ registered_user_permissions = qs.filter(content_type=user_ct)
3041
+ self.assertEqual(
3042
+ set(registered_user_permissions.values_list("codename", flat=True)),
3043
+ {f"add_{model_name}", f"change_{model_name}", f"delete_{model_name}"},
3044
+ )
@@ -149,6 +149,10 @@ class GroupViewSet(ModelViewSet):
149
149
  model = Group
150
150
  ordering = ["name"]
151
151
  add_to_reference_index = False
152
+ menu_name = "groups"
153
+ menu_label = _("Groups")
154
+ menu_order = 601
155
+ add_to_settings_menu = True
152
156
 
153
157
  index_view_class = IndexView
154
158
  add_view_class = CreateView