import logging from braces.views import FormMessagesMixin from compressor.utils.decorators import cached_property from crispy_forms.bootstrap import FieldWithButtons, StrictButton from crispy_forms.helper import FormHelper from crispy_forms.layout import Field, Layout from django import forms from django.conf.urls import url from django.contrib import messages from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.exceptions import ObjectDoesNotExist from django.core.urlresolvers import reverse from django.db import models from django.db.models.fields.related import ForeignKey from django.http.response import Http404 from django.shortcuts import redirect from django.utils.decorators import classonlymethod from django.utils.encoding import force_text from django.utils.translation import ugettext_lazy as _ from django.utils.translation import string_concat from django.views.generic import (CreateView, DeleteView, DetailView, ListView, UpdateView) from django.views.generic.base import ContextMixin from django.views.generic.list import MultipleObjectMixin from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display from sapl.rules.map_rules import (RP_ADD, RP_CHANGE, RP_DELETE, RP_DETAIL, RP_LIST) from sapl.settings import BASE_DIR from sapl.utils import normalize logger = logging.getLogger(BASE_DIR.name) ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \ 'list', 'create', 'detail', 'update', 'delete' def _form_invalid_message(msg): return '%s %s' % (_('Formulário inválido.'), msg) FORM_MESSAGES = {ACTION_CREATE: (_('Registro criado com sucesso!'), _('O registro não foi criado.')), ACTION_UPDATE: (_('Registro alterado com sucesso!'), _('Suas alterações não foram salvas.')), ACTION_DELETE: (_('Registro excluído com sucesso!'), _('O registro não foi excluído.'))} FORM_MESSAGES = {k: (a, _form_invalid_message(b)) for k, (a, b) in FORM_MESSAGES.items()} def from_to(start, end): return list(range(start, end + 1)) def make_pagination(index, num_pages): '''Make a list of adjacent page ranges interspersed with "None"s The list starts with [1, 2] and end with [num_pages-1, num_pages]. The list includes [index-1, index, index+1] "None"s separate those ranges and mean ellipsis (...) Example: [1, 2, None, 10, 11, 12, None, 29, 30] ''' PAGINATION_LENGTH = 10 if num_pages <= PAGINATION_LENGTH: return from_to(1, num_pages) else: if index - 1 <= 5: tail = [num_pages - 1, num_pages] head = from_to(1, PAGINATION_LENGTH - 3) else: if index + 1 >= num_pages - 3: tail = from_to(index - 1, num_pages) else: tail = [index - 1, index, index + 1, None, num_pages - 1, num_pages] head = from_to(1, PAGINATION_LENGTH - len(tail) - 1) return head + [None] + tail """ variáveis do crud: help_path container_field container_field_set is_m2m model model_set form_search_class -> depende de o model relativo implementar SearchMixin list_field_names list_field_names_set -> lista reversa em details permission_required -> este atributo ser vazio não nulo torna a view publ layout_key_set layout_key ordered_list = False desativa os clicks e controles de ord da listagem parent_field = parentesco reverso separado por '__' namespace return_parent_field_url """ class SearchMixin(models.Model): search = models.TextField(blank=True, default='') class Meta: abstract = True def save(self, force_insert=False, force_update=False, using=None, update_fields=None, auto_update_search=True): if auto_update_search and hasattr(self, 'fields_search'): search = '' for str_field in self.fields_search: fields = str_field.split('__') if len(fields) == 1: try: search += str(getattr(self, str_field)) + ' ' except: pass else: _self = self for field in fields: _self = getattr(_self, field) search += str(_self) + ' ' self.search = search self.search = normalize(self.search) return super(SearchMixin, self).save( force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields) class ListWithSearchForm(forms.Form): q = forms.CharField(required=False, label='', widget=forms.TextInput( attrs={'type': 'search'})) o = forms.CharField(required=False, label='', widget=forms.HiddenInput()) class Meta: fields = ['q', 'o'] def __init__(self, *args, **kwargs): super(ListWithSearchForm, self).__init__(*args, **kwargs) self.helper = FormHelper() self.form_class = 'form-inline' self.helper.form_method = 'GET' self.helper.layout = Layout( Field('o'), FieldWithButtons( Field('q', placeholder=_('Filtrar Lista'), css_class='input-lg'), StrictButton( _('Filtrar'), css_class='btn-default btn-lg', type='submit')) ) class PermissionRequiredForAppCrudMixin(PermissionRequiredMixin): def has_permission(self): apps = self.app_label if isinstance(apps, str): apps = apps, # app_label vazio dará acesso geral for app in apps: if not self.request.user.has_module_perms(app): return False return True class PermissionRequiredContainerCrudMixin(PermissionRequiredMixin): def has_permission(self): perms = self.get_permission_required() # Torna a view pública se não possuir conteudo # no atributo permission_required if not len(perms): return True for perm in perms: if self.request.user.has_perm(perm): return True return False # return self.request.user.has_perms(perms) if len(perms) else True def dispatch(self, request, *args, **kwargs): if not self.has_permission(): return self.handle_no_permission() if 'pk' in kwargs: params = {'pk': kwargs['pk']} if self.container_field: params[self.container_field] = request.user.pk if not self.model.objects.filter(**params).exists(): raise Http404() elif self.container_field: container = self.container_field.split('__') if len(container) > 1: container_model = getattr( self.model, container[0]).field.related_model params = {} params['__'.join( container[1:])] = request.user.pk if not container_model.objects.filter(**params).exists(): messages.error( request, 'O Usuário (%s) não está registrado como (%s).' % ( request.user, container_model._meta.verbose_name)) return redirect('/') else: # TODO: implementar caso o user for o próprio o container pass return super(PermissionRequiredMixin, self).dispatch( request, *args, **kwargs) @cached_property def container_field(self): if hasattr(self, 'crud') and not hasattr(self.crud, 'container_field'): self.crud.container_field = '' if hasattr(self, 'crud'): return self.crud.container_field @cached_property def container_field_set(self): if hasattr(self, 'crud') and\ not hasattr(self.crud, 'container_field_set'): self.crud.container_field_set = '' if hasattr(self, 'crud'): return self.crud.container_field_set @cached_property def is_contained(self): return self.container_field_set or self.container_field class CrudBaseMixin(CrispyLayoutFormMixin): def __init__(self, **kwargs): super(CrudBaseMixin, self).__init__(**kwargs) obj = self.crud if hasattr(self, 'crud') else self self.app_label = obj.model._meta.app_label self.model_name = obj.model._meta.model_name if hasattr(obj, 'model_set') and obj.model_set: self.app_label_set = getattr( obj.model, obj.model_set).field.model._meta.app_label self.model_name_set = getattr( obj.model, obj.model_set).field.model._meta.model_name if hasattr(self, 'permission_required') and self.permission_required: if hasattr(obj, 'public'): self.permission_required = list( set(self.permission_required) - set(obj.public)) else: obj.public = [] self.permission_required = tuple(( self.permission(pr) for pr in self.permission_required)) else: obj.public = [] @classmethod def url_name(cls, suffix): return '%s_%s' % (cls.model._meta.model_name, suffix) def url_name_set(self, suffix): obj = self.crud if hasattr(self, 'crud') else self return '%s_%s' % (getattr(obj.model, obj.model_set ).field.model._meta.model_name, suffix) def permission(self, rad): return '%s%s%s' % (self.app_label if rad.endswith('_') else '', rad, self.model_name if rad.endswith('_') else '') def permission_set(self, rad): return '%s%s%s' % (self.app_label_set if rad.endswith('_') else '', rad, self.model_name_set if rad.endswith('_') else '') def resolve_url(self, suffix, args=None): namespace = self.model._meta.app_config.name return reverse('%s:%s' % (namespace, self.url_name(suffix)), args=args) def resolve_url_set(self, suffix, args=None): obj = self.crud if hasattr(self, 'crud') else self namespace = getattr( obj.model, obj.model_set).field.model._meta.app_config.name return reverse('%s:%s' % (namespace, self.url_name_set(suffix)), args=args) @property def ordered_list(self): return True @property def list_url(self): obj = self.crud if hasattr(self, 'crud') else self if not obj.ListView.permission_required: return self.resolve_url(ACTION_LIST) else: return self.resolve_url( ACTION_LIST) if self.request.user.has_perm( self.permission(RP_LIST)) else '' @property def create_url(self): obj = self.crud if hasattr(self, 'crud') else self if not obj.CreateView.permission_required: return self.resolve_url(ACTION_CREATE) else: return self.resolve_url( ACTION_CREATE) if self.request.user.has_perm( self.permission(RP_ADD)) else '' @property def detail_url(self): obj = self.crud if hasattr(self, 'crud') else self if not obj.DetailView.permission_required: return self.resolve_url(ACTION_DETAIL, args=(self.object.id,)) else: return self.resolve_url(ACTION_DETAIL, args=(self.object.id,))\ if self.request.user.has_perm( self.permission(RP_DETAIL)) else '' @property def update_url(self): obj = self.crud if hasattr(self, 'crud') else self if not obj.UpdateView.permission_required: return self.resolve_url(ACTION_UPDATE, args=(self.object.id,)) else: return self.resolve_url(ACTION_UPDATE, args=(self.object.id,))\ if self.request.user.has_perm( self.permission(RP_CHANGE)) else '' @property def delete_url(self): obj = self.crud if hasattr(self, 'crud') else self if not obj.DeleteView.permission_required: return self.resolve_url(ACTION_DELETE, args=(self.object.id,)) else: return self.resolve_url(ACTION_DELETE, args=(self.object.id,))\ if self.request.user.has_perm( self.permission(RP_DELETE)) else '' def get_template_names(self): names = super(CrudBaseMixin, self).get_template_names() names.append("crud/%s.html" % self.template_name_suffix.lstrip('_')) return names @property def verbose_name_set(self): obj = self.crud if hasattr(self, 'crud') else self return getattr(obj.model, obj.model_set).field.model._meta.verbose_name @property def verbose_name(self): return self.model._meta.verbose_name @property def verbose_name_plural(self): return self.model._meta.verbose_name_plural class CrudListView(PermissionRequiredContainerCrudMixin, ListView): permission_required = (RP_LIST, ) @classmethod def get_url_regex(cls): return r'^$' paginate_by = 10 no_entries_msg = _('Nenhum registro encontrado.') def get_rows(self, object_list): return [self._as_row(obj) for obj in object_list] def get_headers(self): """ Transforma o headers de fields de list_field_names para junção de fields via tuplas. list_field_names pode ser construido como list_field_names=('nome', 'endereco', ('telefone', sexo'), 'dat_nasc') ou ainda: list_field_names = ['composicao__comissao__nome', 'cargo__nome', ( 'composicao__periodo__data_inicio', 'composicao__periodo__data_fim')] """ r = [] for fieldname in self.list_field_names: if not isinstance(fieldname, tuple): fieldname = fieldname, s = [] for fn in fieldname: m = self.model fn = fn.split('__') for f in fn: f = m._meta.get_field(f) if hasattr(f, 'related_model') and f.related_model: m = f.related_model s.append(force_text(f.verbose_name)) s = ' / '.join(s) r.append(s) return r def _as_row(self, obj): r = [] for i, name in enumerate(self.list_field_names): url = self.resolve_url( ACTION_DETAIL, args=(obj.id,)) if i == 0 else None """Caso o crud list seja para uma relação ManyToManyField""" if url and hasattr(self, 'crud') and\ hasattr(self.crud, 'is_m2m') and self.crud.is_m2m: url = url + ('?pkk=' + self.kwargs['pk'] if 'pk' in self.kwargs else '') if not isinstance(name, tuple): name = name, """ se elemento de list_field_name for uma tupla, constrói a informação com ' - ' se os campos forem simples, ou com
se for m2m """ if isinstance(name, tuple): s = '' for j, n in enumerate(name): m = obj n = n.split('__') for f in n[:-1]: m = getattr(m, f) if not m: break if m: ss = get_field_display(m, n[-1])[1] ss = ( ('
' if '