diff --git a/docs/releases/2.2.rst b/docs/releases/2.2.rst index 46cd1190b7..f273d0109d 100644 --- a/docs/releases/2.2.rst +++ b/docs/releases/2.2.rst @@ -25,6 +25,7 @@ the user links - Tags are alphabetically ordered by label - Stop loading theme fonts from the web - Add support for attaching multiple tags to single or multiple documents. +- Refactor the workflow for removing tags from single and multiple documents. Removals -------- diff --git a/mayan/apps/sources/wizards.py b/mayan/apps/sources/wizards.py index d34195d39c..e85d132a28 100644 --- a/mayan/apps/sources/wizards.py +++ b/mayan/apps/sources/wizards.py @@ -91,7 +91,10 @@ class DocumentCreateWizard(ViewPermissionCheckMixin, SessionWizardView): return {'user': self.request.user} if step == STEP_TAGS: - return {'user': self.request.user} + return { + 'help_text': _('Tags to be attached.'), + 'user': self.request.user + } return {} diff --git a/mayan/apps/tags/apps.py b/mayan/apps/tags/apps.py index 05a2c7fcc4..c84d8306d9 100644 --- a/mayan/apps/tags/apps.py +++ b/mayan/apps/tags/apps.py @@ -127,10 +127,6 @@ class TagsApp(MayanAppConfig): menu_multi_item.bind_links( links=(link_tag_multiple_delete,), sources=(Tag,) ) - menu_multi_item.bind_links( - links=(link_single_document_multiple_tag_remove,), - sources=(DocumentTag,) - ) menu_object.bind_links( links=( link_tag_tagged_item_list, link_tag_edit, link_acl_list, @@ -139,7 +135,7 @@ class TagsApp(MayanAppConfig): sources=(Tag,) ) menu_sidebar.bind_links( - links=(link_tag_attach,), + links=(link_tag_attach, link_single_document_multiple_tag_remove), sources=( 'tags:document_tags', 'tags:tag_remove', 'tags:tag_multiple_remove', 'tags:tag_attach' diff --git a/mayan/apps/tags/forms.py b/mayan/apps/tags/forms.py index e61d7bc05f..6e8d8b9a86 100644 --- a/mayan/apps/tags/forms.py +++ b/mayan/apps/tags/forms.py @@ -14,33 +14,21 @@ from .widgets import TagFormWidget logger = logging.getLogger(__name__) -class TagListForm(forms.Form): - def __init__(self, *args, **kwargs): - user = kwargs.pop('user', None) - logger.debug('user: %s', user) - super(TagListForm, self).__init__(*args, **kwargs) - - queryset = AccessControlList.objects.filter_by_access( - permission_tag_view, user, queryset=Tag.objects.all() - ) - - self.fields['tag'] = forms.ModelChoiceField( - queryset=queryset, label=_('Tags') - ) - - class TagMultipleSelectionForm(forms.Form): def __init__(self, *args, **kwargs): + help_text = kwargs.pop('help_text', None) + queryset = kwargs.pop('queryset', Tag.objects.all()) user = kwargs.pop('user', None) + logger.debug('user: %s', user) super(TagMultipleSelectionForm, self).__init__(*args, **kwargs) queryset = AccessControlList.objects.filter_by_access( - permission_tag_view, user, queryset=Tag.objects.all() + permission_tag_view, user, queryset=queryset ) self.fields['tags'] = forms.ModelMultipleChoiceField( - label=_('Tags'), help_text=_('Tags to attach to the document.'), + label=_('Tags'), help_text=help_text, queryset=queryset, required=False, widget=TagFormWidget(attrs={'class': 'select2'}, queryset=queryset) ) diff --git a/mayan/apps/tags/urls.py b/mayan/apps/tags/urls.py index b6268cb68e..bc0f7bd63e 100644 --- a/mayan/apps/tags/urls.py +++ b/mayan/apps/tags/urls.py @@ -8,9 +8,8 @@ from .api_views import ( ) from .views import ( DocumentTagListView, TagAttachActionView, TagCreateView, - TagDeleteActionView, TagEditView, TagListView, TagTaggedItemListView, - multiple_documents_selection_tag_remove, - single_document_multiple_tag_remove + TagDeleteActionView, TagEditView, TagListView, TagRemoveActionView, + TagTaggedItemListView ) urlpatterns = [ @@ -31,13 +30,13 @@ urlpatterns = [ ), url( - r'^multiple/remove/document/(?P\d+)/$', - single_document_multiple_tag_remove, + r'^multiple/remove/document/(?P\d+)/$', + TagRemoveActionView.as_view(), name='single_document_multiple_tag_remove' ), url( r'^multiple/remove/document/multiple/$', - multiple_documents_selection_tag_remove, + TagRemoveActionView.as_view(), name='multiple_documents_selection_tag_remove' ), diff --git a/mayan/apps/tags/views.py b/mayan/apps/tags/views.py index b4edc31408..f4debb2e14 100644 --- a/mayan/apps/tags/views.py +++ b/mayan/apps/tags/views.py @@ -3,11 +3,8 @@ from __future__ import absolute_import, unicode_literals import logging from django.contrib import messages -from django.core.urlresolvers import reverse, reverse_lazy -from django.conf import settings -from django.http import HttpResponseRedirect -from django.shortcuts import get_object_or_404, render_to_response -from django.template import RequestContext +from django.core.urlresolvers import reverse_lazy +from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _, ungettext from acls.models import AccessControlList @@ -19,7 +16,7 @@ from documents.models import Document from documents.views import DocumentListView from documents.permissions import permission_document_view -from .forms import TagMultipleSelectionForm, TagListForm +from .forms import TagMultipleSelectionForm from .models import Tag from .permissions import ( permission_tag_attach, permission_tag_create, permission_tag_delete, @@ -44,8 +41,8 @@ class TagAttachActionView(MultipleObjectFormActionView): result = { 'submit_label': _('Attach'), 'title': ungettext( - 'Attach tag to document', - 'Attach tag to documents', + 'Attach tags to document', + 'Attach tags to documents', queryset.count() ) } @@ -54,14 +51,27 @@ class TagAttachActionView(MultipleObjectFormActionView): result.update( { 'object': queryset.first(), - 'title': _('Attach tag to document: %s') % queryset.first() + 'title': _('Attach tags to document: %s') % queryset.first() } ) return result def get_form_extra_kwargs(self): - return {'user': self.request.user} + queryset = self.get_queryset() + result = { + 'help_text': _('Tags to be attached.'), + 'user': self.request.user + } + + if queryset.count() == 1: + result.update( + { + 'queryset': Tag.objects.exclude(pk__in=queryset.first().tags.all()) + } + ) + + return result def object_action(self, form, instance): attached_tags = instance.attached_tags() @@ -218,138 +228,78 @@ class DocumentTagListView(TagListView): return self.document.attached_tags().all() -def tag_remove(request, document_id=None, document_id_list=None, tag_id=None, tag_id_list=None): - if document_id: - documents = Document.objects.filter(pk=document_id) - elif document_id_list: - documents = Document.objects.filter(pk__in=document_id_list) +class TagRemoveActionView(MultipleObjectFormActionView): + form_class = TagMultipleSelectionForm + model = Document + object_permission = permission_tag_remove + success_message = _('Tag remove request performed on %(count)d document') + success_message_plural = _( + 'Tag remove request performed on %(count)d documents' + ) - if not documents: - messages.error( - request, _('Must provide at least one tagged document.') - ) - return HttpResponseRedirect( - request.META.get( - 'HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL) + def get_extra_context(self): + queryset = self.get_queryset() + + result = { + 'submit_label': _('Remove'), + 'title': ungettext( + 'Remove tags from document', + 'Remove tags from documents', + queryset.count() ) - ) + } - documents = AccessControlList.objects.filter_by_access( - permission_tag_remove, request.user, documents - ) - - post_action_redirect = None - - previous = request.POST.get('previous', request.GET.get('previous', request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) - next = request.POST.get('next', request.GET.get('next', post_action_redirect if post_action_redirect else request.META.get('HTTP_REFERER', reverse(settings.LOGIN_REDIRECT_URL)))) - - context = { - 'previous': previous, - 'next': next, - } - - template = 'appearance/generic_confirm.html' - if tag_id: - tags = Tag.objects.filter(pk=tag_id) - elif tag_id_list: - tags = Tag.objects.filter(pk__in=tag_id_list) - else: - template = 'appearance/generic_form.html' - - if request.method == 'POST': - form = TagListForm(request.POST, user=request.user) - if form.is_valid(): - tags = Tag.objects.filter(pk=form.cleaned_data['tag'].pk) - else: - if not tag_id and not tag_id_list: - form = TagListForm(user=request.user) - tags = Tag.objects.none() - - context['form'] = form - if len(documents) == 1: - context['object'] = documents.first() - context['title'] = _( - 'Remove tag from document: %s.' - ) % ', '.join([unicode(d) for d in documents]) - elif len(documents) > 1: - context['title'] = _( - 'Remove tag from documents: %s.' - ) % ', '.join([unicode(d) for d in documents]) - - if tags: - if tags.count() == 1: - if documents.count() == 1: - context['object'] = documents.first() - context['title'] = _( - 'Remove the tag "%(tag)s" from the document: %(document)s?' - ) % { - 'tag': ', '.join([unicode(d) for d in tags]), - 'document': ', '.join([unicode(d) for d in documents]) + if queryset.count() == 1: + result.update( + { + 'object': queryset.first(), + 'title': _('Remove tags from document: %s') % queryset.first() } + ) + + return result + + def get_form_extra_kwargs(self): + queryset = self.get_queryset() + result = { + 'help_text': _('Tags to be removed.'), + 'user': self.request.user + } + + if queryset.count() == 1: + result.update( + { + 'queryset': queryset.first().tags.all() + } + ) + + return result + + def object_action(self, form, instance): + attached_tags = instance.attached_tags() + + for tag in form.cleaned_data['tags']: + AccessControlList.objects.check_access( + obj=tag, permissions=permission_tag_view, + user=self.request.user + ) + + if tag not in attached_tags: + messages.warning( + self.request, _( + 'Document "%(document)s" wasn\'t tagged as "%(tag)s' + ) % { + 'document': instance, 'tag': tag + } + ) else: - context['title'] = _( - 'Remove the tag "%(tag)s" from the documents: %(documents)s?' - ) % { - 'tag': ', '.join([unicode(d) for d in tags]), - 'documents': ', '.join([unicode(d) for d in documents]) - } - elif tags.count() > 1: - if documents.count() == 1: - context['object'] = documents.first() - context['title'] = _( - 'Remove the tags: %(tags)s from the document: %(document)s?' - ) % { - 'tags': ', '.join([unicode(d) for d in tags]), - 'document': ', '.join([unicode(d) for d in documents]) - } - else: - context['title'] = _( - 'Remove the tags %(tags)s from the documents: %(documents)s?' - ) % { - 'tags': ', '.join([unicode(d) for d in tags]), - 'documents': ', '.join([unicode(d) for d in documents]) - } - - if request.method == 'POST': - for document in documents: - for tag in tags: - if tag not in document.attached_tags().all(): - messages.warning( - request, _( - 'Document "%(document)s" wasn\'t tagged as "%(tag)s"' - ) % { - 'document': document, 'tag': tag - } - ) - else: - tag.documents.remove(document) - messages.success( - request, _( - 'Tag "%(tag)s" removed successfully from document "%(document)s".' - ) % { - 'document': document, 'tag': tag - } - ) - - return HttpResponseRedirect(next) - else: - return render_to_response( - template, context, context_instance=RequestContext(request) - ) - - -def single_document_multiple_tag_remove(request, document_id): - return tag_remove( - request, document_id=document_id, - tag_id_list=request.GET.get( - 'id_list', request.POST.get('id_list', '') - ).split(',') - ) - - -def multiple_documents_selection_tag_remove(request): - return tag_remove( - request, document_id_list=request.GET.get( - 'id_list', request.POST.get('id_list', '') - ).split(',') - ) + tag.documents.remove(instance) + messages.success( + self.request, + _( + 'Tag "%(tag)s" removed successfully from document ' + '"%(document)s".' + ) % { + 'document': instance, 'tag': tag + } + )