Artur Barseghyan avatar Artur Barseghyan committed 690407a Draft

0.3; protecting ModelaAmin fields

Comments (0)

Files changed (18)

+Release history
+=====================================
+2013-09-15
+-------------------------------------
+0.3
+- Added ability to protect ModelAdmin fields from users without appropriate permissions.
+
+2013-09-10
+-------------------------------------
+0.2
+- Python 2.6.8 and 3.3 support addeded.
+
+2013-06-17
+-------------------------------------
+0.1
+- Initial.
 >>>         verbose_name = "News item"
 >>>         verbose_name_plural = "News items"
 
-Or if you want to define custom permissions for your model as well, do extend the werewolf permissions as
-follows:
+Or if you want to define custom permissions for your model as well, do extend the django-werewolf
+permissions as follows:
 
 >>> from werewolf.models import WerewolfBaseModel
 >>> from werewolf.utils import extend_werewolf_permissions
 >>> from news.models import NewsItem
 >>>
 >>> class NewsItemAdmin(WerewolfBaseAdmin):
->>>     # Your code comes here
+>>>     werewolf_protected_fields = (
+>>>         ('author', 'can_change_author'),
+>>>         ('editor', 'can_change_editor'),
+>>>         ('chief_editor', 'can_change_chief_editor')
+>>>     )
 >>>
 >>> admin.site.register(NewsItem, NewsItemAdmin)
 
+The ``werewolf_protected_fields`` property is a list of fields that are supposed to be protected. Each item in
+the list is a tuple of (``field_name_to_protect``, ``required_permission``). If given, django-werewolf hides
+fields listed as protected from users that do not have the permission required. In order to do so, django-werewolf
+overrides the Django's ModelAdmin ``get_field`` and ``get_fieldsets`` methods. If you happen to override that
+method for your own needs, make sure the it also reflects the django-werewolf concepts.
+
 NOTE: If you override the ``queryset`` method of your model's admin class, make sure to see the source code
 of `werewolf.admin.WerewolfBaseAdmin.queryset` and copy the approach from there. Otherwise, your users with
 no permission to change the `published` status will be able to chgange the status of already published items
 # built documents.
 #
 # The short X.Y version.
-version = '0.2'
+version = '0.3'
 # The full version, including alpha/beta/rc tags.
-release = '0.2'
+release = '0.3'
 
 # The language for content autogenerated by Sphinx. Refer to documentation
 # for a list of supported languages.
 >>>         verbose_name = "News item"
 >>>         verbose_name_plural = "News items"
 
-Or if you want to define custom permissions for your model as well, do extend the werewolf permissions as
-follows:
+Or if you want to define custom permissions for your model as well, do extend the django-werewolf
+permissions as follows:
 
 >>> from werewolf.models import WerewolfBaseModel
 >>> from werewolf.utils import extend_werewolf_permissions
 >>> from news.models import NewsItem
 >>>
 >>> class NewsItemAdmin(WerewolfBaseAdmin):
->>>     # Your code comes here
+>>>     werewolf_protected_fields = (
+>>>         ('author', 'can_change_author'),
+>>>         ('editor', 'can_change_editor'),
+>>>         ('chief_editor', 'can_change_chief_editor')
+>>>     )
 >>>
 >>> admin.site.register(NewsItem, NewsItemAdmin)
 
+The ``werewolf_protected_fields`` property is a list of fields that are supposed to be protected. Each item in
+the list is a tuple of (``field_name_to_protect``, ``required_permission``). If given, django-werewolf hides
+fields listed as protected from users that do not have the permission required. In order to do so, django-werewolf
+overrides the Django's ModelAdmin ``get_field`` and ``get_fieldsets`` methods. If you happen to override that
+method for your own needs, make sure the it also reflects the django-werewolf concepts.
+
 NOTE: If you override the ``queryset`` method of your model's admin class, make sure to see the source code
 of `werewolf.admin.WerewolfBaseAdmin.queryset` and copy the approach from there. Otherwise, your users with
 no permission to change the `published` status will be able to chgange the status of already published items

example/example/news/admin.py

-from functools import partial
-
 from django.contrib import admin
 from django.utils.translation import ugettext_lazy as _
-from django.forms.models import modelform_factory
-from django.contrib.admin.util import flatten_fieldsets
 
 from werewolf.admin import WerewolfBaseAdmin
 
     readonly_fields = ('date_created', 'date_updated')
     prepopulated_fields = {'slug': ('title',)}
 
+    # Hiding fields that non-authorised users should not have access to.
+    werewolf_protected_fields = (
+        ('author', PERMISSION_CAN_CHANGE_AUTHOR),
+        ('editor', PERMISSION_CAN_CHANGE_EDITOR),
+        ('chief_editor', PERMISSION_CAN_CHANGE_CHIEF_EDITOR)
+    )
+
     fieldsets = (
         (None, {
             'fields': ('title', 'slug', 'teaser', 'body', 'image', 'urgency')
         queryset = queryset.select_related('author', 'editor')
         return queryset
 
-    def get_form(self, request, obj=None, **kwargs):
-        # Hiding fields that non-authorised users should not have access to.
-        if self.declared_fieldsets:
-            fields = flatten_fieldsets(self.declared_fieldsets)
-        else:
-            fields = None
-        if self.exclude is None:
-            exclude = []
-        else:
-            exclude = list(self.exclude)
-        exclude.extend(self.get_readonly_fields(request, obj))
-        if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
-            # Take the custom ModelForm's Meta.exclude into account only if the
-            # ModelAdmin doesn't define its own.
-            exclude.extend(self.form._meta.exclude)
-        # if exclude is an empty list we pass None to be consistant with the
-        # default on modelform_factory
-        exclude = exclude or []
-
-        if not request.user.has_perm('%s.%s' % (NewsItem._meta.app_label, PERMISSION_CAN_CHANGE_AUTHOR)):
-            exclude.append('author')
-        if not request.user.has_perm('%s.%s' % (NewsItem._meta.app_label, PERMISSION_CAN_CHANGE_EDITOR)):
-            exclude.append('editor')
-        if not request.user.has_perm('%s.%s' % (NewsItem._meta.app_label, PERMISSION_CAN_CHANGE_CHIEF_EDITOR)):
-            exclude.append('chief_editor')
-
-        exclude = exclude or None
-
-        defaults = {
-            "form": self.form,
-            "fields": fields,
-            "exclude": exclude,
-            "formfield_callback": partial(self.formfield_for_dbfield, request=request),
-        }
-        defaults.update(kwargs)
-        return modelform_factory(self.model, **defaults)
-
-    def get_fieldsets(self, request, obj=None):
-        # Hiding fields that non-authorised users should not have access to.
-        fieldsets = super(NewsItemAdmin, self).get_fieldsets(request, obj=None)
-
-        cleaned_fieldsets = []
-
-        for fieldset_label, fieldset in fieldsets:
-            fields = list(fieldset['fields'])
-
-            # Cleaning author
-            if not request.user.has_perm('%s.%s' % (NewsItem._meta.app_label, PERMISSION_CAN_CHANGE_AUTHOR)):
-                try:
-                    fields.remove('author')
-                except:
-                    pass
-
-            # Cleaning editor
-            if not request.user.has_perm('%s.%s' % (NewsItem._meta.app_label, PERMISSION_CAN_CHANGE_EDITOR)):
-                try:
-                    fields.remove('editor')
-                except:
-                    pass
-
-            # Cleaning chief editor
-            if not request.user.has_perm('%s.%s' % (NewsItem._meta.app_label, PERMISSION_CAN_CHANGE_CHIEF_EDITOR)):
-                try:
-                    fields.remove('chief_editor')
-                except:
-                    pass
-
-            cleaned_fieldsets.append((fieldset_label, {'fields': fields}))
-
-        return cleaned_fieldsets
-
 admin.site.register(NewsItem, NewsItemAdmin)
+pip install -r example/requirements.txt
+pip uninstall django-werewolf -y
 python setup.py install
-pip install -r example/requirements.txt
 python example/example/manage.py collectstatic --noinput --traceback -v 3
 python example/example/manage.py syncdb --noinput --traceback -v 3
 #python example/example/manage.py migrate --noinput --traceback -v 3
 except:
     readme = ''
 
-version = '0.2'
+version = '0.3'
 
 setup(
     name = 'django-werewolf',

src/werewolf/__init__.py

 __title__ = 'werewolf.views'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('autodiscover',)
 

src/werewolf/admin.py

 __title__ = 'werewolf.admin'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('WerewolfBaseAdmin',)
 
+from functools import partial
+
 from django.contrib import admin
 from django import forms
+from django.forms.models import modelform_factory
+from django.contrib.admin.util import flatten_fieldsets
 
 from werewolf.utils import status_choices_for_user
 from werewolf.triggers import registry
 class WerewolfBaseAdmin(AdminParentClass):
     """
     Base werewolf admin model.
+
+    :property list werewolf_protected_fields: List of fields to protect in form of the following
+        tuple (``field_name``, ``required_permission``).
     """
+    werewolf_protected_fields = []
+
     def formfield_for_dbfield(self, db_field, **kwargs):
         """
         Here we replace the choices based on the user permissions.
         super(WerewolfBaseAdmin, self).save_model(request, obj, form, change)
 
         self.status_change_trigger(request, obj, form, change)
+
+    def get_form(self, request, obj=None, **kwargs):
+        """
+        Hiding fields that non-authorised users should not have access to. It's done based on the
+        ``werewolf_protected_fields`` of your ``ModelAdmin``. But if happen to override that method
+        for your own needs, make sure the it also reflects the django-werewolf concepts.
+        """
+        if not self.werewolf_protected_fields:
+            return super(WerewolfBaseAdmin, self).get_form(request=request, obj=obj, **kwargs)
+
+        # Hiding fields that non-authorised users should not have access to.
+        if self.declared_fieldsets:
+            fields = flatten_fieldsets(self.declared_fieldsets)
+        else:
+            fields = None
+        if self.exclude is None:
+            exclude = []
+        else:
+            exclude = list(self.exclude)
+        exclude.extend(self.get_readonly_fields(request, obj))
+        if self.exclude is None and hasattr(self.form, '_meta') and self.form._meta.exclude:
+            # Take the custom ModelForm's Meta.exclude into account only if the
+            # ModelAdmin doesn't define its own.
+            exclude.extend(self.form._meta.exclude)
+        # if exclude is an empty list we pass None to be consistant with the
+        # default on modelform_factory
+        exclude = exclude or []
+
+        for field_name, required_permission in self.werewolf_protected_fields:
+            if not request.user.has_perm('{0}.{1}'.format(self.model._meta.app_label, required_permission)):
+                exclude.append(field_name)
+
+        exclude = exclude or None
+
+        defaults = {
+            "form": self.form,
+            "fields": fields,
+            "exclude": exclude,
+            "formfield_callback": partial(self.formfield_for_dbfield, request=request),
+        }
+        defaults.update(kwargs)
+        return modelform_factory(self.model, **defaults)
+
+    def get_fieldsets(self, request, obj=None):
+        """
+        Hiding fields that non-authorised users should not have access to. It's done based on the
+        ``werewolf_protected_fields`` of your ``ModelAdmin``. But if happen to override that method
+        for your own needs, make sure the it also reflects the django-werewolf concepts.
+        """
+        fieldsets = super(WerewolfBaseAdmin, self).get_fieldsets(request, obj=None)
+        
+        if not self.werewolf_protected_fields:
+            return fieldsets
+
+        cleaned_fieldsets = []
+
+        for fieldset_label, fieldset in fieldsets:
+            fields = list(fieldset['fields'])
+
+            for field_name, required_permission in self.werewolf_protected_fields:
+                # Cleaning the field that has been already excluded from form (in ``get_form``).
+                if not request.user.has_perm('{0}.{1}'.format(self.model._meta.app_label, required_permission)):
+                    try:
+                        fields.remove(field_name)
+                    except:
+                        pass
+
+            cleaned_fieldsets.append((fieldset_label, {'fields': fields}))
+
+        return cleaned_fieldsets

src/werewolf/conf.py

 __title__ = 'werewolf.conf'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('get_setting',)
 

src/werewolf/constants.py

 __title__ = 'werewolf.constants'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('CHANGE_STATUS_TO', 'CAN_VIEW_STATUS')
 

src/werewolf/defaults.py

 __title__ = 'werewolf.defaults'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('STATUS_CHOICES', 'STATUS_PUBLISHED', 'DEFAULT_STATUS', 'STATUS_MAX_LENGTH', 'USE_DJANGO_REVERSION')
 

src/werewolf/helpers.py

 __title__ = 'werewolf.helpers'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('admin_edit_url', 'admin_edit_url_for_object')
 

src/werewolf/models/__init__.py

 __title__ = 'werewolf.models.__init__'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('WerewolfBaseMeta', 'WerewolfBaseModel')
 

src/werewolf/models/managers.py

 __title__ = 'werewolf.models.managers'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('WerewolfBaseManager',)
 

src/werewolf/settings.py

 __title__ = 'werewolf.settings'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('STATUS_CHOICES', 'STATUS_PUBLISHED', 'DEFAULT_STATUS', 'STATUS_MAX_LENGTH', 'USE_DJANGO_REVERSION')
 

src/werewolf/triggers.py

 __title__ = 'werewolf.triggers'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('WerewolfBaseTrigger', 'registry')
 

src/werewolf/utils.py

 __title__ = 'werewolf.utils'
-__version__ = '0.2'
-__build__ = 0x000002
+__version__ = '0.3'
+__build__ = 0x000003
 __author__ = 'Artur Barseghyan <artur.barseghyan@gmail.com>'
 __all__ = ('permission_key', 'permissions_for_base_model', 'status_choices_for_user', \
            'extend_werewolf_permissions')
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.