1# -*- coding: utf-8 -*-
2from copy import deepcopy
3
4from django.contrib import admin
5from django.contrib.admin import site
6from django.contrib.auth import get_user_model
7from django.contrib.auth.admin import UserAdmin
8from django.contrib.sites.models import Site
9from django.db import OperationalError
10from django.utils.translation import gettext_lazy as _
11
12from cms.admin.forms import GlobalPagePermissionAdminForm, PagePermissionInlineAdminForm, ViewRestrictionInlineAdminForm
13from cms.exceptions import NoPermissionsException
14from cms.models import PagePermission, GlobalPagePermission
15from cms.utils import permissions, page_permissions
16from cms.utils.conf import get_cms_setting
17from cms.utils.helpers import classproperty
18
19
20PERMISSION_ADMIN_INLINES = []
21
22user_model = get_user_model()
23admin_class = UserAdmin
24for model, admin_instance in site._registry.items():
25    if model == user_model:
26        admin_class = admin_instance.__class__
27
28
29class TabularInline(admin.TabularInline):
30    pass
31
32
33class PagePermissionInlineAdmin(TabularInline):
34    model = PagePermission
35    # use special form, so we can override of user and group field
36    form = PagePermissionInlineAdminForm
37    classes = ['collapse', 'collapsed']
38    extra = 0  # edit page load time boost
39    show_with_view_permissions = False
40
41    def has_change_permission(self, request, obj=None):
42        if not obj:
43            return False
44        return page_permissions.user_can_change_page_permissions(
45            request.user,
46            page=obj,
47            site=obj.node.site,
48        )
49
50    def has_add_permission(self, request, obj=None):
51        return self.has_change_permission(request, obj)
52
53    @classproperty
54    def raw_id_fields(cls):
55        # Dynamically set raw_id_fields based on settings
56        threshold = get_cms_setting('RAW_ID_USERS')
57
58        # Given a fresh django-cms install and a django settings with the
59        # CMS_RAW_ID_USERS = CMS_PERMISSION = True
60        # django throws an OperationalError when running
61        # ./manage migrate
62        # because auth_user doesn't exists yet
63        try:
64            threshold = threshold and get_user_model().objects.count() > threshold
65        except OperationalError:
66            threshold = False
67
68        return ['user'] if threshold else []
69
70    def get_queryset(self, request):
71        """
72        Queryset change, so user with global change permissions can see
73        all permissions. Otherwise user can see only permissions for
74        peoples which are under him (he can't see his permissions, because
75        this will lead to violation, when he can add more power to himself)
76        """
77        site = Site.objects.get_current(request)
78
79        try:
80            # can see only permissions for users which are under him in tree
81            qs = self.model.objects.subordinate_to_user(request.user, site)
82        except NoPermissionsException:
83            return self.model.objects.none()
84        return qs.filter(can_view=self.show_with_view_permissions)
85
86    def get_formset(self, request, obj=None, **kwargs):
87        """
88        Some fields may be excluded here. User can change only
89        permissions which are available for him. E.g. if user does not haves
90        can_publish flag, he can't change assign can_publish permissions.
91        """
92        exclude = self.exclude or []
93        if obj:
94            user = request.user
95            if not obj.has_add_permission(user):
96                exclude.append('can_add')
97            if not obj.has_delete_permission(user):
98                exclude.append('can_delete')
99            if not obj.has_publish_permission(user):
100                exclude.append('can_publish')
101            if not obj.has_advanced_settings_permission(user):
102                exclude.append('can_change_advanced_settings')
103            if not obj.has_move_page_permission(user):
104                exclude.append('can_move_page')
105
106        kwargs['exclude'] = exclude
107        formset_cls = super(PagePermissionInlineAdmin, self).get_formset(request, obj=obj, **kwargs)
108        qs = self.get_queryset(request)
109        if obj is not None:
110            qs = qs.filter(page=obj)
111        formset_cls._queryset = qs
112        return formset_cls
113
114
115class ViewRestrictionInlineAdmin(PagePermissionInlineAdmin):
116    extra = 0  # edit page load time boost
117    form = ViewRestrictionInlineAdminForm
118    verbose_name = _("View restriction")
119    verbose_name_plural = _("View restrictions")
120    show_with_view_permissions = True
121
122
123class GlobalPagePermissionAdmin(admin.ModelAdmin):
124    list_display = ['user', 'group', 'can_change', 'can_delete', 'can_publish', 'can_change_permissions']
125    list_filter = ['user', 'group', 'can_change', 'can_delete', 'can_publish', 'can_change_permissions']
126
127    form = GlobalPagePermissionAdminForm
128    search_fields = []
129    for field in admin_class.search_fields:
130        search_fields.append("user__%s" % field)
131    search_fields.append('group__name')
132
133    list_display.append('can_change_advanced_settings')
134    list_filter.append('can_change_advanced_settings')
135
136    def get_list_filter(self, request):
137        threshold = get_cms_setting('RAW_ID_USERS')
138        try:
139            threshold = threshold and get_user_model().objects.count() > threshold
140        except OperationalError:
141            threshold = False
142        filter_copy = deepcopy(self.list_filter)
143        if threshold:
144            filter_copy.remove('user')
145        return filter_copy
146
147    def has_add_permission(self, request):
148        site = Site.objects.get_current(request)
149        return permissions.user_can_add_global_permissions(request.user, site)
150
151    def has_change_permission(self, request, obj=None):
152        site = Site.objects.get_current(request)
153        return permissions.user_can_change_global_permissions(request.user, site)
154
155    def has_delete_permission(self, request, obj=None):
156        site = Site.objects.get_current(request)
157        return permissions.user_can_delete_global_permissions(request.user, site)
158
159    @classproperty
160    def raw_id_fields(cls):
161        # Dynamically set raw_id_fields based on settings
162        threshold = get_cms_setting('RAW_ID_USERS')
163
164        # Given a fresh django-cms install and a django settings with the
165        # CMS_RAW_ID_USERS = CMS_PERMISSION = True
166        # django throws an OperationalError when running
167        # ./manage migrate
168        # because auth_user doesn't exists yet
169        try:
170            threshold = threshold and get_user_model().objects.count() > threshold
171        except OperationalError:
172            threshold = False
173
174        return ['user'] if threshold else []
175
176
177if get_cms_setting('PERMISSION'):
178    admin.site.register(GlobalPagePermission, GlobalPagePermissionAdmin)
179    PERMISSION_ADMIN_INLINES.extend([
180        ViewRestrictionInlineAdmin,
181        PagePermissionInlineAdmin,
182    ])
183