diff --git a/mayan/apps/common/generics.py b/mayan/apps/common/generics.py index 4f4096edc7..d1bfaab802 100644 --- a/mayan/apps/common/generics.py +++ b/mayan/apps/common/generics.py @@ -3,9 +3,8 @@ from __future__ import absolute_import, unicode_literals from django.conf import settings from django.contrib import messages from django.contrib.contenttypes.models import ContentType -from django.core.exceptions import PermissionDenied from django.http import HttpResponseRedirect -from django.utils.translation import ungettext, ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _ from django.views.generic import ( FormView as DjangoFormView, DetailView, TemplateView ) @@ -18,121 +17,38 @@ from django.views.generic.list import ListView from django_downloadview import VirtualDownloadView, VirtualFile from pure_pagination.mixins import PaginationMixin -from acls.models import AccessControlList - from .forms import ChoiceForm from .mixins import * # NOQA from .settings import setting_paginate_by __all__ = ( - 'ActionView', 'AssignRemoveView', 'ConfirmView', 'FormView', - 'MultiFormView', 'SingleObjectCreateView', 'SingleObjectDeleteView', + 'AssignRemoveView', 'ConfirmView', 'FormView', 'MultiFormView', + 'MultipleObjectConfirmActionView', 'MultipleObjectFormActionView', + 'SingleObjectCreateView', 'SingleObjectDeleteView', 'SingleObjectDetailView', 'SingleObjectEditView', 'SingleObjectListView', - 'SimpleView', + 'SimpleView' ) -class ActionView(ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView): +class MultipleObjectFormActionView(ObjectActionMixin, MultipleObjectMixin, FormExtraKwargsMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView): """ This view will present a form and upon receiving a POST request will perform an action on an object or queryset """ - queryset = None - model = None - pk_url_kwarg = 'pk' - object_permission = None - slug_url_kwarg = 'slug' - success_message = 'Operation performed on %(count)d object' - success_message_plural = 'Operation performed on %(count)d objects' template_name = 'appearance/generic_form.html' def form_valid(self, form): self.view_action(form=form) - return super(ActionView, self).form_valid(form=form) + return super(MultipleObjectFormActionView, self).form_valid(form=form) - def get_form_extra_kwargs(self): - return {} - def get_form_kwargs(self): - result = super(ActionView, self).get_form_kwargs() - result.update(self.get_form_extra_kwargs()) - return result +class MultipleObjectConfirmActionView(ObjectActionMixin, MultipleObjectMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, TemplateView): + template_name = 'appearance/generic_confirm.html' - def get_pk_list(self): - result = self.request.GET.get( - 'id_list', self.request.POST.get('id_list') - ) - - if result: - return result.split(',') - else: - return None - - def get_queryset(self): - if self.queryset is not None: - queryset = self.queryset - if isinstance(queryset, QuerySet): - queryset = queryset.all() - elif self.model is not None: - queryset = self.model._default_manager.all() - - pk = self.kwargs.get(self.pk_url_kwarg) - slug = self.kwargs.get(self.slug_url_kwarg) - pk_list = self.get_pk_list() - - if pk is not None: - queryset = queryset.filter(pk=pk) - - # Next, try looking up by slug. - if slug is not None and (pk is None or self.query_pk_and_slug): - slug_field = self.get_slug_field() - queryset = queryset.filter(**{slug_field: slug}) - - if pk_list is not None: - queryset = queryset.filter(pk__in=self.get_pk_list()) - - if pk is None and slug is None and pk_list is None: - raise AttributeError( - 'Generic detail view %s must be called with ' - 'either an object pk, a slug or an id list.' - % self.__class__.__name__ - ) - - if self.object_permission: - return AccessControlList.objects.filter_by_access( - self.object_permission, self.request.user, queryset=queryset - ) - else: - return queryset - - def get_success_message(self, count): - return ungettext( - self.success_message, - self.success_message_plural, - count - ) % { - 'count': count, - } - - def object_action(self, form, instance): - pass - - def view_action(self, form): - count = 0 - - for instance in self.get_queryset(): - try: - self.object_action(form=form, instance=instance) - except PermissionDenied: - pass - else: - count += 1 - - messages.success( - self.request, - self.get_success_message(count=count) - ) + def post(self, request, *args, **kwargs): + self.view_action() + return HttpResponseRedirect(self.get_success_url()) class AssignRemoveView(ExtraContextMixin, ViewPermissionCheckMixin, ObjectPermissionCheckMixin, TemplateView): diff --git a/mayan/apps/common/mixins.py b/mayan/apps/common/mixins.py index 97768c6743..484af98313 100644 --- a/mayan/apps/common/mixins.py +++ b/mayan/apps/common/mixins.py @@ -1,17 +1,21 @@ from __future__ import unicode_literals -from django.apps import apps from django.conf import settings from django.contrib import messages from django.core.exceptions import PermissionDenied from django.core.urlresolvers import reverse +from django.db.models.query import QuerySet from django.http import HttpResponseRedirect from django.utils.translation import ungettext, ugettext_lazy as _ from permissions import Permission +from acls.models import AccessControlList + + __all__ = ( - 'DeleteExtraDataMixin', 'ExtraContextMixin', + 'DeleteExtraDataMixin', 'ExtraContextMixin', 'FormExtraKwargsMixin', + 'MultipleObjectMixin', 'ObjectActionMixin', 'ObjectListPermissionFilterMixin', 'ObjectNameMixin', 'ObjectPermissionCheckMixin', 'RedirectionMixin', 'ViewPermissionCheckMixin' @@ -30,7 +34,27 @@ class DeleteExtraDataMixin(object): return HttpResponseRedirect(success_url) +class FormExtraKwargsMixin(object): + """ + Mixin that allows a view to pass extra keyword arguments to forms + """ + + form_extra_kwargs = {} + + def get_form_extra_kwargs(self): + return self.form_extra_kwargs + + def get_form_kwargs(self): + result = super(FormExtraKwargsMixin, self).get_form_kwargs() + result.update(self.get_form_extra_kwargs()) + return result + + class ExtraContextMixin(object): + """ + Mixin that allows views to pass extra context to the template + """ + extra_context = {} def get_extra_context(self): @@ -44,8 +68,8 @@ class ExtraContextMixin(object): class MultipleInstanceActionMixin(object): model = None - success_message = 'Operation performed on %(count)d object' - success_message_plural = 'Operation performed on %(count)d objects' + success_message = _('Operation performed on %(count)d object') + success_message_plural = _('Operation performed on %(count)d objects') def get_pk_list(self): return self.request.GET.get( @@ -82,14 +106,108 @@ class MultipleInstanceActionMixin(object): return HttpResponseRedirect(self.get_success_url()) +class MultipleObjectMixin(object): + """ + Mixin that allows a view to work on a single or multiple objects + """ + + model = None + object_permission = None + pk_list_key = 'id_list' + pk_list_separator = ',' + pk_url_kwarg = 'pk' + queryset = None + slug_url_kwarg = 'slug' + + def get_pk_list(self): + result = self.request.GET.get( + self.pk_list_key, self.request.POST.get(self.pk_list_key) + ) + + if result: + return result.split(self.pk_list_separator) + else: + return None + + def get_queryset(self): + if self.queryset is not None: + queryset = self.queryset + if isinstance(queryset, QuerySet): + queryset = queryset.all() + elif self.model is not None: + queryset = self.model._default_manager.all() + + pk = self.kwargs.get(self.pk_url_kwarg) + slug = self.kwargs.get(self.slug_url_kwarg) + pk_list = self.get_pk_list() + + if pk is not None: + queryset = queryset.filter(pk=pk) + + # Next, try looking up by slug. + if slug is not None and (pk is None or self.query_pk_and_slug): + slug_field = self.get_slug_field() + queryset = queryset.filter(**{slug_field: slug}) + + if pk_list is not None: + queryset = queryset.filter(pk__in=self.get_pk_list()) + + if pk is None and slug is None and pk_list is None: + raise AttributeError( + 'Generic detail view %s must be called with ' + 'either an object pk, a slug or an id list.' + % self.__class__.__name__ + ) + + if self.object_permission: + return AccessControlList.objects.filter_by_access( + self.object_permission, self.request.user, queryset=queryset + ) + else: + return queryset + + +class ObjectActionMixin(object): + """ + Mixin that performs an user action to a queryset + """ + + success_message = 'Operation performed on %(count)d object' + success_message_plural = 'Operation performed on %(count)d objects' + + def get_success_message(self, count): + return ungettext( + self.success_message, + self.success_message_plural, + count + ) % { + 'count': count, + } + + def object_action(self, instance, form=None): + pass + + def view_action(self, form=None): + count = 0 + + for instance in self.get_queryset(): + try: + self.object_action(form=form, instance=instance) + except PermissionDenied: + pass + else: + count += 1 + + messages.success( + self.request, + self.get_success_message(count=count) + ) + + class ObjectListPermissionFilterMixin(object): object_permission = None def get_queryset(self): - AccessControlList = apps.get_model( - app_label='acls', model_name='AccessControlList' - ) - queryset = super(ObjectListPermissionFilterMixin, self).get_queryset() if self.object_permission: @@ -123,10 +241,6 @@ class ObjectPermissionCheckMixin(object): return self.get_object() def dispatch(self, request, *args, **kwargs): - AccessControlList = apps.get_model( - app_label='acls', model_name='AccessControlList' - ) - if self.object_permission: AccessControlList.objects.check_access( permissions=self.object_permission, user=request.user,