diff --git a/docs/releases/2.2.rst b/docs/releases/2.2.rst index 40d14637a6..fc3b1a6408 100644 --- a/docs/releases/2.2.rst +++ b/docs/releases/2.2.rst @@ -34,6 +34,7 @@ on production install to debug errors live. - Refactor add document to folder view to allow adding a documents to multiple folders at the same time. - Refactor the remove document from folder view to allow removing documents from multiple folders at the same time. - Refactor the document mailing views and add support for sending multiple documents via email at the same time. +- Refactor the document metadata views and add support for adding multiple metadata types to a document at the same time. Removals -------- diff --git a/mayan/apps/common/mixins.py b/mayan/apps/common/mixins.py index 484af98313..22edd785a2 100644 --- a/mayan/apps/common/mixins.py +++ b/mayan/apps/common/mixins.py @@ -67,6 +67,9 @@ class ExtraContextMixin(object): class MultipleInstanceActionMixin(object): + # TODO: Deprecated, replace views using this with + # MultipleObjectFormActionView or MultipleObjectConfirmActionView + model = None success_message = _('Operation performed on %(count)d object') success_message_plural = _('Operation performed on %(count)d objects') @@ -188,7 +191,7 @@ class ObjectActionMixin(object): pass def view_action(self, form=None): - count = 0 + self.action_count = 0 for instance in self.get_queryset(): try: @@ -196,11 +199,11 @@ class ObjectActionMixin(object): except PermissionDenied: pass else: - count += 1 + self.action_count += 1 messages.success( self.request, - self.get_success_message(count=count) + self.get_success_message(count=self.action_count) ) diff --git a/mayan/apps/metadata/forms.py b/mayan/apps/metadata/forms.py index e40ea1aab4..cee2aee0af 100644 --- a/mayan/apps/metadata/forms.py +++ b/mayan/apps/metadata/forms.py @@ -9,7 +9,7 @@ from .classes import MetadataLookup from .models import MetadataType -class MetadataForm(forms.Form): +class DocumentMetadataForm(forms.Form): id = forms.CharField(label=_('ID'), widget=forms.HiddenInput) name = forms.CharField( @@ -22,7 +22,7 @@ class MetadataForm(forms.Form): ) def __init__(self, *args, **kwargs): - super(MetadataForm, self).__init__(*args, **kwargs) + super(DocumentMetadataForm, self).__init__(*args, **kwargs) # Set form fields initial values if 'initial' in kwargs: @@ -104,23 +104,30 @@ class MetadataForm(forms.Form): return self.cleaned_data -MetadataFormSet = formset_factory(MetadataForm, extra=0) +DocumentMetadataFormSet = formset_factory(DocumentMetadataForm, extra=0) -class AddMetadataForm(forms.Form): - metadata_type = forms.ModelChoiceField( - queryset=MetadataType.objects.all(), label=_('Metadata type') +class DocumentAddMetadataForm(forms.Form): + metadata_type = forms.ModelMultipleChoiceField( + help_text=_('Metadata types to be added to the selected documents.'), + label=_('Metadata type'), queryset=MetadataType.objects.all(), + widget=forms.SelectMultiple( + attrs={'class': 'select2'}, + ) ) def __init__(self, *args, **kwargs): document_type = kwargs.pop('document_type') - super(AddMetadataForm, self).__init__(*args, **kwargs) - self.fields['metadata_type'].queryset = MetadataType.objects.filter( - pk__in=document_type.metadata.values_list( - 'metadata_type', flat=True + queryset = kwargs.pop( + 'queryset', MetadataType.objects.get_for_document_type( + document_type=document_type ) ) + super(DocumentAddMetadataForm, self).__init__(*args, **kwargs) + + self.fields['metadata_type'].queryset = queryset + class MetadataTypeForm(forms.ModelForm): def __init__(self, *args, **kwargs): @@ -136,14 +143,16 @@ class MetadataTypeForm(forms.ModelForm): model = MetadataType -class MetadataRemoveForm(MetadataForm): +class DocumentMetadataRemoveForm(DocumentMetadataForm): update = forms.BooleanField( initial=False, label=_('Remove'), required=False ) def __init__(self, *args, **kwargs): - super(MetadataRemoveForm, self).__init__(*args, **kwargs) + super(DocumentMetadataRemoveForm, self).__init__(*args, **kwargs) self.fields.pop('value') -MetadataRemoveFormSet = formset_factory(MetadataRemoveForm, extra=0) +DocumentMetadataRemoveFormSet = formset_factory( + DocumentMetadataRemoveForm, extra=0 +) diff --git a/mayan/apps/metadata/managers.py b/mayan/apps/metadata/managers.py index 5f2fcd3dae..bff9196770 100644 --- a/mayan/apps/metadata/managers.py +++ b/mayan/apps/metadata/managers.py @@ -1,6 +1,22 @@ +from __future__ import unicode_literals + from django.db import models class MetadataTypeManager(models.Manager): def get_by_natural_key(self, name): return self.get(name=name) + + def get_for_document(self, document): + return self.filter( + pk__in=document.metadata.values_list( + 'metadata_type', flat=True + ) + ) + + def get_for_document_type(self, document_type): + return self.filter( + pk__in=document_type.metadata.values_list( + 'metadata_type', flat=True + ) + ) diff --git a/mayan/apps/metadata/urls.py b/mayan/apps/metadata/urls.py index 646d2123da..e317927bd1 100644 --- a/mayan/apps/metadata/urls.py +++ b/mayan/apps/metadata/urls.py @@ -10,37 +10,40 @@ from .api_views import ( APIMetadataTypeView ) from .views import ( - DocumentMetadataListView, MetadataTypeCreateView, MetadataTypeDeleteView, - MetadataTypeEditView, MetadataTypeListView, - SetupDocumentTypeMetadataOptionalView, - SetupDocumentTypeMetadataRequiredView, metadata_add, metadata_edit, - metadata_multiple_add, metadata_multiple_edit, metadata_multiple_remove, - metadata_remove + DocumentMetadataAddView, DocumentMetadataEditView, + DocumentMetadataListView, DocumentMetadataRemoveView, + MetadataTypeCreateView, MetadataTypeDeleteView, MetadataTypeEditView, + MetadataTypeListView, SetupDocumentTypeMetadataOptionalView, + SetupDocumentTypeMetadataRequiredView ) urlpatterns = [ url( - r'^(?P\d+)/edit/$', metadata_edit, name='metadata_edit' + r'^(?P\d+)/edit/$', DocumentMetadataEditView.as_view(), + name='metadata_edit' + ), + url( + r'^multiple/edit/$', DocumentMetadataEditView.as_view(), + name='metadata_multiple_edit' ), url( r'^(?P\d+)/view/$', DocumentMetadataListView.as_view(), name='metadata_view' ), url( - r'^multiple/edit/$', metadata_multiple_edit, - name='metadata_multiple_edit' + r'^(?P\d+)/add/$', DocumentMetadataAddView.as_view(), + name='metadata_add' ), - url(r'^(?P\d+)/add/$', metadata_add, name='metadata_add'), url( - r'^multiple/add/$', metadata_multiple_add, + r'^multiple/add/$', DocumentMetadataAddView.as_view(), name='metadata_multiple_add' ), url( - r'^(?P\d+)/remove/$', metadata_remove, + r'^(?P\d+)/remove/$', DocumentMetadataRemoveView.as_view(), name='metadata_remove' ), url( - r'^multiple/remove/$', metadata_multiple_remove, + r'^multiple/remove/$', DocumentMetadataRemoveView.as_view(), name='metadata_multiple_remove' ), diff --git a/mayan/apps/metadata/views.py b/mayan/apps/metadata/views.py index c5cc970e74..49c1d9ba57 100644 --- a/mayan/apps/metadata/views.py +++ b/mayan/apps/metadata/views.py @@ -2,18 +2,17 @@ from __future__ import absolute_import, unicode_literals from django.conf import settings from django.contrib import messages -from django.core.exceptions import PermissionDenied, ValidationError +from django.core.exceptions import ValidationError from django.core.urlresolvers import reverse, reverse_lazy from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext +from django.shortcuts import get_object_or_404 from django.utils.http import urlencode from django.utils.translation import ugettext_lazy as _, ungettext from acls.models import AccessControlList from common.generics import ( - AssignRemoveView, SingleObjectCreateView, SingleObjectDeleteView, - SingleObjectEditView, SingleObjectListView + AssignRemoveView, MultipleObjectFormActionView, SingleObjectCreateView, + SingleObjectDeleteView, SingleObjectEditView, SingleObjectListView ) from documents.models import Document, DocumentType from documents.permissions import ( @@ -22,7 +21,8 @@ from documents.permissions import ( from .api import save_metadata_list from .forms import ( - AddMetadataForm, MetadataFormSet, MetadataRemoveFormSet, MetadataTypeForm + DocumentAddMetadataForm, DocumentMetadataFormSet, + DocumentMetadataRemoveFormSet, MetadataTypeForm ) from .models import DocumentMetadata, MetadataType from .permissions import ( @@ -33,432 +33,269 @@ from .permissions import ( ) -def metadata_edit(request, document_id=None, document_id_list=None): - if document_id: - documents = Document.objects.filter(pk=document_id) - if not documents: - raise Document.DoesNotExist - elif document_id_list: - documents = Document.objects.filter(pk__in=document_id_list) - - documents = AccessControlList.objects.filter_by_access( - permission_metadata_document_edit, request.user, queryset=documents +class DocumentMetadataAddView(MultipleObjectFormActionView): + form_class = DocumentAddMetadataForm + model = Document + object_permission = permission_metadata_document_add + success_message = _('Metadata add request performed on %(count)d document') + success_message_plural = _( + 'Metadata add request performed on %(count)d documents' ) - if not documents: - if document_id: - raise PermissionDenied - else: - messages.error(request, _('Must provide at least one document.')) - return HttpResponseRedirect( - request.META.get( - 'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL) - ) - ) + def dispatch(self, request, *args, **kwargs): + result = super(DocumentMetadataAddView, self).dispatch(request, *args, **kwargs) - 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) - ) - ) + queryset = self.get_queryset() - if set(documents.values_list('metadata__metadata_type', 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) - ) - ) + for document in queryset: + 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) - ) - ) - - metadata_dict = {} - initial = [] - - for document in documents: - document.add_as_recent_document_for_user(request.user) - - for document_metadata in document.metadata.all(): - metadata_dict.setdefault(document_metadata.metadata_type, set()) - - if document_metadata.value: - metadata_dict[ - document_metadata.metadata_type - ].add(document_metadata.value) - - for key, value in metadata_dict.items(): - initial.append({ - 'document_type': document.document_type, - 'metadata_type': key, - 'value': ', '.join(value) if value else '', - }) - - 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) - - for error in errors: - if settings.DEBUG: - raise - else: - if isinstance(error, ValidationError): - exception_message = ', '.join(error.messages) - else: - exception_message = unicode(error) - - messages.error( - request, _( - 'Error editing metadata for document: ' - '%(document)s; %(exception)s.' - ) % { - 'document': document, - 'exception': exception_message - } - ) - else: - messages.success( - request, - _( - 'Metadata for document %s edited successfully.' - ) % document - ) - - return HttpResponseRedirect(next) - else: - formset = MetadataFormSet(initial=initial) - - context = { - 'form_display_mode_table': True, - 'form': formset, - 'next': next, - } - - if documents.count() == 1: - context['object'] = documents.first() - - context['title'] = ungettext( - 'Edit document metadata', - 'Edit documents metadata', - documents.count() - ) - - 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', request.POST.get('id_list', '') - ).split(',') - ) - - -def metadata_add(request, document_id=None, document_id_list=None): - if document_id: - documents = Document.objects.filter(pk=document_id) - if not documents: - raise Document.DoesNotExist - elif document_id_list: - documents = Document.objects.select_related('document_type').filter(pk__in=document_id_list) - if len(set([document.document_type.pk for document in documents])) > 1: + if len(set([document.document_type.pk for document in queryset])) > 1: messages.error( - request, _('Only select documents of the same type.') + request, _('Selected documents must be of the same type.') ) + return HttpResponseRedirect(self.previous_url) + + return result + + def form_valid(self, form): + result = super(DocumentMetadataAddView, self).form_valid(form=form) + + queryset = self.get_queryset() + + if self.action_count == 1: return HttpResponseRedirect( - request.META.get( - 'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL) - ) + reverse('metadata:metadata_edit', args=(queryset.first().pk,)), ) - - documents = AccessControlList.objects.filter_by_access( - permission_metadata_document_add, request.user, queryset=documents - ) - - if not documents: - if document_id: - raise PermissionDenied - else: - messages.error(request, _('Must provide at least one document.')) + elif self.action_count > 1: 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, - 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 documents.count() == 1: - return HttpResponseRedirect('%s?%s' % ( - reverse('metadata:metadata_edit', args=(document.pk,)), - urlencode({'next': next})) - ) - elif documents.count() > 1: - return HttpResponseRedirect('%s?%s' % ( + '%s?%s' % ( reverse('metadata:metadata_multiple_edit'), - urlencode({'id_list': ','.join(document_id_list), 'next': next})) - ) - - else: - form = AddMetadataForm(document_type=document.document_type) - - context = { - 'form': form, - 'next': next, - } - - if documents.count() == 1: - context['object'] = documents.first() - - context['title'] = ungettext( - 'Add metadata types to document', - 'Add metadata types to documents', - documents.count() - ) - - 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', request.POST.get('id_list', '') - ).split(',') - ) - - -def metadata_remove(request, document_id=None, document_id_list=None): - if document_id: - documents = Document.objects.filter(pk=document_id) - if not documents: - raise Document.DoesNotExist - elif document_id_list: - documents = Document.objects.filter(pk__in=document_id_list) - - documents = AccessControlList.objects.filter_by_access( - permission_metadata_document_remove, request.user, queryset=documents - ) - - if not documents: - if document_id: - raise PermissionDenied - else: - messages.error(request, _('Must provide at least one document.')) - return HttpResponseRedirect( - request.META.get( - 'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL) + urlencode( + { + 'id_list': ','.join( + queryset.value_list('pk', flat=True) + ) + } + ) ) ) - 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) + return result + + def get_extra_context(self): + queryset = self.get_queryset() + + result = { + 'submit_icon': 'fa fa-plus', + 'submit_label': _('Add'), + 'title': ungettext( + 'Add metadata types to document', + 'Add metadata types to documents', + queryset.count() ) - ) + } - 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) + if queryset.count() == 1: + result.update( + { + 'object': queryset.first(), + 'title': _( + 'Add metadata types to document: %s' + ) % queryset.first() + } ) - ) - post_action_redirect = reverse('documents:document_list_recent') + return result - next = request.POST.get( - 'next', request.GET.get( - 'next', request.META.get('HTTP_REFERER', post_action_redirect) - ) - ) + def get_form_extra_kwargs(self): + queryset = self.get_queryset() + result = { + 'document_type': queryset.first().document_type, + } - 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( - { - 'document_type': documents[0].document_type, - 'metadata_type': key, - 'value': ', '.join(value) - } - ) - - 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'] + if queryset.count() == 1: + result.update( + { + 'queryset': MetadataType.objects.exclude( + pk__in=MetadataType.objects.get_for_document( + document=queryset.first() ) - 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) - else: - formset = MetadataRemoveFormSet(initial=initial) + return result - context = { - 'form_display_mode_table': True, - 'form': formset, - 'next': next, - } + def object_action(self, form, instance): + for metadata_type in form.cleaned_data['metadata_type']: + document_metadata, created = DocumentMetadata.objects.get_or_create( + document=instance, + metadata_type=metadata_type, + defaults={'value': ''} + ) - if documents.count() == 1: - context['object'] = documents.first() + if created: + messages.success( + self.request, + _( + 'Metadata type: %(metadata_type)s ' + 'successfully added to document %(document)s.' + ) % { + 'metadata_type': metadata_type, + 'document': instance + } + ) + else: + messages.warning( + self.request, _( + 'Metadata type: %(metadata_type)s already ' + 'present in document %(document)s.' + ) % { + 'metadata_type': metadata_type, + 'document': instance + } + ) - context['title'] = ungettext( - 'Remove metadata types from the document', - 'Remove metadata types from the documents', - documents.count() + +class DocumentMetadataEditView(MultipleObjectFormActionView): + form_class = DocumentMetadataFormSet + model = Document + object_permission = permission_metadata_document_edit + success_message = _( + 'Metadata edit request performed on %(count)d document' + ) + success_message_plural = _( + 'Metadata edit request performed on %(count)d documents' ) - return render_to_response( - 'appearance/generic_form.html', context, - context_instance=RequestContext(request) - ) + def dispatch(self, request, *args, **kwargs): + result = super(DocumentMetadataEditView, self).dispatch(request, *args, **kwargs) + queryset = self.get_queryset() -def metadata_multiple_remove(request): - return metadata_remove( - request, document_id_list=request.GET.get( - 'id_list', request.POST.get('id_list', '') - ).split(',') - ) + for document in queryset: + document.add_as_recent_document_for_user(request.user) + + if len(set([document.document_type.pk for document in queryset])) > 1: + messages.error( + request, _('Selected documents must be of the same type.') + ) + return HttpResponseRedirect(self.previous_url) + + return result + + def form_valid(self, form): + result = super(DocumentMetadataEditView, self).form_valid(form=form) + + queryset = self.get_queryset() + + if self.action_count == 1: + return HttpResponseRedirect( + reverse('metadata:metadata_edit', args=(queryset.first().pk,)), + ) + elif self.action_count > 1: + return HttpResponseRedirect( + '%s?%s' % ( + reverse('metadata:metadata_multiple_edit'), + urlencode( + { + 'id_list': ','.join( + queryset.value_list('pk', flat=True) + ) + } + ) + ) + ) + + return result + + def get_extra_context(self): + queryset = self.get_queryset() + + result = { + 'form_display_mode_table': True, + 'submit_icon': 'fa fa-pencil', + 'submit_label': _('Edit'), + 'title': ungettext( + 'Edit document metadata', + 'Edit documents metadata', + queryset.count() + ) + } + + if queryset.count() == 1: + result.update( + { + 'object': queryset.first(), + 'title': _( + 'Edit metadata for document: %s' + ) % queryset.first() + } + ) + + return result + + def get_initial(self): + queryset = self.get_queryset() + + metadata_dict = {} + initial = [] + + for document in queryset: + document.add_as_recent_document_for_user(self.request.user) + + for document_metadata in document.metadata.all(): + metadata_dict.setdefault(document_metadata.metadata_type, set()) + + if document_metadata.value: + metadata_dict[ + document_metadata.metadata_type + ].add(document_metadata.value) + + for key, value in metadata_dict.items(): + initial.append({ + 'document_type': document.document_type, + 'metadata_type': key, + 'value': ', '.join(value) if value else '', + }) + + return initial + + def object_action(self, form, instance): + errors = [] + for form in form.forms: + if form.cleaned_data['update']: + try: + save_metadata_list([form.cleaned_data], instance) + except Exception as exception: + errors.append(exception) + + for error in errors: + if settings.DEBUG: + raise + else: + if isinstance(error, ValidationError): + exception_message = ', '.join(error.messages) + else: + exception_message = unicode(error) + + messages.error( + self.request, _( + 'Error editing metadata for document: ' + '%(document)s; %(exception)s.' + ) % { + 'document': instance, + 'exception': exception_message + } + ) + else: + messages.success( + self.request, + _( + 'Metadata for document %s edited successfully.' + ) % instance + ) class DocumentMetadataListView(SingleObjectListView): @@ -487,6 +324,143 @@ class DocumentMetadataListView(SingleObjectListView): return self.get_document().metadata.all() +class DocumentMetadataRemoveView(MultipleObjectFormActionView): + form_class = DocumentMetadataRemoveFormSet + model = Document + object_permission = permission_metadata_document_remove + success_message = _( + 'Metadata remove request performed on %(count)d document' + ) + success_message_plural = _( + 'Metadata remove request performed on %(count)d documents' + ) + + def dispatch(self, request, *args, **kwargs): + result = super(DocumentMetadataRemoveView, self).dispatch(request, *args, **kwargs) + + queryset = self.get_queryset() + + for document in queryset: + document.add_as_recent_document_for_user(request.user) + + if len(set([document.document_type.pk for document in queryset])) > 1: + messages.error( + request, _('Selected documents must be of the same type.') + ) + return HttpResponseRedirect(self.previous_url) + + return result + + def form_valid(self, form): + result = super(DocumentMetadataRemoveView, self).form_valid(form=form) + + queryset = self.get_queryset() + + if self.action_count == 1: + return HttpResponseRedirect( + reverse('metadata:metadata_edit', args=(queryset.first().pk,)), + ) + elif self.action_count > 1: + return HttpResponseRedirect( + '%s?%s' % ( + reverse('metadata:metadata_multiple_edit'), + urlencode( + { + 'id_list': ','.join( + queryset.value_list('pk', flat=True) + ) + } + ) + ) + ) + + return result + + def get_extra_context(self): + queryset = self.get_queryset() + + result = { + 'form_display_mode_table': True, + 'submit_icon': 'fa fa-minus', + 'submit_label': _('Remove'), + 'title': ungettext( + 'Remove metadata types from the document', + 'Remove metadata types from the documents', + queryset.count() + ) + } + + if queryset.count() == 1: + result.update( + { + 'object': queryset.first(), + 'title': _( + 'Remove metadata types from the document: %s' + ) % queryset.first() + } + ) + + return result + + def get_initial(self): + queryset = self.get_queryset() + + metadata = {} + for document in queryset: + document.add_as_recent_document_for_user(self.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( + { + 'document_type': queryset.first().document_type, + 'metadata_type': key, + 'value': ', '.join(value) + } + ) + return initial + + def object_action(self, form, instance): + for form in form.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=instance, metadata_type=metadata_type + ) + document_metadata.delete() + messages.success( + self.request, + _( + 'Successfully remove metadata type "%(metadata_type)s" from document: %(document)s.' + ) % { + 'metadata_type': metadata_type, + 'document': instance + } + ) + except Exception as exception: + messages.error( + self.request, + _( + 'Error removing metadata type "%(metadata_type)s" from document: %(document)s; %(exception)s' + ) % { + 'metadata_type': metadata_type, + 'document': instance, + 'exception': ', '.join(exception.messages) + } + ) + + # Setup views class MetadataTypeCreateView(SingleObjectCreateView): extra_context = {'title': _('Create metadata type')} diff --git a/mayan/apps/sources/wizards.py b/mayan/apps/sources/wizards.py index e85d132a28..fb8c58b893 100644 --- a/mayan/apps/sources/wizards.py +++ b/mayan/apps/sources/wizards.py @@ -10,7 +10,7 @@ from formtools.wizard.views import SessionWizardView from common.mixins import ViewPermissionCheckMixin from documents.forms import DocumentTypeSelectForm -from metadata.forms import MetadataFormSet +from metadata.forms import DocumentMetadataFormSet from tags.forms import TagMultipleSelectionForm from .literals import STEP_DOCUMENT_TYPE, STEP_METADATA, STEP_TAGS @@ -33,10 +33,13 @@ def has_metadata_types(wizard): class DocumentCreateWizard(ViewPermissionCheckMixin, SessionWizardView): condition_dict = {STEP_METADATA: has_metadata_types} extra_context = {} - form_list = (DocumentTypeSelectForm, MetadataFormSet, TagMultipleSelectionForm) + form_list = ( + DocumentTypeSelectForm, DocumentMetadataFormSet, + TagMultipleSelectionForm + ) form_titles = { DocumentTypeSelectForm: _('Step 1 of 3: Select document type'), - MetadataFormSet: _('Step 2 of 3: Enter document metadata'), + DocumentMetadataFormSet: _('Step 2 of 3: Enter document metadata'), TagMultipleSelectionForm: _('Step 3 of 3: Select tags'), } template_name = 'appearance/generic_wizard.html'