浏览代码

Allow displaying permissions linked to the Admin model's content type (#11668)

Sage Abdullah 1 年之前
父节点
当前提交
bded2cb98b

+ 1 - 0
CHANGELOG.txt

@@ -11,6 +11,7 @@ Changelog
  * Add the ability to disable the usage of a shared password for enhanced security for the private pages and collections (documents) feature (Salvo Polizzi, Jake Howard)
  * Add system checks to ensure that `WAGTAIL_DATE_FORMAT`, `WAGTAIL_DATETIME_FORMAT`, `WAGTAIL_TIME_FORMAT` are correctly configured (Rohit Sharma, Coen van der Kamp)
  * Allow custom permissions with the same prefix as built-in permissions (Sage Abdullah)
+ * Allow displaying permissions linked to the Admin model's content type (Sage Abdullah)
  * Fix: Fix typo in `__str__` for MySQL search index (Jake Howard)
  * Fix: Ensure that unit tests correctly check for migrations in all core Wagtail apps (Matt Westcott)
  * Fix: Correctly handle `date` objects on `human_readable_date` template tag (Jhonatan Lopes)

+ 1 - 0
docs/releases/6.1.md

@@ -20,6 +20,7 @@ depth: 1
  * Add the ability to disable the usage of a shared password for enhanced security for the [private pages](private_pages) and [collections (documents)](private_collections) feature (Salvo Polizzi, Jake Howard)
  * Add system checks to ensure that `WAGTAIL_DATE_FORMAT`, `WAGTAIL_DATETIME_FORMAT`, `WAGTAIL_TIME_FORMAT` are [correctly configured](wagtail_date_time_formats) (Rohit Sharma, Coen van der Kamp)
  * Allow custom permissions with the same prefix as built-in permissions (Sage Abdullah)
+ * Allow displaying permissions linked to the Admin model's content type (Sage Abdullah)
 
 
 ### Bug fixes

+ 22 - 2
docs/topics/permissions.md

@@ -67,9 +67,29 @@ See Django's documentation on [custom permissions](https://docs.djangoproject.co
 The ability to have custom permissions with codenames starting with `add_`, `change_`, or `delete_` was added.
 ```
 
-## Displaying custom permissions in the admin
+Permissions for models registered with Wagtail will automatically show up in the Wagtail admin Group edit form. For other models, you can also add the permissions using the `register_permissions` hook (see [](register_permissions)).
 
-Most permissions will automatically show up in the Wagtail admin Group edit form, however, you can also add them using the `register_permissions` hook (see [](register_permissions)).
+To add a custom permission to be used in the Wagtail admin without relating to a specific model, you can create it using the content type of the `wagtail.admin.models.Admin` model. For example:
+
+```python
+from django.contrib.auth.models import Permission
+from django.contrib.contenttypes.models import ContentType
+from wagtail.admin.models import Admin
+
+
+content_type = ContentType.objects.get_for_model(Admin)
+permission = Permission.objects.create(
+    content_type=content_type,
+    codename="can_do_something",
+    name="Can do something",
+)
+```
+
+After registering the permission using the `register_permissions` hook, it will be displayed in the Wagtail admin Group edit form under the 'Other permissions' section, alongside the 'Can access Wagtail admin' permission.
+
+```{versionadded} 6.1
+The ability to register custom permissions in the "Other permissions" section was added.
+```
 
 ## `FieldPanel` and `PanelGroup` permissions
 

+ 19 - 8
wagtail/users/templatetags/wagtailusers_tags.py

@@ -4,9 +4,11 @@ from collections import defaultdict
 from django import template
 from django.contrib.auth import get_permission_codename
 from django.contrib.auth.models import Permission
+from django.contrib.contenttypes.models import ContentType
 from django.utils.text import camel_case_to_spaces
 
 from wagtail import hooks
+from wagtail.admin.models import Admin
 from wagtail.users.permission_order import CONTENT_TYPE_ORDER
 
 register = template.Library()
@@ -97,9 +99,6 @@ def format_permissions(permission_bound_field):
         for checkbox in permission_bound_field
     }
 
-    object_perms = []
-    other_perms = []
-
     # Permissions that are known by Wagtail, to be shown under their own columns.
     # Other permissions will be shown under the "custom permissions" column.
     main_permission_names = ["add", "change", "delete", "publish", "lock", "unlock"]
@@ -118,17 +117,29 @@ def format_permissions(permission_bound_field):
     for permission in permissions:
         content_perms_by_ct_id[permission.content_type_id].append(permission)
 
+    # Permissions that use Wagtail's Admin content type, to be displayed
+    # under the "Other permissions" section alongside the
+    # "Can access Wagtail admin" permission.
+    admin_content_type = ContentType.objects.get_for_model(Admin)
+    admin_permissions = content_perms_by_ct_id.pop(admin_content_type.id, [])
+    other_perms = [(perm, checkboxes_by_id[perm.id]) for perm in admin_permissions]
+
+    # We're done with the admin content type, so remove it from the list of content types
+    # but make sure the sorted order is preserved.
+    content_type_ids = [
+        ct_id for ct_id in content_type_ids if ct_id != admin_content_type.pk
+    ]
+
+    # Permissions for all other content types, to be displayed under the
+    # "Object permissions" section.
+    object_perms = []
+
     # Iterate using the sorted content_type_ids
     for ct_id in content_type_ids:
         content_perms = content_perms_by_ct_id[ct_id]
         content_perms_dict = {}
         custom_perms = []
 
-        if content_perms[0].content_type.name == "admin":
-            perm = content_perms[0]
-            other_perms.append((perm, checkboxes_by_id[perm.id]))
-            continue
-
         for perm in content_perms:
             content_perms_dict["object"] = perm.content_type.name
             checkbox = checkboxes_by_id[perm.id]

+ 41 - 0
wagtail/users/tests/test_admin_views.py

@@ -5,6 +5,7 @@ from django.apps import apps
 from django.conf import settings
 from django.contrib.auth import get_user_model
 from django.contrib.auth.models import Group, Permission
+from django.contrib.contenttypes.models import ContentType
 from django.core.exceptions import ImproperlyConfigured
 from django.core.files.uploadedfile import SimpleUploadedFile
 from django.db.models import Q
@@ -14,6 +15,7 @@ from django.urls import reverse
 
 from wagtail import hooks
 from wagtail.admin.admin_url_finder import AdminURLFinder
+from wagtail.admin.models import Admin
 from wagtail.compat import AUTH_USER_APP_LABEL, AUTH_USER_MODEL_NAME
 from wagtail.models import (
     Collection,
@@ -1637,6 +1639,45 @@ class TestGroupCreateView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
             )
         )
 
+    def test_custom_other_permissions_with_wagtail_admin_content_type(self):
+        """
+        https://github.com/wagtail/wagtail/issues/8086
+        Allow custom permissions using Wagtail's Admin content type to be
+        displayed in the "Other permissions" section.
+        """
+        admin_ct = ContentType.objects.get_for_model(Admin)
+        custom_permission = Permission.objects.create(
+            codename="roadmap_sync",
+            name="Can sync roadmap items from GitHub",
+            content_type=admin_ct,
+        )
+
+        with self.register_hook(
+            "register_permissions",
+            lambda: Permission.objects.filter(
+                codename="roadmap_sync", content_type=admin_ct
+            ),
+        ):
+            response = self.get()
+
+        soup = self.get_soup(response.content)
+
+        other_permissions = soup.select_one("#other-permissions-section")
+        self.assertIsNotNone(other_permissions)
+
+        custom_checkbox = other_permissions.select_one(
+            f'input[value="{custom_permission.pk}"]'
+        )
+        self.assertIsNotNone(custom_checkbox)
+
+        custom_label = other_permissions.select_one(
+            f'label[for="{custom_checkbox.attrs.get("id")}"]'
+        )
+        self.assertIsNotNone(custom_label)
+        self.assertEqual(
+            custom_label.get_text(strip=True), "Can sync roadmap items from GitHub"
+        )
+
 
 class TestGroupEditView(AdminTemplateTestUtils, WagtailTestUtils, TestCase):
     def setUp(self):