diff --git a/sapl/base/models.py b/sapl/base/models.py index cc2004455..71f2634fe 100644 --- a/sapl/base/models.py +++ b/sapl/base/models.py @@ -7,12 +7,11 @@ from django.contrib.contenttypes.models import ContentType from django.core import exceptions from django.db import models, router from django.db.utils import DEFAULT_DB_ALIAS -from django.utils.translation import string_concat from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import string_concat from sapl.utils import UF, YES_NO_CHOICES - TIPO_DOCUMENTO_ADMINISTRATIVO = (('O', _('Ostensivo')), ('R', _('Restritivo'))) @@ -107,6 +106,15 @@ class AppConfig(models.Model): ('view_tabelas_auxiliares', _('Visualizar Tabelas Auxiliares')), ) + @classmethod + def attr(cls, attr): + config = AppConfig.objects.first() + + if not config: + return '' + + return getattr(config, attr) + def __str__(self): return _('Configurações da Aplicação - %(id)s') % { 'id': self.id} @@ -204,4 +212,4 @@ def create_proxy_permissions( models.signals.post_migrate.connect( receiver=create_proxy_permissions, - dispatch_uid="django.contrib.auth.management.create_permissions") \ No newline at end of file + dispatch_uid="django.contrib.auth.management.create_permissions") diff --git a/sapl/base/templatetags/common_tags.py b/sapl/base/templatetags/common_tags.py index c926a485d..86205cef2 100644 --- a/sapl/base/templatetags/common_tags.py +++ b/sapl/base/templatetags/common_tags.py @@ -122,10 +122,12 @@ def ultima_filiacao(value): @register.filter def get_config_not_exists(user): - if not AppConfig.objects.all().exists(): - return True - else: - return False + return not AppConfig.objects.exists() + + +@register.filter +def get_config_attr(attribute): + return AppConfig.attr(attribute) @register.filter diff --git a/sapl/base/templatetags/menus.py b/sapl/base/templatetags/menus.py index d5a398faf..61c468acf 100644 --- a/sapl/base/templatetags/menus.py +++ b/sapl/base/templatetags/menus.py @@ -1,6 +1,7 @@ -import yaml from django import template from django.core.urlresolvers import reverse +import yaml + register = template.Library() @@ -14,6 +15,12 @@ def subnav(context, path=None): 1) Se a variável path não é nula; 2) Se existe no contexto a chave subnav_template_name; 3) o path default: /subnav.yaml + + Os campos esperados nos arquivos yaml são: + title + url + check_permission - opcional. quando usado + será realizado o teste de permissão para renderizá-lo. """ menu = None root_pk = context.get('root_pk', None) @@ -76,40 +83,36 @@ def resolve_urls_inplace(menu, pk, rm, context): else: if 'url' in menu: url_name = menu['url'] - menu['url'] = reverse('%s:%s' % (rm.app_name, menu['url']), - kwargs={'pk': pk}) - menu['active'] = 'active'\ - if context['request'].path == menu['url'] else '' - - if not menu['active']: - """ - Se não encontrada diretamente, - procura a url acionada dentro do crud, caso seja um. - Serve para manter o active no suvnav correto ao acionar - as funcionalidades diretas do MasterDetailCrud, como: - - visualização de detalhes, adição, edição, remoção. - - Casos para urls_extras: - Em relações de segundo nível, como ocorre em - (0) Comissões -> (1) Composição -> (2) Participação - - (2) não tem ligação direta com (1) através da view. Para (2) - ser localizado, e o nav-tabs ou nav-pills do front-end serem - ativados foi inserido o teste de existência de urls_extras - para serem testadas e, sendo válidado, o active do front-end - seja devidamente colocado. - """ - - view = context['view'] - if hasattr(view, '__class__') and\ - hasattr(view.__class__, 'crud'): - urls = view.__class__.crud.get_urls() - for u in urls: - if (u.name == url_name or - 'urls_extras' in menu and - u.name in menu['urls_extras']): - menu['active'] = 'active' - break + + if 'check_permission' in menu and not context[ + 'request'].user.has_perm(menu['check_permission']): + menu['url'] = '' + menu['active'] = '' + else: + menu['url'] = reverse( + '%s:%s' % (rm.app_name, menu['url']), kwargs={'pk': pk}) + menu['active'] = 'active'\ + if context['request'].path == menu['url'] else '' + + if not menu['active']: + """ + Se não encontrada diretamente, + procura a url acionada dentro do crud, caso seja um. + Serve para manter o active no suvnav correto ao acionar + as funcionalidades diretas do MasterDetailCrud, como: + - visualização de detalhes, adição, edição, remoção. + """ + + view = context['view'] + if hasattr(view, '__class__') and\ + hasattr(view.__class__, 'crud'): + urls = view.__class__.crud.get_urls() + for u in urls: + if (u.name == url_name or + 'urls_extras' in menu and + u.name in menu['urls_extras']): + menu['active'] = 'active' + break if 'children' in menu: menu['active'] = resolve_urls_inplace( diff --git a/sapl/base/urls.py b/sapl/base/urls.py index 1cfb5a395..5611ab354 100644 --- a/sapl/base/urls.py +++ b/sapl/base/urls.py @@ -20,7 +20,7 @@ urlpatterns = [ (TemplateView.as_view(template_name='sistema.html'))), url(r'^ajuda/', TemplateView.as_view(template_name='ajuda.html')), - url(r'^relatorios/', TemplateView.as_view( + url(r'^relatorios/$', TemplateView.as_view( template_name='base/relatorios_list.html')), url(r'^ajuda/(?P\w+)$', HelpView.as_view(), name='help_topic'), url(r'^ajuda/', TemplateView.as_view(template_name='ajuda/index.html'), diff --git a/sapl/base/views.py b/sapl/base/views.py index fa3475a8d..b7d6378b8 100644 --- a/sapl/base/views.py +++ b/sapl/base/views.py @@ -1,5 +1,5 @@ -from braces.views import PermissionRequiredMixin +from django.contrib.auth.mixins import PermissionRequiredMixin from django.core.urlresolvers import reverse from django.db.models import Count, Q from django.http import HttpResponseRedirect diff --git a/sapl/comissoes/views.py b/sapl/comissoes/views.py index 9d7208f62..ae11cdbf7 100644 --- a/sapl/comissoes/views.py +++ b/sapl/comissoes/views.py @@ -68,13 +68,14 @@ class MateriasTramitacaoListView(ListView): def get_queryset(self): # FIXME: Otimizar consulta lista = [] - materias = MateriaLegislativa.objects.filter(tramitacao__isnull=False) + materias = MateriaLegislativa.objects.filter( + tramitacao__isnull=False).order_by('tipo', 'ano', 'numero') for materia in materias: comissao = materia.tramitacao_set.last( - ).unidade_tramitacao_local.comissao - if comissao: - if comissao.pk == int(self.kwargs['pk']): - lista.append(materia) + ).unidade_tramitacao_destino.comissao + if (comissao and materia not in lista and + comissao.pk == int(self.kwargs['pk'])): + lista.append(materia) return lista def get_context_data(self, **kwargs): diff --git a/sapl/crispy_layout_mixin.py b/sapl/crispy_layout_mixin.py index 5ffc2533c..e0184e0f0 100644 --- a/sapl/crispy_layout_mixin.py +++ b/sapl/crispy_layout_mixin.py @@ -54,7 +54,7 @@ def get_field_display(obj, fieldname): field = obj._meta.get_field(fieldname) except: value = getattr(obj, fieldname) - return '', value + return '', str(value) verbose_name = str(field.verbose_name)\ if hasattr(field, 'verbose_name') else '' if hasattr(field, 'choices') and field.choices: diff --git a/sapl/crud/base.py b/sapl/crud/base.py index 6f67af03e..97aef7461 100644 --- a/sapl/crud/base.py +++ b/sapl/crud/base.py @@ -13,8 +13,8 @@ from django.db import models from django.http.response import Http404 from django.utils.decorators import classonlymethod from django.utils.encoding import force_text -from django.utils.translation import string_concat 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 @@ -23,7 +23,6 @@ from django.views.generic.list import MultipleObjectMixin from sapl.crispy_layout_mixin import CrispyLayoutFormMixin, get_field_display from sapl.utils import normalize - logger = logging.getLogger(__name__) ACTION_LIST, ACTION_CREATE, ACTION_DETAIL, ACTION_UPDATE, ACTION_DELETE = \ @@ -158,11 +157,25 @@ class ListWithSearchForm(forms.Form): ) +class PermissionRequiredForAppCrudMixin(PermissionRequiredMixin): + + def has_permission(self): + apps = self.app_label + if isinstance(apps, str): + apps = apps, + # papp_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 o atributo permission_required + # Torna a view pública se não possuir conteudo + # no atributo permission_required return self.request.user.has_perms(perms) if len(perms) else True def dispatch(self, request, *args, **kwargs): @@ -302,7 +315,7 @@ class CrudBaseMixin(CrispyLayoutFormMixin): 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) + 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( @@ -354,7 +367,6 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): for fieldname in self.list_field_names: if not isinstance(fieldname, tuple): fieldname = fieldname, - s = [] for fn in fieldname: m = self.model @@ -363,20 +375,12 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): f = m._meta.get_field(f) if hasattr(f, 'related_model') and f.related_model: m = f.related_model - if m == self.model: - s.append(force_text(f.verbose_name)) - else: - s.append(force_text(m._meta.verbose_name)) + s.append(force_text(f.verbose_name)) s = ' / '.join(s) r.append(s) return r def _as_row(self, obj): - """ - FIXME: Refatorar função para capturar url correta em caso de uso de - campos foreignkey. getHeaders já faz isso para construir o título. - falta fazer com esta função - """ r = [] for i, name in enumerate(self.list_field_names): url = self.resolve_url( @@ -388,20 +392,28 @@ class CrudListView(PermissionRequiredContainerCrudMixin, ListView): 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): - ss = get_field_display(obj, n)[1] - ss = ( - ('
' if '