from __future__ import absolute_import, unicode_literals from django.conf import settings from django.contrib import messages from django.core.exceptions import PermissionDenied from django.core.urlresolvers import reverse from django.http import HttpResponseRedirect, Http404 from django.shortcuts import get_object_or_404, render_to_response from django.template import RequestContext from django.utils.http import urlencode from django.utils.translation import ugettext_lazy as _, ungettext from acls.models import AccessControlList from common.utils import encapsulate from common.views import AssignRemoveView from documents.models import Document, DocumentType from documents.permissions import ( permission_document_type_edit ) from documents.views import DocumentListView from permissions import Permission from .api import save_metadata_list from .forms import ( AddMetadataForm, MetadataFormSet, MetadataRemoveFormSet, MetadataTypeForm ) from .models import DocumentMetadata, MetadataType from .permissions import ( permission_metadata_document_add, permission_metadata_document_edit, permission_metadata_document_remove, permission_metadata_document_view, permission_metadata_type_create, permission_metadata_type_delete, permission_metadata_type_edit, permission_metadata_type_view ) 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) documents = Document.objects.select_related('metadata').filter(pk__in=document_id_list.split(',')) try: Permission.check_permissions(request.user, [permission_metadata_document_edit]) except PermissionDenied: documents = AccessControlList.objects.filter_by_access(permission_metadata_document_edit, request.user, documents) if not documents: if document_id: raise Http404 else: messages.error(request, _('Must provide at least one document.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) if len(set([document.document_type.pk for document in documents])) > 1: messages.error(request, _('Only select documents of the same type.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) if set(documents.values_list('metadata__value', flat=True)) == set([None]): message = ungettext( 'The selected document doesn\'t have any metadata.', 'The selected documents don\'t have any metadata.', len(documents) ) messages.warning(request, message) return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) post_action_redirect = reverse('documents:document_list_recent') next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', post_action_redirect))) metadata = {} initial = [] for document in documents: document.add_as_recent_document_for_user(request.user) for item in document.metadata.all(): value = item.value if item.metadata_type in metadata: if value not in metadata[item.metadata_type]: metadata[item.metadata_type].append(value) else: metadata[item.metadata_type] = [value] if value else [] for key, value in metadata.items(): initial.append({ 'metadata_type': key, 'value': ', '.join(value) if value else '', 'required': key in document.document_type.metadata.filter(required=True), }) formset = MetadataFormSet(initial=initial) if request.method == 'POST': formset = MetadataFormSet(data=request.POST, initial=initial) if formset.is_valid(): for document in documents: errors = [] for form in formset.forms: if form.cleaned_data['update']: try: save_metadata_list([form.cleaned_data], document) except Exception as exception: errors.append(exception) if errors: for error in errors: if settings.DEBUG: raise else: messages.error(request, _('Error editing metadata for document %(document)s; %(exception)s.') % { 'document': document, 'exception': ', '.join(exception.messages)}) else: messages.success(request, _('Metadata for document %s edited successfully.') % document) return HttpResponseRedirect(next) context = { 'form_display_mode_table': True, 'form': formset, 'next': next, } if len(documents) == 1: context['object'] = documents[0] context['title'] = ungettext( 'Edit document metadata', 'Edit documents metadata', len(documents) ) return render_to_response('appearance/generic_form.html', context, context_instance=RequestContext(request)) def metadata_multiple_edit(request): return metadata_edit(request, document_id_list=request.GET.get('id_list', '')) def metadata_add(request, document_id=None, document_id_list=None): if document_id: documents = [get_object_or_404(Document, pk=document_id)] elif document_id_list: documents = [get_object_or_404(Document.objects.select_related('document_type'), pk=document_id) for document_id in document_id_list.split(',')] if len(set([document.document_type.pk for document in documents])) > 1: messages.error(request, _('Only select documents of the same type.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) try: Permission.check_permissions(request.user, [permission_metadata_document_add]) except PermissionDenied: documents = AccessControlList.objects.filter_by_access(permission_metadata_document_add, request.user, documents) if not documents: messages.error(request, _('Must provide at least one document.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) for document in documents: document.add_as_recent_document_for_user(request.user) post_action_redirect = reverse('documents:document_list_recent') next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', post_action_redirect))) if request.method == 'POST': form = AddMetadataForm(data=request.POST, document_type=document.document_type) if form.is_valid(): metadata_type = form.cleaned_data['metadata_type'] for document in documents: try: document_metadata, created = DocumentMetadata.objects.get_or_create(document=document, metadata_type=metadata_type.metadata_type, defaults={'value': ''}) except Exception as exception: if getattr(settings, 'DEBUG', False): raise else: messages.error(request, _('Error adding metadata type "%(metadata_type)s" to document: %(document)s; %(exception)s') % { 'metadata_type': metadata_type, 'document': document, 'exception': ', '.join(getattr(exception, 'messages', exception))}) else: if created: messages.success(request, _('Metadata type: %(metadata_type)s successfully added to document %(document)s.') % { 'metadata_type': metadata_type, 'document': document}) else: messages.warning(request, _('Metadata type: %(metadata_type)s already present in document %(document)s.') % { 'metadata_type': metadata_type, 'document': document}) if len(documents) == 1: return HttpResponseRedirect('%s?%s' % ( reverse('metadata:metadata_edit', args=[document.pk]), urlencode({'next': next})) ) elif len(documents) > 1: return HttpResponseRedirect('%s?%s' % ( reverse('metadata:metadata_multiple_edit'), urlencode({'id_list': document_id_list, 'next': next})) ) else: form = AddMetadataForm(document_type=document.document_type) context = { 'form': form, 'next': next, } if len(documents) == 1: context['object'] = documents[0] context['title'] = ungettext( 'Add metadata types to document', 'Add metadata types to documents', len(documents) ) return render_to_response('appearance/generic_form.html', context, context_instance=RequestContext(request)) def metadata_multiple_add(request): return metadata_add(request, document_id_list=request.GET.get('id_list', [])) def metadata_remove(request, document_id=None, document_id_list=None): if document_id: document_id_list = unicode(document_id) documents = Document.objects.select_related('metadata').filter(pk__in=document_id_list.split(',')) try: Permission.check_permissions(request.user, [permission_metadata_document_remove]) except PermissionDenied: documents = AccessControlList.objects.filter_by_access(permission_metadata_document_remove, request.user, documents) if not documents: if document_id: raise Http404 else: messages.error(request, _('Must provide at least one document.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) if len(set([document.document_type.pk for document in documents])) > 1: messages.error(request, _('Only select documents of the same type.')) return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) if set(documents.values_list('metadata__value', flat=True)) == set([None]): message = ungettext( 'The selected document doesn\'t have any metadata.', 'The selected documents doesn\'t have any metadata.', len(documents) ) messages.warning(request, message) return HttpResponseRedirect(request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL))) post_action_redirect = reverse('documents:document_list_recent') next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', post_action_redirect))) metadata = {} for document in documents: document.add_as_recent_document_for_user(request.user) for item in document.metadata.all(): value = item.value if item.metadata_type in metadata: if value not in metadata[item.metadata_type]: metadata[item.metadata_type].append(value) else: metadata[item.metadata_type] = [value] if value else '' initial = [] for key, value in metadata.items(): initial.append({ 'metadata_type': key, 'value': ', '.join(value) }) formset = MetadataRemoveFormSet(initial=initial) if request.method == 'POST': formset = MetadataRemoveFormSet(request.POST) if formset.is_valid(): for document in documents: for form in formset.forms: if form.cleaned_data['update']: metadata_type = get_object_or_404(MetadataType, pk=form.cleaned_data['id']) try: document_metadata = DocumentMetadata.objects.get(document=document, metadata_type=metadata_type) document_metadata.delete() messages.success(request, _('Successfully remove metadata type "%(metadata_type)s" from document: %(document)s.') % { 'metadata_type': metadata_type, 'document': document}) except Exception as exception: messages.error(request, _('Error removing metadata type "%(metadata_type)s" from document: %(document)s; %(exception)s') % { 'metadata_type': metadata_type, 'document': document, 'exception': ', '.join(exception.messages)}) return HttpResponseRedirect(next) context = { 'form_display_mode_table': True, 'form': formset, 'next': next, } if len(documents) == 1: context['object'] = documents[0] context['title'] = ungettext( 'Remove metadata types from the document', 'Remove metadata types from the documents', len(documents) ) return render_to_response('appearance/generic_form.html', context, context_instance=RequestContext(request)) def metadata_multiple_remove(request): return metadata_remove(request, document_id_list=request.GET.get('id_list', [])) def metadata_view(request, document_id): document = get_object_or_404(Document, pk=document_id) try: Permission.check_permissions(request.user, [permission_metadata_document_view]) except PermissionDenied: AccessControlList.objects.check_access(permission_metadata_document_view, request.user, document) return render_to_response('appearance/generic_list.html', { 'title': _('Metadata for document: %s') % document, 'object_list': document.metadata.all(), 'extra_columns': [ {'name': _('Value'), 'attribute': 'value'}, {'name': _('Required'), 'attribute': encapsulate(lambda x: x.metadata_type in document.document_type.metadata.filter(required=True))} ], 'hide_link': True, 'object': document, }, context_instance=RequestContext(request)) # Setup views def setup_metadata_type_list(request): Permission.check_permissions(request.user, [permission_metadata_type_view]) context = { 'object_list': MetadataType.objects.all(), 'title': _('Metadata types'), 'hide_link': True, 'extra_columns': [ { 'name': _('Internal name'), 'attribute': 'name', }, ] } return render_to_response('appearance/generic_list.html', context, context_instance=RequestContext(request)) def setup_metadata_type_edit(request, metadatatype_id): Permission.check_permissions(request.user, [permission_metadata_type_edit]) metadata_type = get_object_or_404(MetadataType, pk=metadatatype_id) if request.method == 'POST': form = MetadataTypeForm(instance=metadata_type, data=request.POST) if form.is_valid(): try: form.save() messages.success(request, _('Metadata type edited successfully')) return HttpResponseRedirect(reverse('metadata:setup_metadata_type_list')) except Exception as exception: messages.error(request, _('Error editing metadata type; %s') % exception) pass else: form = MetadataTypeForm(instance=metadata_type) return render_to_response('appearance/generic_form.html', { 'title': _('Edit metadata type: %s') % metadata_type, 'form': form, 'object': metadata_type, }, context_instance=RequestContext(request)) def setup_metadata_type_create(request): Permission.check_permissions(request.user, [permission_metadata_type_create]) if request.method == 'POST': form = MetadataTypeForm(request.POST) if form.is_valid(): form.save() messages.success(request, _('Metadata type created successfully')) return HttpResponseRedirect(reverse('metadata:setup_metadata_type_list')) else: form = MetadataTypeForm() return render_to_response('appearance/generic_form.html', { 'title': _('Create metadata type'), 'form': form, }, context_instance=RequestContext(request)) def setup_metadata_type_delete(request, metadatatype_id): Permission.check_permissions(request.user, [permission_metadata_type_delete]) metadata_type = get_object_or_404(MetadataType, pk=metadatatype_id) post_action_redirect = reverse('metadata:setup_metadata_type_list') previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', post_action_redirect))) next = request.POST.get('next', request.GET.get('next', request.META.get('HTTP_REFERER', post_action_redirect))) if request.method == 'POST': try: metadata_type.delete() messages.success(request, _('Metadata type: %s deleted successfully.') % metadata_type) except Exception as exception: messages.error(request, _('Metadata type: %(metadata_type)s delete error: %(error)s') % { 'metadata_type': metadata_type, 'error': exception}) return HttpResponseRedirect(next) context = { 'delete_view': True, 'next': next, 'previous': previous, 'object': metadata_type, 'title': _('Are you sure you wish to delete the metadata type: %s?') % metadata_type, } return render_to_response('appearance/generic_confirm.html', context, context_instance=RequestContext(request)) class SetupDocumentTypeMetadataOptionalView(AssignRemoveView): decode_content_type = True view_permission = permission_document_type_edit left_list_title = _('Available metadata types') right_list_title = _('Metadata types assigned') def add(self, item): self.get_object().metadata.create(metadata_type=item, required=False) def get_object(self): return get_object_or_404(DocumentType, pk=self.kwargs['pk']) def left_list(self): return AssignRemoveView.generate_choices(set(MetadataType.objects.all()) - set(MetadataType.objects.filter(id__in=self.get_object().metadata.values_list('metadata_type', flat=True)))) def right_list(self): return AssignRemoveView.generate_choices(self.get_object().metadata.filter(required=False)) def remove(self, item): item.delete() def get_context_data(self, **kwargs): data = super(SetupDocumentTypeMetadataOptionalView, self).get_context_data(**kwargs) data.update({ 'object': self.get_object(), 'title': _('Optional metadata types for document type: %s') % self.get_object(), }) return data class SetupDocumentTypeMetadataRequiredView(SetupDocumentTypeMetadataOptionalView): def add(self, item): self.get_object().metadata.create(metadata_type=item, required=True) def right_list(self): return AssignRemoveView.generate_choices(self.get_object().metadata.filter(required=True)) def get_context_data(self, **kwargs): data = super(SetupDocumentTypeMetadataRequiredView, self).get_context_data(**kwargs) data.update({ 'object': self.get_object(), 'title': _('Required metadata types for document type: %s') % self.get_object(), }) return data