diff --git a/mayan/apps/common/apps.py b/mayan/apps/common/apps.py index 3f1a8a8a46..af82f368a8 100644 --- a/mayan/apps/common/apps.py +++ b/mayan/apps/common/apps.py @@ -19,9 +19,10 @@ from .handlers import ( from .links import ( link_about, link_current_user_details, link_current_user_edit, link_current_user_locale_profile_details, - link_current_user_locale_profile_edit, link_license, link_setup, link_tools + link_current_user_locale_profile_edit, link_filters, link_license, + link_setup, link_tools ) -from .menus import menu_facet, menu_main, menu_secondary +from .menus import menu_facet, menu_main, menu_secondary, menu_tools logger = logging.getLogger(__name__) @@ -97,6 +98,10 @@ class CommonApp(MayanAppConfig): ) ) + menu_tools.bind_links( + links=(link_filters,) + ) + post_save.connect( user_locale_profile_create, dispatch_uid='user_locale_profile_create', diff --git a/mayan/apps/common/classes.py b/mayan/apps/common/classes.py index c3297e79c6..624a7c5667 100644 --- a/mayan/apps/common/classes.py +++ b/mayan/apps/common/classes.py @@ -1,8 +1,12 @@ from __future__ import unicode_literals +from django.core.exceptions import PermissionDenied from django.db import models from django.utils.translation import ugettext +from acls.models import AccessControlList +from permissions import Permission + class ModelAttribute(object): __registry = {} @@ -95,3 +99,53 @@ class MissingItem(object): self.description = description self.view = view self.__class__._registry.append(self) + + +class Filter(object): + _registry = {} + + @classmethod + def get(cls, slug): + return cls._registry[slug] + + @classmethod + def all(cls): + return cls._registry + + def __init__(self, label, slug, filter_kwargs, model, object_permission=None, hide_links=False): + self.label = label + self.slug = slug + self.filter_kwargs = filter_kwargs + self.model = model + self.object_permission = object_permission + self.hide_links = hide_links + + self.__class__._registry[self.slug] = self + + def __unicode__(self): + return unicode(self.label) + + def get_queryset(self, user): + queryset = self.model.objects.all() + for kwargs in self.filter_kwargs: + queryset = queryset.filter(**kwargs) + + queryset = queryset.distinct() + + if self.object_permission: + try: + # Check to see if the user has the permissions globally + Permission.check_permissions( + user, (self.object_permission,) + ) + except PermissionDenied: + # No global permission, filter ther queryset per object + + # permission + return AccessControlList.objects.filter_by_access( + self.object_permission, user, queryset + ) + else: + # Has the permission globally, return all results + return queryset + else: + return queryset diff --git a/mayan/apps/common/forms.py b/mayan/apps/common/forms.py index 5dd18151fb..dcc447d62d 100644 --- a/mayan/apps/common/forms.py +++ b/mayan/apps/common/forms.py @@ -9,6 +9,7 @@ from django.db import models from django.utils.html import escape from django.utils.translation import ugettext_lazy as _ +from .classes import Filter from .models import UserLocaleProfile from .utils import return_attrib from .widgets import DetailSelectMultiple, DisableableSelectWidget, PlainWidget @@ -134,6 +135,14 @@ class FileDisplayForm(forms.Form): fd.close() +class FilterForm(forms.Form): + filter_slug = forms.ChoiceField(label=_('Field')) + + def __init__(self, *args, **kwargs): + super(FilterForm, self).__init__(*args, **kwargs) + self.fields['filter_slug'].choices = Filter.all().items() + + class LicenseForm(FileDisplayForm): FILENAME = 'LICENSE' DIRECTORY = [] diff --git a/mayan/apps/common/links.py b/mayan/apps/common/links.py index d2fc412566..b0685afb32 100644 --- a/mayan/apps/common/links.py +++ b/mayan/apps/common/links.py @@ -23,6 +23,10 @@ link_current_user_locale_profile_edit = Link( icon='fa fa-globe', text=_('Edit locale profile'), view='common:current_user_locale_profile_edit' ) +link_filters = Link( + icon='fa fa-filter', text=_('Data filters'), + view='common:filter_selection' +) link_license = Link( icon='fa fa-book', text=_('License'), view='common:license_view' ) diff --git a/mayan/apps/common/urls.py b/mayan/apps/common/urls.py index 7aa779405d..0a9b467d27 100644 --- a/mayan/apps/common/urls.py +++ b/mayan/apps/common/urls.py @@ -7,7 +7,8 @@ from django.views.generic import RedirectView from .views import ( AboutView, CurrentUserDetailsView, CurrentUserEditView, CurrentUserLocaleProfileDetailsView, CurrentUserLocaleProfileEditView, - HomeView, LicenseView, SetupListView, ToolsListView + FilterResultListView, FilterSelectView, HomeView, LicenseView, + SetupListView, ToolsListView ) urlpatterns = patterns( @@ -37,6 +38,14 @@ urlpatterns = patterns( r'^user/locale/edit/$', CurrentUserLocaleProfileEditView.as_view(), name='current_user_locale_profile_edit' ), + url( + r'^filter/select/$', FilterSelectView.as_view(), + name='filter_selection' + ), + url( + r'^filter/(?P[\w-]+)/results/$', FilterResultListView.as_view(), + name='filter_results' + ), ) urlpatterns += patterns( diff --git a/mayan/apps/common/views.py b/mayan/apps/common/views.py index 523ce0d43f..3e6cf3cb88 100644 --- a/mayan/apps/common/views.py +++ b/mayan/apps/common/views.py @@ -5,15 +5,16 @@ from json import dumps from django.conf import settings from django.contrib import messages from django.core.urlresolvers import reverse, reverse_lazy -from django.http import HttpResponseRedirect +from django.http import Http404, HttpResponseRedirect from django.template import RequestContext from django.utils import timezone, translation from django.utils.http import urlencode -from django.utils.translation import ugettext_lazy as _ +from django.utils.translation import ugettext_lazy as _, ugettext from django.views.generic import TemplateView +from .classes import Filter from .forms import ( - LicenseForm, LocaleProfileForm, LocaleProfileForm_view, + FilterForm, LicenseForm, LocaleProfileForm, LocaleProfileForm_view, UserForm, UserForm_view ) from .generics import * # NOQA @@ -99,6 +100,45 @@ class CurrentUserLocaleProfileEditView(SingleObjectEditView): return self.request.user.locale_profile +class FilterSelectView(SimpleView): + form_class = FilterForm + template_name = 'appearance/generic_form.html' + + def get_form(self): + return FilterForm() + + def get_extra_context(self): + return { + 'form': self.get_form(), + 'title': _('Filter selection') + } + + def post(self, request, *args, **kwargs): + return HttpResponseRedirect( + reverse( + 'common:filter_results', + args=(request.POST.get('filter_slug'),) + ) + ) + + +class FilterResultListView(SingleObjectListView): + def get_extra_context(self): + return { + 'hide_links': self.get_filter().hide_links, + 'title': _('Results for filter: %s') % self.get_filter() + } + + def get_filter(self): + try: + return Filter.get(self.kwargs['slug']) + except KeyError: + raise Http404(ugettext('Filter not found')) + + def get_queryset(self): + return self.get_filter().get_queryset(user=self.request.user) + + class HomeView(TemplateView): template_name = 'appearance/home.html' diff --git a/mayan/apps/metadata/apps.py b/mayan/apps/metadata/apps.py index 9169b4cacf..8a2c47df11 100644 --- a/mayan/apps/metadata/apps.py +++ b/mayan/apps/metadata/apps.py @@ -12,11 +12,12 @@ from common import ( MayanAppConfig, menu_facet, menu_multi_item, menu_object, menu_secondary, menu_setup, menu_sidebar, menu_tools ) -from common.classes import ModelAttribute +from common.classes import ModelAttribute, Filter from common.widgets import two_state_template from documents.models import Document, DocumentType from documents.search import document_search from documents.signals import post_document_type_change +from documents.permissions import permission_document_view from mayan.celery import app from navigation import SourceColumn from rest_api.classes import APIEndPoint @@ -36,7 +37,6 @@ from .links import ( link_setup_document_type_metadata_required, link_setup_metadata_type_create, link_setup_metadata_type_delete, link_setup_metadata_type_edit, link_setup_metadata_type_list, - link_documents_missing_required_metadata ) from .models import DocumentMetadata, DocumentTypeMetadataType, MetadataType from .permissions import ( @@ -60,6 +60,40 @@ class MetadataApp(MayanAppConfig): 'metadata_value_of', DocumentMetadataHelper.constructor ) + Filter( + label=_('Documents missing required metadata'), + slug='documents-no-required-metadata', + filter_kwargs=[ + { + 'document_type__metadata__required': True, + }, + { + 'metadata__value__isnull': True + }, + { + 'is_stub': False + } + ], model=Document, object_permission=permission_document_view, + hide_links=True + ) + + Filter( + label=_('Documents missing optional metadata'), + slug='documents-no-optional-metadata', + filter_kwargs=[ + { + 'document_type__metadata__required': False, + }, + { + 'metadata__value__isnull': True + }, + { + 'is_stub': False + } + ], model=Document, object_permission=permission_document_view, + hide_links=True + ) + ModelAttribute( Document, 'metadata', type_name='related', description=_( @@ -165,9 +199,6 @@ class MetadataApp(MayanAppConfig): 'metadata:metadata_remove', 'metadata:metadata_view' ) ) - menu_tools.bind_links( - links=(link_documents_missing_required_metadata,) - ) post_delete.connect( post_document_type_metadata_type_delete, diff --git a/mayan/apps/metadata/links.py b/mayan/apps/metadata/links.py index 0351b340e6..147f796fba 100644 --- a/mayan/apps/metadata/links.py +++ b/mayan/apps/metadata/links.py @@ -12,10 +12,6 @@ from .permissions import ( permission_metadata_type_edit, permission_metadata_type_view ) -link_documents_missing_required_metadata = Link( - icon='fa fa-edit', text=_('Missing metadata'), - view='metadata:documents_missing_required_metadata' -) link_metadata_add = Link( permissions=(permission_metadata_document_add,), text=_('Add metadata'), view='metadata:metadata_add', args='object.pk' diff --git a/mayan/apps/metadata/urls.py b/mayan/apps/metadata/urls.py index b570978d3e..4468843a89 100644 --- a/mayan/apps/metadata/urls.py +++ b/mayan/apps/metadata/urls.py @@ -12,7 +12,6 @@ from .api_views import ( from .views import ( DocumentMetadataListView, MetadataTypeCreateView, MetadataTypeDeleteView, MetadataTypeEditView, MetadataTypeListView, - MissingRequiredMetadataDocumentListView, SetupDocumentTypeMetadataOptionalView, SetupDocumentTypeMetadataRequiredView ) @@ -69,12 +68,6 @@ urlpatterns = patterns( SetupDocumentTypeMetadataRequiredView.as_view(), name='setup_document_type_metadata_required' ), - - url( - r'^tools/missing_required_metadata/$', - MissingRequiredMetadataDocumentListView.as_view(), - name='documents_missing_required_metadata' - ), ) api_urls = patterns( diff --git a/mayan/apps/metadata/views.py b/mayan/apps/metadata/views.py index 03b8c88912..79bcc5d07a 100644 --- a/mayan/apps/metadata/views.py +++ b/mayan/apps/metadata/views.py @@ -35,19 +35,6 @@ from .permissions import ( ) -class MissingRequiredMetadataDocumentListView(DocumentListView): - extra_context = { - 'hide_links': True, - 'title': _('Documents missing required metadata'), - } - - def get_document_queryset(self): - return Document.objects.filter( - document_type__metadata__required=True, - metadata__value__isnull=True - ) - - def metadata_edit(request, document_id=None, document_id_list=None): if document_id: document_id_list = unicode(document_id)