Files
mayan-edms/mayan/apps/common/generics.py

526 lines
17 KiB
Python

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.views.generic import (
FormView as DjangoFormView, DetailView, TemplateView
)
from django.views.generic.detail import SingleObjectMixin
from django.views.generic.edit import (
CreateView, DeleteView, FormMixin, ModelFormMixin, UpdateView
)
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',
'SingleObjectDetailView', 'SingleObjectEditView', 'SingleObjectListView',
'SimpleView',
)
class ActionView(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)
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
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)
)
class AssignRemoveView(ExtraContextMixin, ViewPermissionCheckMixin, ObjectPermissionCheckMixin, TemplateView):
decode_content_type = False
right_list_help_text = None
left_list_help_text = None
grouped = False
left_list_title = None
right_list_title = None
template_name = 'appearance/generic_form.html'
LEFT_LIST_NAME = 'left_list'
RIGHT_LIST_NAME = 'right_list'
@staticmethod
def generate_choices(choices):
results = []
for choice in choices:
ct = ContentType.objects.get_for_model(choice)
label = unicode(choice)
results.append(('%s,%s' % (ct.model, choice.pk), '%s' % (label)))
# Sort results by the label not the key value
return sorted(results, key=lambda x: x[1])
def left_list(self):
# Subclass must override
raise NotImplementedError
def right_list(self):
# Subclass must override
raise NotImplementedError
def add(self, item):
# Subclass must override
raise NotImplementedError
def remove(self, item):
# Subclass must override
raise NotImplementedError
def get_disabled_choices(self):
return ()
def get_left_list_help_text(self):
return self.left_list_help_text
def get_right_list_help_text(self):
return self.right_list_help_text
def get(self, request, *args, **kwargs):
self.unselected_list = ChoiceForm(
prefix=self.LEFT_LIST_NAME, choices=self.left_list()
)
self.selected_list = ChoiceForm(
prefix=self.RIGHT_LIST_NAME, choices=self.right_list(),
disabled_choices=self.get_disabled_choices(),
help_text=self.get_right_list_help_text()
)
return self.render_to_response(self.get_context_data())
def process_form(self, prefix, items_function, action_function):
if '%s-submit' % prefix in self.request.POST.keys():
form = ChoiceForm(
self.request.POST, prefix=prefix,
choices=items_function()
)
if form.is_valid():
for selection in form.cleaned_data['selection']:
if self.grouped:
flat_list = []
for group in items_function():
flat_list.extend(group[1])
else:
flat_list = items_function()
label = dict(flat_list)[selection]
if self.decode_content_type:
model, pk = selection.split(',')
selection_obj = ContentType.objects.get(
model=model
).get_object_for_this_type(pk=pk)
else:
selection_obj = selection
try:
action_function(selection_obj)
except:
if settings.DEBUG:
raise
else:
messages.error(
self.request,
_('Unable to transfer selection: %s.') % label
)
def post(self, request, *args, **kwargs):
self.process_form(
prefix=self.LEFT_LIST_NAME, items_function=self.left_list,
action_function=self.add
)
self.process_form(
prefix=self.RIGHT_LIST_NAME, items_function=self.right_list,
action_function=self.remove
)
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
data = super(AssignRemoveView, self).get_context_data(**kwargs)
data.update({
'subtemplates_list': [
{
'name': 'appearance/generic_form_subtemplate.html',
'column_class': 'col-xs-12 col-sm-6 col-md-6 col-lg-6',
'context': {
'form': self.unselected_list,
'title': self.left_list_title or ' ',
'submit_label': _('Add'),
'submit_icon': 'fa fa-plus',
'hide_labels': True,
}
},
{
'name': 'appearance/generic_form_subtemplate.html',
'column_class': 'col-xs-12 col-sm-6 col-md-6 col-lg-6',
'context': {
'form': self.selected_list,
'title': self.right_list_title or ' ',
'submit_label': _('Remove'),
'submit_icon': 'fa fa-minus',
'hide_labels': True,
}
},
],
})
return data
class ConfirmView(ObjectListPermissionFilterMixin, ObjectPermissionCheckMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, TemplateView):
template_name = 'appearance/generic_confirm.html'
def post(self, request, *args, **kwargs):
self.view_action()
return HttpResponseRedirect(self.get_success_url())
class FormView(ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DjangoFormView):
template_name = 'appearance/generic_form.html'
class MultiFormView(DjangoFormView):
prefixes = {}
prefix = None
def get_form_kwargs(self, form_name):
kwargs = {}
kwargs.update({'initial': self.get_initial(form_name)})
kwargs.update({'prefix': self.get_prefix(form_name)})
if self.request.method in ('POST', 'PUT'):
kwargs.update({
'data': self.request.POST,
'files': self.request.FILES,
})
return kwargs
def _create_form(self, form_name, klass):
form_kwargs = self.get_form_kwargs(form_name)
form_create_method = 'create_%s_form' % form_name
if hasattr(self, form_create_method):
form = getattr(self, form_create_method)(**form_kwargs)
else:
form = klass(**form_kwargs)
return form
def get_context_data(self, **kwargs):
"""
Insert the form into the context dict.
"""
if 'forms' not in kwargs:
kwargs['forms'] = self.get_forms(
form_classes=self.get_form_classes()
)
return super(FormMixin, self).get_context_data(**kwargs)
def get_forms(self, form_classes):
return dict(
[
(
key, self._create_form(key, klass)
) for key, klass in form_classes.items()
]
)
def get_initial(self, form_name):
initial_method = 'get_%s_initial' % form_name
if hasattr(self, initial_method):
return getattr(self, initial_method)()
else:
return self.initial.copy()
def get_prefix(self, form_name):
return self.prefixes.get(form_name, self.prefix)
def get(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
forms = self.get_forms(form_classes)
return self.render_to_response(self.get_context_data(forms=forms))
def forms_valid(self, forms):
for form_name, form in forms.items():
form_valid_method = '%s_form_valid' % form_name
if hasattr(self, form_valid_method):
return getattr(self, form_valid_method)(form)
self.all_forms_valid(forms)
return HttpResponseRedirect(self.get_success_url())
def forms_invalid(self, forms):
return self.render_to_response(self.get_context_data(forms=forms))
def post(self, request, *args, **kwargs):
form_classes = self.get_form_classes()
forms = self.get_forms(form_classes)
if all([form.is_valid() for form in forms.values()]):
return self.forms_valid(forms)
else:
return self.forms_invalid(forms)
class SimpleView(ViewPermissionCheckMixin, ExtraContextMixin, TemplateView):
pass
class SingleObjectCreateView(ObjectNameMixin, ViewPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, CreateView):
template_name = 'appearance/generic_form.html'
def form_valid(self, form):
# This overrides the original Django form_valid method
self.object = form.save(commit=False)
if hasattr(self, 'get_instance_extra_data'):
for key, value in self.get_instance_extra_data().items():
setattr(self.object, key, value)
if hasattr(self, 'get_save_extra_data'):
save_extra_data = self.get_save_extra_data()
else:
save_extra_data = {}
try:
self.object.save(**save_extra_data)
except Exception as exception:
context = self.get_context_data()
messages.error(
self.request,
_('%(object)s not created, error: %(error)s') % {
'object': self.get_object_name(context=context),
'error': exception
}
)
else:
context = self.get_context_data()
messages.success(
self.request,
_(
'%(object)s created successfully.'
) % {'object': self.get_object_name(context=context)}
)
return HttpResponseRedirect(self.get_success_url())
class SingleObjectDeleteView(ObjectNameMixin, DeleteExtraDataMixin, ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, DeleteView):
template_name = 'appearance/generic_confirm.html'
def get_context_data(self, **kwargs):
context = super(SingleObjectDeleteView, self).get_context_data(**kwargs)
context.update({'delete_view': True})
return context
def delete(self, request, *args, **kwargs):
self.object = self.get_object()
context = self.get_context_data()
object_name = self.get_object_name(context=context)
try:
result = super(SingleObjectDeleteView, self).delete(request, *args, **kwargs)
except Exception as exception:
messages.error(
self.request,
_('%(object)s not deleted, error: %(error)s.') % {
'object': object_name,
'error': exception
}
)
raise exception
else:
messages.success(
self.request,
_(
'%(object)s deleted successfully.'
) % {'object': object_name}
)
return result
class SingleObjectDetailView(ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, ModelFormMixin, DetailView):
template_name = 'appearance/generic_form.html'
def get_context_data(self, **kwargs):
context = super(SingleObjectDetailView, self).get_context_data(**kwargs)
context.update({'read_only': True, 'form': self.get_form()})
return context
class SingleObjectDownloadView(ViewPermissionCheckMixin, ObjectPermissionCheckMixin, VirtualDownloadView, SingleObjectMixin):
VirtualFile = VirtualFile
class SingleObjectEditView(ObjectNameMixin, ViewPermissionCheckMixin, ObjectPermissionCheckMixin, ExtraContextMixin, RedirectionMixin, UpdateView):
template_name = 'appearance/generic_form.html'
def form_valid(self, form):
# This overrides the original Django form_valid method
self.object = form.save(commit=False)
if hasattr(self, 'get_instance_extra_data'):
for key, value in self.get_instance_extra_data().items():
setattr(self.object, key, value)
if hasattr(self, 'get_save_extra_data'):
save_extra_data = self.get_save_extra_data()
else:
save_extra_data = {}
context = self.get_context_data()
object_name = self.get_object_name(context=context)
try:
self.object.save(**save_extra_data)
except Exception as exception:
messages.error(
self.request,
_('%(object)s not updated, error: %(error)s.') % {
'object': object_name,
'error': exception
}
)
raise exception
else:
messages.success(
self.request,
_(
'%(object)s updated successfully.'
) % {'object': object_name}
)
return HttpResponseRedirect(self.get_success_url())
def get_object(self, queryset=None):
obj = super(SingleObjectEditView, self).get_object(queryset=queryset)
if hasattr(self, 'get_instance_extra_data'):
for key, value in self.get_instance_extra_data().items():
setattr(obj, key, value)
return obj
class SingleObjectListView(PaginationMixin, ViewPermissionCheckMixin, ObjectListPermissionFilterMixin, ExtraContextMixin, RedirectionMixin, ListView):
template_name = 'appearance/generic_list.html'
def get_paginate_by(self, queryset):
return setting_paginate_by.value