import os from django.contrib.auth.views import redirect_to_login from django.core.exceptions import PermissionDenied from django.forms.models import modelform_factory from django.http import HttpResponseRedirect from django.urls import reverse, reverse_lazy from django.utils.encoding import force_str from django.utils.text import get_text_list from django.utils.translation import ugettext as _ # braces 1.3 views exported AccessMixin # in braces 1.4 this was moved views._access and not exported in views # not sure if this was the intent of braces or an oversight # if intent - should look at AccessMixin vs. using a more specific mixin try: from braces.views import AccessMixin except ImportError: from braces.views._access import AccessMixin from . import settings, permissions from .utils import admin2_urlname, model_options class PermissionMixin(AccessMixin): do_not_call_in_templates = True permission_classes = (permissions.IsStaffPermission,) login_url = reverse_lazy('admin2:dashboard') def __init__(self, **kwargs): self.permissions = [ permission_class() for permission_class in self.permission_classes] super(PermissionMixin, self).__init__(**kwargs) def has_permission(self, obj=None): ''' Return ``True`` if the permission for this view shall be granted, ``False`` otherwise. Supports object-level permission by passing the related object as first argument. ''' for permission in self.permissions: if not permission.has_permission(self.request, self, obj): return False return True def dispatch(self, request, *args, **kwargs): # Raise exception or redirect to login if user doesn't have # permissions. if not self.has_permission(): if self.raise_exception: raise PermissionDenied # return a forbidden response else: return redirect_to_login( request.get_full_path(), self.get_login_url(), self.get_redirect_field_name()) return super(PermissionMixin, self).dispatch(request, *args, **kwargs) def get_context_data(self, **kwargs): context = super(PermissionMixin, self).get_context_data(**kwargs) permission_checker = permissions.TemplatePermissionChecker( self.request, self.model_admin) context.update({ 'permissions': permission_checker, }) return context class Admin2Mixin(PermissionMixin): # are set in the ModelAdmin2 class when creating the view via # .as_view(...) model_admin = None model_name = None app_label = None login_view = None index_path = reverse_lazy('admin2:dashboard') def get_template_names(self): return [os.path.join( settings.ADMIN2_THEME_DIRECTORY, self.default_template_name)] def get_templates(self): return os.path.join(settings.ADMIN2_THEME_DIRECTORY, self.default_template_name) def get_model(self): return self.model def get_queryset(self): return self.get_model()._default_manager.all() def get_form_class(self): if self.form_class is not None: return self.form_class return modelform_factory(self.get_model()) def is_user(self, request): return hasattr(request, 'user') and not (request.user.is_active and request.user.is_staff) def dispatch(self, request, *args, **kwargs): if self.is_user(request): if request.path == reverse('admin2:logout'): return HttpResponseRedirect(self.index_path) if request.path == self.index_path: extra = { 'next': request.GET.get('next', self.index_path) } return self.login_view().dispatch(request, extra_context=extra, *args, **kwargs) return super(Admin2Mixin, self).dispatch(request, *args, **kwargs) class Admin2ModelMixin(Admin2Mixin): model_admin = None def get_context_data(self, **kwargs): context = super(Admin2ModelMixin, self).get_context_data(**kwargs) model = self.get_model() model_meta = model_options(model) app_verbose_names = self.model_admin.admin.app_verbose_names context.update({ 'app_label': model_meta.app_label, 'app_verbose_name': app_verbose_names.get(model_meta.app_label), 'model_name': model_meta.verbose_name, 'model_name_pluralized': model_meta.verbose_name_plural }) return context def get_model(self): return self.model def get_queryset(self): return self.get_model()._default_manager.all() def get_form_class(self): if self.form_class is not None: return self.form_class return modelform_factory(self.get_model(), fields='__all__') class Admin2ModelFormMixin(object): def get_success_url(self): if '_continue' in self.request.POST: view_name = admin2_urlname(self, 'update') return reverse(view_name, kwargs={'pk': self.object.pk}) if '_addanother' in self.request.POST: return reverse(admin2_urlname(self, 'create')) # default to index view return reverse(admin2_urlname(self, 'index')) def construct_change_message(self, request, form, formsets): """ Construct a change message from a changed object """ change_message = [] if form.changed_data: change_message.append( _('Changed {0}.'.format( get_text_list(form.changed_data, _('and'))))) if formsets: for formset in formsets: for added_object in formset.new_objects: change_message.append( _('Added {0} "{1}".'.format( force_str(added_object._meta.verbose_name), force_str(added_object)))) for changed_object, changed_fields in formset.changed_objects: change_message.append( _('Changed {0} for {1} "{2}".'.format( get_text_list(changed_fields, _('and')), force_str(changed_object._meta.verbose_name), force_str(changed_object)))) for deleted_object in formset.deleted_objects: change_message.append( _('Deleted {0} "{1}".'.format( force_str(deleted_object._meta.verbose_name), force_str(deleted_object)))) change_message = ' '.join(change_message) return change_message or _('No fields changed.')