diff --git a/CHANGELOG.rst b/CHANGELOG.rst index a2834231819..932170cfca7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -17,6 +17,7 @@ Features: * Python 3.11, 3.10 support * Remove patching of PageContent by djangocms-versioning (#7446) * Utility function get_placeholder_from_slot for PlaceholderRelationField (#7479) +* Use autocomplete for entities from model `User` and `Group` instead of the raw primary key. Bug Fixes: ---------- diff --git a/cms/admin/permissionadmin.py b/cms/admin/permissionadmin.py index 409ae821efd..989feeff82f 100644 --- a/cms/admin/permissionadmin.py +++ b/cms/admin/permissionadmin.py @@ -1,44 +1,26 @@ -from copy import deepcopy - from django.contrib import admin -from django.contrib.admin import site from django.contrib.auth import get_user_model +from django.contrib.auth.models import Group from django.contrib.auth.admin import UserAdmin from django.contrib.sites.models import Site -from django.db import OperationalError from django.utils.translation import gettext_lazy as _ -from cms.admin.forms import ( - GlobalPagePermissionAdminForm, - PagePermissionInlineAdminForm, - ViewRestrictionInlineAdminForm, -) from cms.exceptions import NoPermissionsException from cms.models import GlobalPagePermission, PagePermission from cms.utils import page_permissions, permissions from cms.utils.conf import get_cms_setting -from cms.utils.helpers import classproperty +from cms.utils.permissions import get_subordinate_users, get_subordinate_groups PERMISSION_ADMIN_INLINES = [] user_model = get_user_model() admin_class = UserAdmin -for model, admin_instance in site._registry.items(): +for model, admin_instance in admin.site._registry.items(): if model == user_model: admin_class = admin_instance.__class__ -class TabularInline(admin.TabularInline): - pass - - -class PagePermissionInlineAdmin(TabularInline): - model = PagePermission - # use special form, so we can override of user and group field - form = PagePermissionInlineAdminForm - extra = 0 # edit page load time boost - show_with_view_permissions = False - +class PagePermissionAdminMixin: def has_change_permission(self, request, obj=None): if not obj: return False @@ -51,27 +33,10 @@ def has_change_permission(self, request, obj=None): def has_add_permission(self, request, obj=None): return self.has_change_permission(request, obj) - @classproperty - def raw_id_fields(cls): - # Dynamically set raw_id_fields based on settings - threshold = get_cms_setting('RAW_ID_USERS') - - # Given a fresh django-cms install and a django settings with the - # CMS_RAW_ID_USERS = CMS_PERMISSION = True - # django throws an OperationalError when running - # ./manage migrate - # because auth_user doesn't exists yet - try: - threshold = threshold and get_user_model().objects.count() > threshold - except OperationalError: - threshold = False - - return ['user'] if threshold else [] - def get_queryset(self, request): """ Queryset change, so user with global change permissions can see - all permissions. Otherwise user can see only permissions for + all permissions. Otherwise, a user can see only permissions for peoples which are under him (he can't see his permissions, because this will lead to violation, when he can add more power to himself) """ @@ -79,10 +44,34 @@ def get_queryset(self, request): try: # can see only permissions for users which are under him in tree - qs = self.model.objects.subordinate_to_user(request.user, site) + queryset = self.model.objects.subordinate_to_user(request.user, site) except NoPermissionsException: - return self.model.objects.none() - return qs.filter(can_view=self.show_with_view_permissions) + queryset = self.model.objects.none() + return queryset + + def formfield_for_dbfield(self, db_field, request, **kwargs): + site = Site.objects.get_current(request) + formfield = super().formfield_for_dbfield(db_field, request, **kwargs) + if db_field.name == 'user': + formfield._queryset = get_subordinate_users(request.user, site) + if db_field.name == 'group': + formfield._queryset = get_subordinate_groups(request.user, site) + return formfield + + +class PagePermissionInlineAdmin(PagePermissionAdminMixin, admin.TabularInline): + model = PagePermission + # use special form, so we can override of user and group field + # form = PagePermissionInlineAdminForm + classes = ['collapse', 'collapsed'] + fields = ['user', 'group', 'can_add', 'can_change', 'can_delete', 'can_change_advanced_settings', + 'can_change_permissions', 'can_move_page', 'grant_on', + ] + extra = 0 # edit page load time boost + autocomplete_fields = ['user', 'group'] + + def get_queryset(self, request): + return super().get_queryset(request).filter(can_view=False) def get_formset(self, request, obj=None, **kwargs): """ @@ -106,26 +95,36 @@ def get_formset(self, request, obj=None, **kwargs): kwargs['exclude'] = exclude formset_cls = super().get_formset(request, obj=obj, **kwargs) - qs = self.get_queryset(request) - if obj is not None: - qs = qs.filter(page=obj) - formset_cls._queryset = qs + queryset = self.get_queryset(request) + if obj: + queryset = queryset.filter(page=obj) + formset_cls._queryset = queryset return formset_cls -class ViewRestrictionInlineAdmin(PagePermissionInlineAdmin): +class ViewRestrictionInlineAdmin(PagePermissionAdminMixin, admin.TabularInline): + model = PagePermission extra = 0 # edit page load time boost - form = ViewRestrictionInlineAdminForm verbose_name = _("View restriction") verbose_name_plural = _("View restrictions") - show_with_view_permissions = True + fields = ['user', 'group', 'grant_on', 'can_view'] + autocomplete_fields = ['user', 'group'] + + def formfield_for_dbfield(self, db_field, request, **kwargs): + formfield = super().formfield_for_dbfield(db_field, request, **kwargs) + if db_field.name == 'can_view': + formfield.widget = formfield.hidden_widget() + formfield.initial = True + return formfield + + def get_queryset(self, request): + return super().get_queryset(request).filter(can_view=True) class GlobalPagePermissionAdmin(admin.ModelAdmin): list_display = ['user', 'group', 'can_change', 'can_delete', 'can_publish', 'can_change_permissions'] list_filter = ['user', 'group', 'can_change', 'can_delete', 'can_publish', 'can_change_permissions'] - form = GlobalPagePermissionAdminForm search_fields = [] for field in admin_class.search_fields: search_fields.append("user__%s" % field) @@ -135,15 +134,13 @@ class GlobalPagePermissionAdmin(admin.ModelAdmin): list_filter.append('can_change_advanced_settings') def get_list_filter(self, request): - threshold = get_cms_setting('RAW_ID_USERS') - try: - threshold = threshold and get_user_model().objects.count() > threshold - except OperationalError: - threshold = False - filter_copy = deepcopy(self.list_filter) - if threshold: - filter_copy.remove('user') - return filter_copy + list_filter = list(super().get_list_filter(request)) + users_groups_threshold = get_cms_setting('USERS_GROUPS_THRESHOLD') + if Group.objects.count() <= users_groups_threshold: + list_filter.insert(0, 'group') + if get_user_model().objects.count() <= users_groups_threshold: + list_filter.insert(0, 'user') + return list_filter def has_add_permission(self, request): site = Site.objects.get_current(request) @@ -157,23 +154,6 @@ def has_delete_permission(self, request, obj=None): site = Site.objects.get_current(request) return permissions.user_can_delete_global_permissions(request.user, site) - @classproperty - def raw_id_fields(cls): - # Dynamically set raw_id_fields based on settings - threshold = get_cms_setting('RAW_ID_USERS') - - # Given a fresh django-cms install and a django settings with the - # CMS_RAW_ID_USERS = CMS_PERMISSION = True - # django throws an OperationalError when running - # ./manage migrate - # because auth_user doesn't exists yet - try: - threshold = threshold and get_user_model().objects.count() > threshold - except OperationalError: - threshold = False - - return ['user'] if threshold else [] - if get_cms_setting('PERMISSION'): admin.site.register(GlobalPagePermission, GlobalPagePermissionAdmin)