diff --git a/mayan/apps/appearance/static/appearance/js/mayan_app.js b/mayan/apps/appearance/static/appearance/js/mayan_app.js index b77e94859e..b3df0c8e14 100644 --- a/mayan/apps/appearance/static/appearance/js/mayan_app.js +++ b/mayan/apps/appearance/static/appearance/js/mayan_app.js @@ -8,10 +8,10 @@ class MayanApp { ajaxMenusOptions: [] } - this.ajaxSpinnerSeletor = '#ajax-spinner'; this.ajaxExecuting = false; this.ajaxMenusOptions = options.ajaxMenusOptions; this.ajaxMenuHashes = {}; + this.ajaxSpinnerSeletor = '#ajax-spinner'; this.window = $(window); } @@ -81,22 +81,6 @@ class MayanApp { }); } - static tagSelectionTemplate (tag, container) { - var $tag = $( - ' ' + tag.text + '' - ); - container[0].style.background = tag.element.dataset.color; - return $tag; - } - - static tagResultTemplate (tag) { - if (!tag.element) { return ''; } - var $tag = $( - ' ' + tag.text + '' - ); - return $tag; - } - static updateNavbarState () { var uri = new URI(window.location.hash); var uriFragment = uri.fragment(); @@ -445,12 +429,6 @@ class MayanApp { dropdownAutoWidth: true, width: '100%' }); - - $('.select2-tags').select2({ - templateSelection: MayanApp.tagSelectionTemplate, - templateResult: MayanApp.tagResultTemplate, - width: '100%' - }); } resizeFullHeight () { diff --git a/mayan/apps/tags/admin.py b/mayan/apps/tags/admin.py index 7d0f1a007a..a703ba2e03 100644 --- a/mayan/apps/tags/admin.py +++ b/mayan/apps/tags/admin.py @@ -8,4 +8,4 @@ from .models import Tag @admin.register(Tag) class TagAdmin(admin.ModelAdmin): filter_horizontal = ('documents',) - list_display = ('label', 'color', 'get_preview_widget') + list_display = ('label', 'color') diff --git a/mayan/apps/tags/api_views.py b/mayan/apps/tags/api_views.py index e4d109c08f..7018c54599 100644 --- a/mayan/apps/tags/api_views.py +++ b/mayan/apps/tags/api_views.py @@ -1,14 +1,20 @@ from __future__ import absolute_import, unicode_literals -from rest_framework import generics +from rest_framework import generics, status +from rest_framework.decorators import action from rest_framework.exceptions import ValidationError from rest_framework.response import Response -from mayan.apps.common.mixins import ExternalObjectViewMixin +from mayan.apps.common.mixins import ExternalObjectMixin +from mayan.apps.documents.api_views import DocumentViewSet from mayan.apps.documents.models import Document from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.serializers import DocumentSerializer from mayan.apps.rest_api.filters import MayanObjectPermissionsFilter +from mayan.apps.rest_api.generics import ( + ListAPIView, ListCreateAPIView, RetrieveDestroyAPIView, + RetrieveUpdateDestroyAPIView +) from mayan.apps.rest_api.permissions import MayanPermission from .models import Tag @@ -17,116 +23,295 @@ from .permissions import ( permission_tag_edit, permission_tag_remove, permission_tag_view ) from .serializers import ( - DocumentTagSerializer, TagSerializer, WritableTagSerializer + DocumentTagAttachSerializer, DocumentTagSerializer, TagAttachSerializer, + TagRemoveSerializer, TagSerializer, ) -class APITagListView(generics.ListCreateAPIView): + +from django.conf.urls import url, include +from django.contrib.auth.models import User + +from rest_framework import routers, serializers, viewsets +from rest_framework.decorators import detail_route +from rest_framework.response import Response + +from drf_yasg.utils import swagger_auto_schema + + +class TagViewSet(viewsets.ModelViewSet): + filter_backends = (MayanObjectPermissionsFilter,) + lookup_field = 'pk' + lookup_url_kwarg='tag_id' + permission_classes = (MayanPermission,) + queryset = Tag.objects.all() + serializer_class = TagSerializer + + + #@swagger_auto_schema(operation_description='GET /articles/today/') + @swagger_auto_schema( + operation_description="partial_update description override", responses={200: TagAttachSerializer} + ) + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='tag_id', + methods=('post',), serializer_class=TagAttachSerializer, + url_name='document-attach', url_path='attach' + ) + def attach(self, request, *args, **kwargs): + #print '!!! attach', args, kwargs#, self.context + #return Response({}) + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + #print '((((((((', serializer.validated_data + #self.perform_attach(serializer=serializer) + serializer.attach(instance=self.get_object()) + headers = self.get_success_headers(data=serializer.data) + return Response( + serializer.data, status=status.HTTP_200_OK, headers=headers + ) + + #def perform_attach(self, serializer): + # #print '!!!!', serializer + # serializer.attach(instance=self.get_object()) + + #def get_success_headers(self, data): + # try: + # return {'Location': str(data[api_settings.URL_FIELD_NAME])} + # except (TypeError, KeyError): + # return {} + + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='tag_id', + url_name='document-list', url_path='documents' + ) + def document_list(self, request, *args, **kwargs): + queryset = self.get_object().documents.all() + + #TODO:Filter queryset + #queryset = self.filter_queryset(self.get_queryset()) + + page = self.paginate_queryset(queryset) + if page is not None: + #serializer = self.get_serializer(page, many=True) + serializer = DocumentSerializer(page, many=True, context={'request': request}) + return self.get_paginated_response(serializer.data) + + #serializer = self.get_serializer(queryset, many=True) + serializer = DocumentSerializer(queryset, many=True, context={'request': request}) + return Response(serializer.data) + + + #serializer = DocumentSerializer( + # instance=, many=True, + # context={'request': request} + #) + #return Response(serializer.data) + + + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='tag_id', + methods=('post',), serializer_class=TagRemoveSerializer, + url_name='document-remove', url_path='remove' + ) + def remove(self, request, *args, **kwargs): + #print '!!! attach', args, kwargs#, self.context + #return Response({}) + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + #print '((((((((', serializer.validated_data + #self.perform_attach(serializer=serializer) + serializer.remove(instance=self.get_object()) + headers = self.get_success_headers(data=serializer.data) + return Response( + serializer.data, status=status.HTTP_200_OK, headers=headers + ) + + #def get_serializer_class(self, *args, **kwargs): + # #if self.action == 'attach': + # print '!!!!get_serializer_class', args, kwargs + # return TagAttachSerializer + + + +class DocumentTagViewSet(ExternalObjectMixin, viewsets.ReadOnlyModelViewSet): + external_object_class = Document + external_object_pk_url_kwarg = 'document_id' + external_object_permission = permission_tag_view + lookup_field = 'pk' + object_permission = { + 'list': permission_document_view, + 'retrieve': permission_document_view + } + serializer_class = DocumentTagSerializer + + @action( + detail=True, lookup_field='pk', lookup_url_kwarg='document_id', + methods=('post',), serializer_class=DocumentTagAttachSerializer, + url_name='tag-attach', url_path='attach' + ) + def attach(self, request, *args, **kwargs): + return Response({}) + + + ''' + serializer = DocumentSerializer( + instance=self.get_object().documents.all(), many=True, + context={'request': request} + ) + return Response(serializer.data) + ''' + + def get_document(self): + return self.get_external_object() + + def get_queryset(self): + #return self.get_document().get_tags(user=self.request.user).all() + return self.get_document().tags.all() + + #@detail_route(lookup_url_kwarg='tag_id') + #def document_list(self, request, *args, **kwargs): + # serializer = DocumentSerializer( + ## instance=self.get_object().documents.all(), many=True, + # context={'request': request} + # ) + # return Response(serializer.data) + +''' + +class APITagListView(ListCreateAPIView): """ get: Returns a list of all the tags. post: Create a new tag. """ - filter_backends = (MayanObjectPermissionsFilter,) - mayan_object_permissions = {'GET': (permission_tag_view,)} - mayan_view_permissions = {'POST': (permission_tag_create,)} - permission_classes = (MayanPermission,) + object_permission = {'GET': permission_tag_view} queryset = Tag.objects.all() - - def get_serializer(self, *args, **kwargs): - if not self.request: - return None - - return super(APITagListView, self).get_serializer(*args, **kwargs) - - def get_serializer_class(self): - if self.request.method == 'GET': - return TagSerializer - elif self.request.method == 'POST': - return WritableTagSerializer + serializer_class = TagSerializer + view_permission = {'POST': permission_tag_create} -class APITagView(generics.RetrieveUpdateDestroyAPIView): +class APITagView(RetrieveUpdateDestroyAPIView): """ delete: Delete the selected tag. get: Return the details of the selected tag. patch: Edit the selected tag. put: Edit the selected tag. """ - filter_backends = (MayanObjectPermissionsFilter,) lookup_url_kwarg = 'tag_pk' - mayan_object_permissions = { - 'DELETE': (permission_tag_delete,), - 'GET': (permission_tag_view,), - 'PATCH': (permission_tag_edit,), - 'PUT': (permission_tag_edit,) + object_permission = { + 'DELETE': permission_tag_delete, + 'GET': permission_tag_view, + 'PATCH': permission_tag_edit, + 'PUT': permission_tag_edit } queryset = Tag.objects.all() + serializer_class = TagSerializer - def get_serializer(self, *args, **kwargs): - if not self.request: - return None +## - return super(APITagView, self).get_serializer(*args, **kwargs) - - def get_serializer_class(self): - if self.request.method == 'GET': - return TagSerializer - else: - return WritableTagSerializer - - -class APITagDocumentListView(ExternalObjectViewMixin, generics.ListAPIView): +class APITagDocumentListView(ExternalObjectMixin, ListCreateAPIView): """ get: Returns a list of all the documents tagged by a particular tag. """ external_object_class = Tag external_object_pk_url_kwarg = 'tag_pk' external_object_permission = permission_tag_view - filter_backends = (MayanObjectPermissionsFilter,) - mayan_object_permissions = {'GET': (permission_document_view,)} - serializer_class = DocumentSerializer + object_permission = {'GET': permission_document_view} + serializer_class = TagDocumentSerializer def get_queryset(self): - return self.get_tag().documents.all() + return self.get_tag().get_documents(user=self.request.user).all() def get_tag(self): return self.get_external_object() +## +''' -class APIDocumentTagListView(ExternalObjectViewMixin, generics.ListCreateAPIView): + +''' +class APITagView(RetrieveDestroyAPIView): """ - get: Returns a list of all the tags attached to a document. - post: Attach a tag to a document. + delete: Delete the selected tag document. + get: Return the details of the selected tag document. """ + lookup_url_kwarg = 'tag_pk' + object_permission = { + 'DELETE': permission_tag_delete, + 'GET': permission_tag_view, + 'PATCH': permission_tag_edit, + 'PUT': permission_tag_edit + } + queryset = Tag.objects.all() + serializer_class = TagSerializer +''' +## +''' +class DocumentSourceMixin(ExternalObjectMixin): external_object_class = Document external_object_pk_url_kwarg = 'document_pk' - filter_backends = (MayanObjectPermissionsFilter,) - mayan_object_permissions = { - 'GET': (permission_tag_view,), - 'POST': (permission_tag_attach,) - } + serializer_class = DocumentTagSerializer def get_document(self): return self.get_external_object() def get_external_object_permission(self): - if self.request.method == 'POST': - return permission_tag_attach - else: - return permission_tag_view + permission_dictionary = { + 'DELETE': permission_tag_remove, + 'GET': permission_tag_view, + 'POST': permission_tag_attach + } + + return permission_dictionary.get(self.request.method) def get_queryset(self): - return self.get_document().get_tags().all() + return self.get_document().tags.all() - def get_serializer(self, *args, **kwargs): - if not self.request: - return None + def get_serializer_context(self): + context = super(DocumentSourceMixin, self).get_serializer_context() + if self.kwargs: + context.update( + { + 'document': self.get_document(), + } + ) - return super(APIDocumentTagListView, self).get_serializer(*args, **kwargs) + return context - def get_serializer_class(self): - return DocumentTagSerializer +class APIDocumentTagListView(DocumentSourceMixin, ListCreateAPIView): + """ + get: Returns a list of all the tags attached to a document. + post: Attach a tag to a document. + """ + #external_object_class = Document + #external_object_pk_url_kwarg = 'document_pk' + object_permission = { + 'GET': permission_tag_view, + #'POST': permission_tag_attach + } + #serializer_class = DocumentTagSerializer + + #def get_document(self): + # return self.get_external_object() + + #def get_external_object_permission(self): + # if self.request.method == 'POST': + # return permission_tag_attach + # else: + # return permission_tag_view + + #def get_queryset(self): + # #if self.request.method == 'POST': + # # permission = permission_tag_attach + # #else: + # # permission = permission_tag_view + # + # return self.get_document().tags().all() + # #return self.get_document().get_tags( + ## # permission=permission, user=self.request.user + # #).all() + + """ def get_serializer_context(self): """ Extra context provided to the serializer class. @@ -140,41 +325,44 @@ class APIDocumentTagListView(ExternalObjectViewMixin, generics.ListCreateAPIView ) return context + """ - -class APIDocumentTagView(ExternalObjectViewMixin, generics.RetrieveDestroyAPIView): +class APIDocumentTagView(DocumentSourceMixin, RetrieveDestroyAPIView): """ delete: Remove a tag from the selected document. get: Returns the details of the selected document tag. """ - external_object_class = Document - external_object_pk_url_kwarg = 'document_pk' - filter_backends = (MayanObjectPermissionsFilter,) + #external_object_class = Document + #external_object_pk_url_kwarg = 'document_pk' lookup_url_kwarg = 'tag_pk' - mayan_object_permissions = { - 'GET': (permission_tag_view,), - 'DELETE': (permission_tag_remove,) + mayan_object_permission = { + 'GET': permission_tag_view, + 'DELETE': permission_tag_remove } - serializer_class = DocumentTagSerializer + #serializer_class = DocumentTagSerializer - def get_document(self): - return self.get_external_object() + #def get_document(self): + # return self.get_external_object() - def get_external_object_permission(self): - if self.request.method == 'DELETE': - return permission_tag_remove - else: - return permission_tag_view + #def get_external_object_permission(self): + # if self.request.method == 'DELETE': + # return permission_tag_remove + # else: + # return permission_tag_view + """ def get_queryset(self): - return self.get_document().get_tags().all() + if self.request.method == 'DELETE': + permission = permission_tag_remove + else: + permission = permission_tag_view - def get_serializer(self, *args, **kwargs): - if not self.request: - return None - - return super(APIDocumentTagView, self).get_serializer(*args, **kwargs) + return self.get_document().get_tags( + permission=permission, user=self.request.user + ).all() + """ + """ def get_serializer_context(self): """ Extra context provided to the serializer class. @@ -188,15 +376,33 @@ class APIDocumentTagView(ExternalObjectViewMixin, generics.RetrieveDestroyAPIVie ) return context + """ def perform_destroy(self, instance): - try: - instance.documents.remove(self.get_document()) - except Exception as exception: - raise ValidationError(exception) + # try: + from mayan.apps.acls.models import AccessControlList + from rest_framework.generics import get_object_or_404 - def retrieve(self, request, *args, **kwargs): - instance = self.get_object() + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_tag_remove, queryset=Tag.objects.all(), + user=self.request.user + ) + instance = get_object_or_404(queryset=queryset, pk=instance.pk) + #instance.attach_to( + # document=self.context['document'], + # user=self.context['request'].user + #) - serializer = self.get_serializer(instance) - return Response(serializer.data) + instance.remove_from( + document=self.get_document(), user=self.request.user + ) + #instance.documents.remove(self.get_document()) + # except Exception as exception: + # raise ValidationError(exception) + + #def retrieve(self, request, *args, **kwargs): + # instance = self.get_object() + + # serializer = self.get_serializer(instance) + # return Response(serializer.data) +''' diff --git a/mayan/apps/tags/apps.py b/mayan/apps/tags/apps.py index ee9c6617af..75209a5517 100644 --- a/mayan/apps/tags/apps.py +++ b/mayan/apps/tags/apps.py @@ -24,12 +24,13 @@ from .events import ( event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove ) from .handlers import handler_index_document, handler_tag_pre_delete +from .html_widgets import DocumentTagsWidget, TagWidget from .links import ( - link_document_tag_list, link_multiple_documents_attach_tag, - link_multiple_documents_tag_remove, - link_single_document_multiple_tag_remove, link_tag_attach, - link_tag_create, link_tag_delete, link_tag_edit, link_tag_list, - link_tag_multiple_delete, link_tag_tagged_item_list + link_document_tag_list, link_document_multiple_tag_multiple_attach, + link_document_multiple_tag_multiple_remove, + link_document_tag_multiple_remove, link_document_tag_multiple_attach, + link_tag_create, link_tag_delete, link_tag_document_list, link_tag_edit, + link_tag_list, link_tag_multiple_delete ) from .menus import menu_tags from .methods import method_get_tags @@ -38,7 +39,6 @@ from .permissions import ( permission_tag_remove, permission_tag_view ) from .search import tag_search # NOQA -from .widgets import widget_document_tags class TagsApp(MayanAppConfig): @@ -63,13 +63,11 @@ class TagsApp(MayanAppConfig): app_label='documents', model_name='DocumentPageSearchResult' ) - DocumentTag = self.get_model('DocumentTag') - Tag = self.get_model('Tag') + DocumentTag = self.get_model(model_name='DocumentTag') + Tag = self.get_model(model_name='Tag') Document.add_to_class(name='get_tags', value=method_get_tags) - ModelAttribute(model=Document, name='get_tags') - ModelEventType.register( model=Tag, event_types=( event_tag_attach, event_tag_created, event_tag_edited, @@ -78,10 +76,10 @@ class TagsApp(MayanAppConfig): ) ModelField( - Document, name='tags__label' + model=Document, name='tags__label' ) ModelField( - Document, name='tags__color' + model=Document, name='tags__color' ) ModelPermission.register( @@ -96,29 +94,31 @@ class TagsApp(MayanAppConfig): permission_acl_edit, permission_acl_view, permission_events_view, permission_tag_attach, permission_tag_delete, permission_tag_edit, - permission_tag_remove, permission_tag_view, + permission_tag_remove, permission_tag_view ) ) SourceColumn( attribute='label', is_identifier=True, is_sortable=True, - source=DocumentTag, + source=DocumentTag ) SourceColumn( - attribute='get_preview_widget', source=DocumentTag + label=_('Preview'), source=DocumentTag, widget=TagWidget ) SourceColumn( - func=lambda context: widget_document_tags( - document=context['object'], user=context['request'].user - ), label=_('Tags'), source=Document - ) - - SourceColumn( - func=lambda context: widget_document_tags( - document=context['object'].document, + func=lambda context: context['object'].get_tags( + permission=permission_tag_view, user=context['request'].user - ), label=_('Tags'), source=DocumentPageSearchResult + ), label=_('Tags'), source=Document, widget=DocumentTagsWidget + ) + + SourceColumn( + func=lambda context: context['object'].document.get_tag( + permission=permission_tag_view, + user=context['request'].user + ), label=_('Tags'), source=DocumentPageSearchResult, + widget=DocumentTagsWidget ) SourceColumn( @@ -126,12 +126,12 @@ class TagsApp(MayanAppConfig): source=Tag ) SourceColumn( - attribute='get_preview_widget', source=Tag + label=_('Preview'), source=Tag, widget=TagWidget ) SourceColumn( func=lambda context: context['object'].get_document_count( user=context['request'].user - ), label=_('Documents'), source=Tag + ), include_label=True, label=_('Documents'), source=Tag ) document_page_search.add_model_field( @@ -147,19 +147,17 @@ class TagsApp(MayanAppConfig): links=( link_acl_list, link_events_for_object, link_object_event_types_user_subcriptions_list, - link_tag_tagged_item_list, - ), - sources=(Tag,) + link_tag_document_list, + ), sources=(Tag,) ) menu_main.bind_links(links=(menu_tags,), position=98) menu_multi_item.bind_links( links=( - link_multiple_documents_attach_tag, - link_multiple_documents_tag_remove - ), - sources=(Document,) + link_document_multiple_tag_multiple_attach, + link_document_multiple_tag_multiple_remove + ), sources=(Document,) ) menu_multi_item.bind_links( links=(link_tag_multiple_delete,), sources=(Tag,) @@ -167,14 +165,14 @@ class TagsApp(MayanAppConfig): menu_object.bind_links( links=( link_tag_edit, link_tag_delete - ), - sources=(Tag,) + ), sources=(Tag,) ) menu_sidebar.bind_links( - links=(link_tag_attach, link_single_document_multiple_tag_remove), - sources=( - 'tags:tag_attach', 'tags:document_tags', - 'tags:single_document_multiple_tag_remove' + links=( + link_document_tag_multiple_attach, link_document_tag_multiple_remove + ), sources=( + 'tags:document_tag_multiple_attach', 'tags:document_tag_list', + 'tags:document_tag_multiple_remove' ) ) menu_tags.bind_links( @@ -188,13 +186,11 @@ class TagsApp(MayanAppConfig): # Index update m2m_changed.connect( - handler_index_document, dispatch_uid='tags_handler_index_document', - sender=Tag.documents.through + receiver=handler_index_document, sender=Tag.documents.through ) pre_delete.connect( - handler_tag_pre_delete, dispatch_uid='tags_handler_tag_pre_delete', - sender=Tag + receiver=handler_tag_pre_delete, sender=Tag ) diff --git a/mayan/apps/tags/events.py b/mayan/apps/tags/events.py index 44f5793802..8f753bbbe9 100644 --- a/mayan/apps/tags/events.py +++ b/mayan/apps/tags/events.py @@ -4,7 +4,7 @@ from django.utils.translation import ugettext_lazy as _ from mayan.apps.events import EventTypeNamespace -namespace = EventTypeNamespace(name='tags', label=_('Tags')) +namespace = EventTypeNamespace(label=_('Tags'), name='tags') event_tag_attach = namespace.add_event_type( label=_('Tag attached to document'), name='attach' diff --git a/mayan/apps/tags/forms.py b/mayan/apps/tags/forms.py index 392de0d68f..30b60a9937 100644 --- a/mayan/apps/tags/forms.py +++ b/mayan/apps/tags/forms.py @@ -1,37 +1,22 @@ from __future__ import absolute_import, unicode_literals -import logging - from django import forms from django.utils.translation import ugettext_lazy as _ from mayan.apps.acls.models import AccessControlList +from mayan.apps.common.forms import FilteredSelectionForm from .models import Tag from .permissions import permission_tag_view from .widgets import TagFormWidget -logger = logging.getLogger(__name__) +class TagMultipleSelectionForm(FilteredSelectionForm): + class Media: + js = ('tags/js/tags_form.js',) -class TagMultipleSelectionForm(forms.Form): - def __init__(self, *args, **kwargs): - help_text = kwargs.pop('help_text', None) - permission = kwargs.pop('permission', permission_tag_view) - 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=permission, queryset=queryset, user=user - ) - - self.fields['tags'] = forms.ModelMultipleChoiceField( - label=_('Tags'), help_text=help_text, - queryset=queryset, required=False, - widget=TagFormWidget( - attrs={'class': 'select2-tags'}, queryset=queryset - ) - ) + class Meta: + allow_multiple = True + field_name = 'tags' + label = _('Tags') + widget_attributes = {'class': 'select2-tags'} diff --git a/mayan/apps/tags/html_widgets.py b/mayan/apps/tags/html_widgets.py new file mode 100644 index 0000000000..94128dc441 --- /dev/null +++ b/mayan/apps/tags/html_widgets.py @@ -0,0 +1,26 @@ +from __future__ import absolute_import, unicode_literals + +from django.template.loader import render_to_string + + +class DocumentTagsWidget(object): + """ + A tag widget that displays the tags for the given document + """ + def render(self, name, value): + return render_to_string( + template_name='tags/document_tags_widget.html', + context={ + 'tags': value, + } + ) + + +class TagWidget(object): + def render(self, name, value): + return render_to_string( + template_name='tags/tag_widget.html', + context={ + 'tag': value, + } + ) diff --git a/mayan/apps/tags/icons.py b/mayan/apps/tags/icons.py index a5f55f6e96..7e84defc64 100644 --- a/mayan/apps/tags/icons.py +++ b/mayan/apps/tags/icons.py @@ -2,19 +2,22 @@ from __future__ import absolute_import, unicode_literals from mayan.apps.appearance.classes import Icon -icon_menu_tags = Icon(driver_name='fontawesome', symbol='tags') -icon_multiple_documents_tag_attach = Icon( - driver_name='fontawesome-dual', primary_symbol='tag', - secondary_symbol='arrow-right' -) -icon_multiple_documents_tag_remove = Icon( +icon_document_multiple_tag_multiple_remove = Icon( driver_name='fontawesome-dual', primary_symbol='tag', secondary_symbol='minus' ) -icon_tag_attach = Icon( +icon_document_tag_multiple_attach = Icon( driver_name='fontawesome-dual', primary_symbol='tag', secondary_symbol='arrow-right' ) +icon_document_tag_multiple_remove = Icon( + driver_name='fontawesome-dual', primary_symbol='tag', + secondary_symbol='minus' +) +icon_document_tag_multiple_remove_submit = Icon( + driver_name='fontawesome', symbol='minus' +) +icon_menu_tags = Icon(driver_name='fontawesome', symbol='tags') icon_tag_create = Icon( driver_name='fontawesome-dual', primary_symbol='tag', secondary_symbol='plus' @@ -25,8 +28,3 @@ icon_tag_delete_submit = Icon(driver_name='fontawesome', symbol='times') icon_tag_document_list = Icon(driver_name='fontawesome', symbol='tags') icon_tag_list = Icon(driver_name='fontawesome', symbol='tags') icon_tag_multiple_delete = Icon(driver_name='fontawesome', symbol='times') -icon_tag_remove = Icon( - driver_name='fontawesome-dual', primary_symbol='tag', - secondary_symbol='minus' -) -icon_tag_remove_submit = Icon(driver_name='fontawesome', symbol='minus') diff --git a/mayan/apps/tags/links.py b/mayan/apps/tags/links.py index 062a44f425..0a7a3ab980 100644 --- a/mayan/apps/tags/links.py +++ b/mayan/apps/tags/links.py @@ -6,10 +6,10 @@ from mayan.apps.documents.icons import icon_document_list from mayan.apps.navigation import Link, get_cascade_condition from .icons import ( - icon_document_multiple_tag_multiple_remove, icon_document_multiple_tag_multiple_remove, - icon_document_tag_multiple_attach, icon_tag_create, icon_tag_delete, icon_tag_edit, - icon_tag_document_list, icon_tag_list, icon_tag_multiple_delete, - icon_document_tag_multiple_remove + icon_document_multiple_tag_multiple_remove, + icon_document_tag_multiple_attach, icon_tag_create, icon_tag_delete, + icon_tag_edit, icon_tag_document_list, icon_tag_list, + icon_tag_multiple_delete, icon_document_tag_multiple_remove ) from .permissions import ( permission_tag_attach, permission_tag_create, permission_tag_delete, @@ -17,10 +17,6 @@ from .permissions import ( ) -link_document_tag_list = Link( - args='resolved_object.pk', icon_class=icon_tag_document_list, - permission=permission_tag_view, text=_('Tags'), view='tags:document_tags' -) link_document_multiple_tag_multiple_attach = Link( icon_class=icon_document_multiple_tag_multiple_remove, text=_('Attach tags'), view='tags:document_multiple_tag_multiple_attach' @@ -29,27 +25,37 @@ link_document_multiple_tag_multiple_remove = Link( icon_class=icon_document_multiple_tag_multiple_remove, text=_('Remove tag'), view='tags:document_multiple_tag_multiple_remove' ) +link_document_tag_list = Link( + icon_class=icon_tag_document_list, + kwargs={'document_id': 'resolved_object.pk'}, + permission=permission_tag_view, text=_('Tags'), + view='tags:document_tag_list' +) link_document_tag_multiple_attach = Link( - args='object.pk', icon_class=icon_document_tag_multiple_attach, - permission=permission_tag_attach, text=_('Attach tags'), - view='tags:document_tag_multiple_attach' + icon_class=icon_document_tag_multiple_attach, + kwargs={'document_id': 'object.pk'}, permission=permission_tag_attach, + text=_('Attach tags'), view='tags:document_tag_multiple_attach' ) link_document_tag_multiple_remove = Link( - args='object.id', icon_class=icon_document_tag_multiple_remove, - permission=permission_tag_remove, text=_('Remove tags'), - view='tags:document_tag_multiple_remove' + icon_class=icon_document_tag_multiple_remove, + kwargs={'document_id': 'object.pk'}, permission=permission_tag_remove, + text=_('Remove tags'), view='tags:document_tag_multiple_remove' ) link_tag_create = Link( icon_class=icon_tag_create, permission=permission_tag_create, text=_('Create new tag'), view='tags:tag_create' ) link_tag_delete = Link( - args='object.id', icon_class=icon_tag_delete, + icon_class=icon_tag_delete, kwargs={'tag_id': 'object.pk'}, permission=permission_tag_delete, tags='dangerous', text=_('Delete'), view='tags:tag_delete' ) +link_tag_document_list = Link( + icon_class=icon_document_list, kwargs={'tag_id': 'object.pk'}, + text=('Documents'), view='tags:tag_document_list' +) link_tag_edit = Link( - args='object.id', icon_class=icon_tag_edit, + icon_class=icon_tag_edit, kwargs={'tag_id': 'object.pk'}, permission=permission_tag_edit, text=_('Edit'), view='tags:tag_edit' ) link_tag_list = Link( @@ -62,7 +68,3 @@ link_tag_multiple_delete = Link( icon_class=icon_tag_multiple_delete, permission=permission_tag_delete, text=_('Delete'), view='tags:tag_multiple_delete' ) -link_tag_tagged_item_list = Link( - args='object.id', icon_class=icon_document_list, text=('Documents'), - view='tags:tag_tagged_item_list' -) diff --git a/mayan/apps/tags/methods.py b/mayan/apps/tags/methods.py index 83761fc92f..bfc0163ebc 100644 --- a/mayan/apps/tags/methods.py +++ b/mayan/apps/tags/methods.py @@ -3,10 +3,18 @@ from __future__ import unicode_literals from django.apps import apps from django.utils.translation import ugettext_lazy as _ +from .permissions import permission_tag_view -def method_get_tags(self): + +def method_get_tags(self, permission, user): + AccessControlList = apps.get_model( + app_label='acls', model_name='AccessControlList' + ) DocumentTag = apps.get_model(app_label='tags', model_name='DocumentTag') - return DocumentTag.objects.filter(documents=self) + return AccessControlList.objects.restrict_queryset( + permission=permission, + queryset=DocumentTag.objects.filter(documents=self), user=user + ) method_get_tags.help_text = _('Return a the tags attached to the document.') diff --git a/mayan/apps/tags/models.py b/mayan/apps/tags/models.py index 0e49ae2f27..5f2e6ec88b 100644 --- a/mayan/apps/tags/models.py +++ b/mayan/apps/tags/models.py @@ -15,7 +15,6 @@ from .events import ( event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove ) from .managers import DocumentTagManager -from .widgets import widget_single_tag @python_2_unicode_compatible @@ -56,23 +55,22 @@ class Tag(models.Model): def get_absolute_url(self): return reverse( - viewname='tags:tag_tagged_item_list', kwargs={'tag_pk': str(self.pk)} + viewname='tags:tag_tagged_item_list', kwargs={ + 'tag_id': self.pk + } + ) + + def get_documents(self, user): + """ + Return a filtered queryset documents that have this tag attached. + """ + return AccessControlList.objects.restrict_queryset( + permission=permission_document_view, queryset=self.documents, + user=user ) def get_document_count(self, user): - """ - Return the numeric count of documents that have this tag attached. - The count if filtered by access. - """ - queryset = AccessControlList.objects.filter_by_access( - permission_document_view, user, queryset=self.documents - ) - - return queryset.count() - - def get_preview_widget(self): - return widget_single_tag(tag=self) - get_preview_widget.short_description = _('Preview') + return self.get_documents(user=user).count() def remove_from(self, document, user=None): """ diff --git a/mayan/apps/tags/routers.py b/mayan/apps/tags/routers.py new file mode 100644 index 0000000000..04f7371af6 --- /dev/null +++ b/mayan/apps/tags/routers.py @@ -0,0 +1,20 @@ +from __future__ import unicode_literals + +#from rest_framework import routers + +#router = routers.SimpleRouter() +#router.register(r'users', UserViewSet) +#router.register(r'accounts', AccountViewSet) +#urlpatterns = router.urls + +#router = routers.DefaultRouter() +#from mayan.apps.rest_api.api_views import router +#from mayan.apps.rest_api.urls import router + +from .api_views import TagViewSet + +router_entries = ( + {'prefix': r'tags', 'viewset': TagViewSet, 'base_name': 'tag'}, +) + +#router.register(prefix=r'tags', viewset=TagViewSet, basename='tag') diff --git a/mayan/apps/tags/search.py b/mayan/apps/tags/search.py index f16b8e75c1..d74bee54a8 100644 --- a/mayan/apps/tags/search.py +++ b/mayan/apps/tags/search.py @@ -7,9 +7,8 @@ from mayan.apps.dynamic_search.classes import SearchModel from .permissions import permission_tag_view tag_search = SearchModel( - app_label='tags', model_name='Tag', - permission=permission_tag_view, - serializer_string='mayan.apps.tags.serializers.TagSerializer' + app_label='tags', model_name='Tag', permission=permission_tag_view, + serializer_path='mayan.apps.tags.serializers.TagSerializer' ) tag_search.add_model_field( diff --git a/mayan/apps/tags/serializers.py b/mayan/apps/tags/serializers.py index 5048d10324..9f4d5c3012 100644 --- a/mayan/apps/tags/serializers.py +++ b/mayan/apps/tags/serializers.py @@ -8,34 +8,42 @@ from rest_framework.reverse import reverse from mayan.apps.acls.models import AccessControlList from mayan.apps.documents.models import Document +from mayan.apps.documents.serializers import DocumentSerializer +from mayan.apps.rest_api.relations import MultiKwargHyperlinkedIdentityField from .models import Tag from .permissions import permission_tag_attach class TagSerializer(serializers.HyperlinkedModelSerializer): - documents_url = serializers.HyperlinkedIdentityField( - lookup_field='pk', lookup_url_kwarg='tag_pk', - view_name='rest_api:tag-document-list' + attach_url = serializers.HyperlinkedIdentityField( + lookup_url_kwarg='tag_id', view_name='rest_api:tag-document-attach' + ) + + documents_url = serializers.HyperlinkedIdentityField( + lookup_url_kwarg='tag_id', view_name='rest_api:tag-document-list' + ) + + remove_url = serializers.HyperlinkedIdentityField( + lookup_url_kwarg='tag_id', view_name='rest_api:tag-document-remove' ) - documents_count = serializers.SerializerMethodField() class Meta: extra_kwargs = { 'url': { - 'lookup_field': 'pk', 'lookup_url_kwarg': 'tag_pk', + 'lookup_url_kwarg': 'tag_id', 'view_name': 'rest_api:tag-detail' - } + }, } fields = ( - 'color', 'documents_count', 'documents_url', 'id', 'label', 'url' + 'attach_url', 'color', 'documents_url', 'label', 'id', + 'remove_url', 'url' ) model = Tag - def get_documents_count(self, instance): - return instance.documents.count() +""" class WritableTagSerializer(serializers.ModelSerializer): documents_pk_list = serializers.CharField( help_text=_( @@ -81,37 +89,303 @@ class WritableTagSerializer(serializers.ModelSerializer): ) return instance - +""" class DocumentTagSerializer(TagSerializer): - document_tag_url = serializers.SerializerMethodField( - help_text=_( - 'API URL pointing to a tag in relation to the document ' - 'attached to it. This URL is different than the canonical ' - 'tag URL.' - ) - ) - tag_pk = serializers.IntegerField( - help_text=_('Primary key of the tag to be added.'), write_only=True - ) + #document_attach_url = serializers.HyperlinkedIdentityField( + # lookup_url_kwarg='document_id', view_name='rest_api:document-tag-attach' + #) class Meta(TagSerializer.Meta): - fields = TagSerializer.Meta.fields + ('document_tag_url', 'tag_pk') - read_only_fields = TagSerializer.Meta.fields + ('document_tag_url',) + #fields = TagSerializer.Meta.fields + ('document_attach_url',) + fields = TagSerializer.Meta.fields + #fields = TagSerializer.Meta.fields + ('document_tag_url', 'tag_pk') + #fields = TagSerializer.Meta.fields + ('tag_pk',) + #read_only_fields = TagSerializer.Meta.fields + ('document_attach_url',) + read_only_fields = TagSerializer.Meta.fields + #read_only_fields = TagSerializer.Meta.fields - def get_document_tag_url(self, instance): - return reverse( - viewname='rest_api:document-tag-detail', kwargs={ - 'document_pk': self.context['document'].pk, - 'tag_pk': instance.pk - }, request=self.context['request'], format=self.context['format'] + #related_models = ('tags',) + #related_models_kwargs = { + # 'documents': { + ## #'pk_list': 'tags_pk_list', 'model': Tag, + # #'model': Tag, + # 'object_permission': {'create': permission_tag_attach}, + # #'add_method': 'add', 'add_method_kwargs': 'document' + # } + #} + + ''' + def create(self, validated_data): + """ + queryset = Tag.objects.filter(pk__in=validated_data['tags_pk_list'].split(',')) + + #permission = self.object_permission.get('create') + + #if permission: + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_tag_attach, queryset=queryset, + user=self.context['request'].user ) - def create(self, validated_data): - queryset = AccessControlList.objects.filter_by_access( + for tag in queryset.all(): + tag.attach_to( + document=self.context['document'], + user=self.context['request'].user + ) + """ + queryset = AccessControlList.objects.restrict_queryset( permission=permission_tag_attach, queryset=Tag.objects.all(), user=self.context['request'].user ) tag = get_object_or_404(queryset=queryset, pk=validated_data['tag_pk']) - tag.documents.add(self.context['document']) + tag.attach_to( + document=self.context['document'], + user=self.context['request'].user + ) return tag + #return None + ''' + + def get_document_tag_url(self, instance): + return reverse( + viewname='rest_api:document-tag-detail', kwargs={ + 'document_id': self.context['document'].pk, + 'tag_id': instance.pk + }, request=self.context['request'], format=self.context['format'] + ) + + +class DocumentTagAttachSerializer(serializers.Serializer): + tags_pk_list = serializers.CharField( + help_text=_( + 'Comma separated list of tag primary keys that will be attached ' + 'to this document.' + ), write_only=True + ) + + +class TagAttachSerializer(serializers.Serializer): +#class TagAttachSerializer(TagSerializer): + documents_pk_list = serializers.CharField( + help_text=_( + 'Comma separated list of document primary keys to which this ' + 'tag will be attached.' + ), write_only=True + ) + + #class Meta(TagSerializer.Meta): + # fields = TagSerializer.Meta.fields + ('documents_pk_list',) + # read_only_fields = TagSerializer.Meta.fields + + def attach(self, instance): + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_tag_attach, queryset=Document.objects.all(), + user=self.context['request'].user + ) + + for document in queryset.filter(pk__in=self.validated_data['documents_pk_list'].split(',')): + instance.attach_to(document=document, user=self.context['request'].user) + + #print '@@@@@@@', self.validated_data['document_pk_list'] + #print '@@@@@@@', instance + #print '22222', validated_data + #print '!!!', self.data['document_pk_list'] + + +class TagRemoveSerializer(serializers.Serializer): + documents_pk_list = serializers.CharField( + help_text=_( + 'Comma separated list of document primary keys from which this ' + 'tag will be removed.' + ), write_only=True + ) + + def remove(self, instance): + queryset = AccessControlList.objects.restrict_queryset( + permission=permission_tag_attach, queryset=Document.objects.all(), + user=self.context['request'].user + ) + + for document in queryset.filter(pk__in=self.validated_data['documents_pk_list'].split(',')): + instance.remove_from(document=document, user=self.context['request'].user) + + + +class RelatedModel(object): + @classmethod + def generate(cls, serializer, validated_data): + result = [] + + kwargs = getattr(serializer.Meta, 'related_model_kwargs', {}) + kwargs.update({'serializer': serializer}) + + for field_name in getattr(serializer.Meta, 'related_models', []): + kwargs.update({'field_name': field_name}) + related_field = cls(**kwargs) + related_field.pop_pk_list(validated_data=validated_data) + result.append(related_field) + + return result + + def __init__(self, field_name, serializer, pk_list_field=None, model=None, object_permission=None): + self.field_name = field_name + self._pk_list_field = pk_list_field + self.model = model + self.object_permission = object_permission + self.serializer = serializer + + def create(self, instance): + field = self.get_field(instance=instance) + field.clear() + #model = self.get_model() + + queryset = self.get_model().objects.filter(pk__in=self.pk_list.split(',')) + + permission = self.object_permission.get('create') + + if permission: + queryset = AccessControlList.objects.restrict_queryset( + permission=permission, + queryset=queryset, + user=self.serializer.context['request'].user + ) + + self.related_add() + + field.add(*queryset) + #fieldqueryset=queryset) + + #def related_add(self, queryset): + # self.get_field().add(*queryset) + + + #def _get_m2m_field(self, instance): + # getattr(instance, m2m_field_name).all() + + """ + def _add_m2m(self, instance, m2m_pk_list, permission): + m2m_field = self._get_m2m_field() + m2m_field.clear() + + queryset = AccessControlList.objects.restrict_queryset( + permission=permission, + queryset=m2m_model.objects.filter(pk__in=m2m_pk_list.split(',')), + user=self.context['request'].user + ) + + #m2m_field.add(*queryset) + self._m2m_add(m2m_field=m2m_field, queryset=queryset) + """ + + def get_model(self): + return self.model or self.get_field.model + + def get_field(self, instance): + return getattr(instance, self.field_name) + + def get_pk_list_field_name(self): + return self._pk_list_field or '{}_pk_list'.format(self.field_name) + + def pop_pk_list(self, validated_data): + self.pk_list = validated_data.pop(self.get_pk_list_field_name(), '') + + +class RelatedModelSerializerMixin(object): + #m2m_pk_list_name = 'documents_pk_list' + #m2m_field_name = 'documents' + #m2m_model = Document + + """ + class Meta: + extra_kwargs = { + 'url': { + 'lookup_field': 'pk', 'lookup_url_kwarg': 'tag_pk', + 'view_name': 'rest_api:tag-detail' + } + } + fields = ( + 'color', 'documents_count', 'documents_pk_list', 'documents_url', + 'id', 'label', 'url' + ) + model = Tag + related_models = ('documents',) + related_models_kwargs = { + 'documents': { + 'pk_list_field': 'documents_pk_list', 'model': Document, + 'permissions': {'create': permission_tag_attach} + } + } + """ + + def _get_m2m_field(self, instance): + getattr(instance, m2m_field_name).all() + + def _add_m2m(self, instance, m2m_pk_list, permission): + m2m_field = self._get_m2m_field() + m2m_field.clear() + + queryset = AccessControlList.objects.restrict_queryset( + permission=permission, + queryset=m2m_model.objects.filter(pk__in=m2m_pk_list.split(',')), + user=self.context['request'].user + ) + + #m2m_field.add(*queryset) + self._m2m_add(m2m_field=m2m_field, queryset=queryset) + + #def _m2m_add(self, m2m_field, queryset): + # m2m_field.add(*queryset) + + def _m2m_add(self, m2m_field, queryset): + for document in queryset.all(): + m2m_field.add(document=document, user=self.context['request'].user) + + def create(self, validated_data): + related_objects = RelatedModel.generate( + serializer=self, validated_data=validated_data + ) + + instance = super(RelatedModelSerializerMixin, self).create( + validated_data=validated_data + ) + + #TODO: return a container class + #TODO:related_objects.create(instance=instance) + for related_object in related_objects: + related_object.create(instance=instance) + + #if m2m_pk_list: + ## self._add_m2m( + # instance=instance, m2m_pk_list=m2m_pk_list, + # permission=permission_tag_add + # ) + + return instance + + ''' + # Extract the related field data before calling the superclass + # .create() and avoid an error due to unknown field data. + + #related_models = self.Meta.related_models + + #self.Meta.related_models + related_models_dictionary = {} + for related_model in self.Meta.related_models: + + #if self.m2m_pk_list_name: + m2m_pk_list = validated_data.pop(self.get_related_model_pk_list(), '') + + instance = super(RelatedObjectSerializerMixin, self).create( + validated_data=validated_data + ) + + if m2m_pk_list: + self._add_m2m( + instance=instance, m2m_pk_list=m2m_pk_list, + permission=permission_tag_add + ) + + return instance + ''' + diff --git a/mayan/apps/tags/static/tags/js/tags_form.js b/mayan/apps/tags/static/tags/js/tags_form.js new file mode 100644 index 0000000000..290f1dd82f --- /dev/null +++ b/mayan/apps/tags/static/tags/js/tags_form.js @@ -0,0 +1,26 @@ +'use strict'; + +var tagSelectionTemplate = function (tag, container) { + var $tag = $( + ' ' + escape(tag.text) + '' + ); + container[0].style.background = tag.element.dataset.color; + return $tag; +} + +var tagResultTemplate = function (tag) { + if (!tag.element) { return ''; } + var $tag = $( + ' ' + escape(tag.text) + '' + ); + return $tag; +} + +jQuery(document).ready(function() { + $('.select2-tags').select2({ + templateSelection: tagSelectionTemplate, + templateResult: tagResultTemplate, + width: '100%' + }); +}); + diff --git a/mayan/apps/tags/templates/tags/document_tags_widget.html b/mayan/apps/tags/templates/tags/document_tags_widget.html new file mode 100644 index 0000000000..4d0e6fe825 --- /dev/null +++ b/mayan/apps/tags/templates/tags/document_tags_widget.html @@ -0,0 +1,5 @@ +
+ {% for tag in tags %} + {% include 'tags/tag_widget.html' with tag=tag %} + {% endfor %} +
diff --git a/mayan/apps/tags/templates/tags/forms/widgets/tag_select_option.html b/mayan/apps/tags/templates/tags/forms/widgets/tag_select_option.html index 8dabad9809..5a657c2915 100644 --- a/mayan/apps/tags/templates/tags/forms/widgets/tag_select_option.html +++ b/mayan/apps/tags/templates/tags/forms/widgets/tag_select_option.html @@ -1,2 +1 @@ {% include 'django/forms/widgets/select_option.html' %} - diff --git a/mayan/apps/tags/tests/literals.py b/mayan/apps/tags/tests/literals.py index 6949b2d9dd..1c08f4765b 100644 --- a/mayan/apps/tags/tests/literals.py +++ b/mayan/apps/tags/tests/literals.py @@ -9,15 +9,15 @@ TEST_TAG_INDEX_HAS_TAG = 'HAS_TAG' TEST_TAG_INDEX_NO_TAG = 'NO_TAG' TEST_TAG_INDEX_NODE_TEMPLATE = ''' {{% for tag in document.get_tags().all() %}} -{{% if tag.label == "{}" %}} -{} + {{% if tag.label == "{label}" %}} + {has_tag} + {{% else %}} + {not_tagged} + {{% endif %}} {{% else %}} -NO_TAG -{{% endif %}} -{{% else %}} -NO_TAG + {not_tagged} {{% endfor %}} '''.format( - TEST_TAG_LABEL, TEST_TAG_INDEX_HAS_TAG, TEST_TAG_INDEX_NO_TAG, - TEST_TAG_INDEX_NO_TAG + label=TEST_TAG_LABEL, has_tag=TEST_TAG_INDEX_HAS_TAG, + not_tagged=TEST_TAG_INDEX_NO_TAG ).replace('\n', '') diff --git a/mayan/apps/tags/tests/mixins.py b/mayan/apps/tags/tests/mixins.py index d93041edb5..2b4b1dd6f4 100644 --- a/mayan/apps/tags/tests/mixins.py +++ b/mayan/apps/tags/tests/mixins.py @@ -9,39 +9,100 @@ from .literals import ( class TagTestMixin(object): - def _create_tag(self): - self.tag = Tag.objects.create( + def _create_test_tag(self): + self.test_tag = Tag.objects.create( color=TEST_TAG_COLOR, label=TEST_TAG_LABEL ) + +class TagAPITestMixin(object): def _request_api_tag_create_view(self): return self.post( viewname='rest_api:tag-list', data={ - 'label': TEST_TAG_LABEL, 'color': TEST_TAG_COLOR + 'color': TEST_TAG_COLOR, 'label': TEST_TAG_LABEL + } + ) + + def _request_api_tag_create_and_attach_view(self): + return self.post( + viewname='rest_api:tag-list', data={ + 'color': TEST_TAG_COLOR, 'label': TEST_TAG_LABEL, + 'document_id_list': self.document.pk } ) def _request_api_tag_delete_view(self): return self.delete( - viewname='rest_api:tag-detail', kwargs={'tag_pk': self.tag.pk} + viewname='rest_api:tag-detail', kwargs={'tag_id': self.test_tag.pk} ) - def _request_api_tag_edit_via_patch_view(self): + def _request_api_tag_edit_patch_view(self): return self.patch( - viewname='rest_api:tag-detail', kwargs={'tag_pk': self.tag.pk}, data={ + viewname='rest_api:tag-detail', kwargs={ + 'tag_id': self.test_tag.pk + }, data={ 'label': TEST_TAG_LABEL_EDITED, 'color': TEST_TAG_COLOR_EDITED } ) - def _request_api_tag_edit_via_put_view(self): + def _request_api_tag_edit_put_view(self): return self.put( - viewname='rest_api:tag-detail', kwargs={'tag_pk': self.tag.pk}, data={ + viewname='rest_api:tag-detail', kwargs={ + 'tag_id': self.test_tag.pk + }, data={ 'label': TEST_TAG_LABEL_EDITED, 'color': TEST_TAG_COLOR_EDITED } ) + def _request_api_tag_list_view(self): + return self.get(viewname='rest_api:tag-list') + + +class TagViewTestMixin(object): + def _request_document_tag_multiple_attach_view(self): + return self.post( + viewname='tags:document_tag_multiple_attach', + kwargs={'document_id': self.document.pk}, data={ + 'tags': self.test_tag.pk, + } + ) + + def _request_document_multiple_tag_multiple_attach_view(self): + return self.post( + viewname='tags:document_multiple_tag_multiple_attach', data={ + 'id_list': self.document.pk, 'tags': self.test_tag.pk, + } + ) + + def _request_document_tag_multiple_remove_view(self): + return self.post( + viewname='tags:document_tag_multiple_remove', + kwargs={'document_id': self.document.pk}, data={ + 'tags': self.test_tag.pk, + } + ) + + def _request_document_multiple_tag_multiple_remove_view(self): + return self.post( + viewname='tags:document_multiple_tag_multiple_remove', + data={ + 'id_list': self.document.pk, + 'tags': self.test_tag.pk, + } + ) + + def _request_document_tag_list_view(self): + return self.get( + viewname='tags:document_tag_list', + kwargs={ + 'document_id': self.document.pk, + } + ) + + # Normal tag view + def _request_tag_create_view(self): return self.post( viewname='tags:tag_create', data={ @@ -52,68 +113,19 @@ class TagTestMixin(object): def _request_tag_delete_view(self): return self.post( - viewname='tags:tag_delete', kwargs={'tag_pk': self.tag.pk} + viewname='tags:tag_delete', kwargs={'tag_id': self.test_tag.pk}, ) def _request_tag_edit_view(self): return self.post( - viewname='tags:tag_edit', kwargs={'tag_pk': self.tag.pk}, data={ + viewname='tags:tag_edit', kwargs={'tag_id': self.test_tag.pk}, + data={ 'label': TEST_TAG_LABEL_EDITED, 'color': TEST_TAG_COLOR_EDITED } ) - def _request_multiple_delete_view(self): + def _request_tag_multiple_delete_view(self): return self.post( viewname='tags:tag_multiple_delete', - data={'id_list': self.tag.pk}, - ) - - def _request_edit_tag_view(self): - return self.post( - viewname='tags:tag_edit', kwargs={'tag_pk': self.tag.pk}, data={ - 'label': TEST_TAG_LABEL_EDITED, 'color': TEST_TAG_COLOR_EDITED - } - ) - - def _request_create_tag_view(self): - return self.post( - viewname='tags:tag_create', data={ - 'label': TEST_TAG_LABEL, - 'color': TEST_TAG_COLOR - } - ) - - def _request_attach_tag_view(self): - return self.post( - viewname='tags:tag_attach', - kwargs={'document_pk': self.document.pk}, data={ - 'tags': self.tag.pk, - 'user': self.user.pk - } - ) - - def _request_multiple_attach_tag_view(self): - return self.post( - viewname='tags:multiple_documents_tag_attach', data={ - 'id_list': self.document.pk, 'tags': self.tag.pk, - 'user': self.user.pk - } - ) - - def _request_single_document_multiple_tag_remove_view(self): - return self.post( - viewname='tags:single_document_multiple_tag_remove', - kwargs={'document_pk': self.document.pk}, data={ - 'id_list': self.document.pk, - 'tags': self.tag.pk, - } - ) - - def _request_multiple_documents_selection_tag_remove_view(self): - return self.post( - viewname='tags:multiple_documents_selection_tag_remove', - data={ - 'id_list': self.document.pk, - 'tags': self.tag.pk, - } + data={'id_list': self.test_tag.pk} ) diff --git a/mayan/apps/tags/tests/test_api.py b/mayan/apps/tags/tests/test_api.py index ec55335692..4c259f2de9 100644 --- a/mayan/apps/tags/tests/test_api.py +++ b/mayan/apps/tags/tests/test_api.py @@ -21,11 +21,7 @@ from .literals import ( from .mixins import TagTestMixin -class TagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): - def setUp(self): - super(TagAPITestCase, self).setUp() - self.login_user() - +class TagAPITestCase(TagTestMixin, BaseAPITestCase): def test_tag_create_view_no_permission(self): response = self._request_api_tag_create_view() self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) @@ -46,284 +42,301 @@ class TagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): self.assertEqual(tag.color, TEST_TAG_COLOR) def test_tag_delete_view_no_access(self): - self._create_tag() + self._create_test_tag() response = self._request_api_tag_delete_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertTrue(self.tag in Tag.objects.all()) + self.assertTrue(self.test_tag in Tag.objects.all()) self.assertEqual(Tag.objects.all().count(), 1) def test_tag_delete_view_with_access(self): - self._create_tag() - self.grant_access(obj=self.tag, permission=permission_tag_delete) + self._create_test_tag() + self.grant_access(obj=self.test_tag, permission=permission_tag_delete) response = self._request_api_tag_delete_view() self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(Tag.objects.all().count(), 0) - def test_tag_edit_via_patch_no_access(self): - self._create_tag() - response = self._request_api_tag_edit_via_patch_view() + def test_tag_edit_patch_view_no_access(self): + self._create_test_tag() + response = self._request_api_tag_edit_patch_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.tag.refresh_from_db() - self.assertEqual(self.tag.label, TEST_TAG_LABEL) - self.assertEqual(self.tag.color, TEST_TAG_COLOR) + self.test_tag.refresh_from_db() + self.assertEqual(self.test_tag.label, TEST_TAG_LABEL) + self.assertEqual(self.test_tag.color, TEST_TAG_COLOR) self.assertEqual(Tag.objects.all().count(), 1) - def test_tag_edit_via_patch_with_access(self): - self._create_tag() - self.grant_access(obj=self.tag, permission=permission_tag_edit) - response = self._request_api_tag_edit_via_patch_view() + def test_tag_edit_patch_view_with_access(self): + self._create_test_tag() + self.grant_access(obj=self.test_tag, permission=permission_tag_edit) + response = self._request_api_tag_edit_patch_view() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.tag.refresh_from_db() - self.assertEqual(self.tag.label, TEST_TAG_LABEL_EDITED) - self.assertEqual(self.tag.color, TEST_TAG_COLOR_EDITED) + self.test_tag.refresh_from_db() + self.assertEqual(self.test_tag.label, TEST_TAG_LABEL_EDITED) + self.assertEqual(self.test_tag.color, TEST_TAG_COLOR_EDITED) self.assertEqual(Tag.objects.all().count(), 1) - def test_tag_edit_via_put_no_access(self): - self._create_tag() - response = self._request_api_tag_edit_via_put_view() + def test_tag_edit_put_view_no_access(self): + self._create_test_tag() + response = self._request_api_tag_edit_put_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.tag.refresh_from_db() - self.assertEqual(self.tag.label, TEST_TAG_LABEL) - self.assertEqual(self.tag.color, TEST_TAG_COLOR) + self.test_tag.refresh_from_db() + self.assertEqual(self.test_tag.label, TEST_TAG_LABEL) + self.assertEqual(self.test_tag.color, TEST_TAG_COLOR) self.assertEqual(Tag.objects.all().count(), 1) - def test_tag_edit_via_put_with_access(self): - self._create_tag() - self.grant_access(obj=self.tag, permission=permission_tag_edit) - response = self._request_api_tag_edit_via_put_view() + def test_tag_edit_put_view_with_access(self): + self._create_test_tag() + self.grant_access(obj=self.test_tag, permission=permission_tag_edit) + response = self._request_api_tag_edit_put_view() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.tag.refresh_from_db() - self.assertEqual(self.tag.label, TEST_TAG_LABEL_EDITED) - self.assertEqual(self.tag.color, TEST_TAG_COLOR_EDITED) + self.test_tag.refresh_from_db() + self.assertEqual(self.test_tag.label, TEST_TAG_LABEL_EDITED) + self.assertEqual(self.test_tag.color, TEST_TAG_COLOR_EDITED) self.assertEqual(Tag.objects.all().count(), 1) - -class DocumentAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): - auto_upload_document = False - - def setUp(self): - super(DocumentAPITestCase, self).setUp() - self.login_user() - - def _request_api_tag_document_list_view(self): - return self.get( - viewname='rest_api:tag-document-list', - kwargs={'tag_pk': self.tag.pk} - ) - - def test_tag_document_list_view_no_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - response = self._request_api_tag_document_list_view() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - - def test_tag_document_list_view_with_tag_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.tag, permission=permission_tag_view) - response = self._request_api_tag_document_list_view() + def test_tag_list_view_no_access(self): + self._create_test_tag() + response = self._request_api_tag_list_view() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['count'], 0) - def test_tag_document_list_view_with_document_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access( - obj=self.document, permission=permission_document_view - ) - response = self._request_api_tag_document_list_view() - self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - - def test_tag_document_list_view_with_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.tag, permission=permission_tag_view) - self.grant_access( - obj=self.document, permission=permission_document_view - ) - response = self._request_api_tag_document_list_view() + def test_tag_list_view_with_access(self): + self._create_test_tag() + self.grant_access(obj=self.test_tag, permission=permission_tag_view) + response = self._request_api_tag_list_view() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual( - response.data['results'][0]['uuid'], - force_text(self.document.uuid) - ) + self.assertEqual(response.data['count'], 1) - def _request_api_document_attach_tag_view(self): + +class DocumentTagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): + auto_upload_document = False + + def _request_api_document_tag_attach_view(self): return self.post( viewname='rest_api:document-tag-list', - kwargs={'document_pk': self.document.pk}, - data={'tag_pk': self.tag.pk} + kwargs={'document_id': self.test_document.pk}, + data={'tag_id': self.test_tag.pk} ) - def test_document_attach_tag_view_no_access(self): - self._create_tag() - self.document = self.upload_document() - response = self._request_api_document_attach_tag_view() + def test_document_tag_attach_view_no_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + response = self._request_api_document_tag_attach_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertTrue(self.tag not in self.document.tags.all()) + self.assertTrue(self.test_tag not in self.test_document.tags.all()) self.assertEqual(Tag.objects.all().count(), 1) - def test_document_attach_tag_view_with_document_access(self): - self._create_tag() - self.document = self.upload_document() - self.grant_access(obj=self.document, permission=permission_tag_attach) - response = self._request_api_document_attach_tag_view() + def test_document_tag_attach_view_with_document_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.grant_access(obj=self.test_document, permission=permission_tag_attach) + response = self._request_api_document_tag_attach_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertTrue(self.tag not in self.document.tags.all()) + self.assertTrue(self.test_tag not in self.test_document.tags.all()) self.assertEqual(Tag.objects.all().count(), 1) - def test_document_attach_tag_view_with_tag_access(self): - self._create_tag() - self.document = self.upload_document() - self.grant_access(obj=self.tag, permission=permission_tag_attach) - response = self._request_api_document_attach_tag_view() + def test_document_tag_attach_view_with_tag_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + response = self._request_api_document_tag_attach_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertTrue(self.tag not in self.document.tags.all()) + self.assertTrue(self.test_tag not in self.test_document.tags.all()) self.assertEqual(Tag.objects.all().count(), 1) - def test_document_attach_tag_view_with_full_access(self): - self._create_tag() - self.document = self.upload_document() - self.grant_access(obj=self.document, permission=permission_tag_attach) - self.grant_access(obj=self.tag, permission=permission_tag_attach) - response = self._request_api_document_attach_tag_view() + def test_document_tag_attach_view_with_full_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.grant_access( + obj=self.test_document, permission=permission_tag_attach + ) + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + response = self._request_api_document_tag_attach_view() self.assertEqual(response.status_code, status.HTTP_201_CREATED) - self.assertTrue(self.tag in self.document.tags.all()) + self.assertTrue(self.test_tag in self.test_document.tags.all()) self.assertEqual(Tag.objects.all().count(), 1) def _request_api_document_tag_detail_view(self): return self.get( viewname='rest_api:document-tag-detail', kwargs={ - 'document_pk': self.document.pk, 'tag_pk': self.tag.pk + 'document_id': self.test_document.pk, 'tag_id': self.test_tag.pk } ) def test_document_tag_detail_view_no_permission(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) response = self._request_api_document_tag_detail_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_document_tag_detail_view_with_document_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) self.grant_access( - obj=self.document, permission=permission_document_view + obj=self.test_document, permission=permission_document_view ) response = self._request_api_document_tag_detail_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_document_tag_detail_view_with_tag_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.tag, permission=permission_tag_view) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_tag, permission=permission_tag_view) response = self._request_api_document_tag_detail_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_document_tag_detail_view_with_full_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.tag, permission=permission_tag_view) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_tag, permission=permission_tag_view) self.grant_access( - obj=self.document, permission=permission_tag_view + obj=self.test_document, permission=permission_tag_view ) response = self._request_api_document_tag_detail_view() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['label'], self.tag.label) + self.assertEqual(response.data['label'], self.test_tag.label) def _request_api_document_tag_list_view(self): return self.get( viewname='rest_api:document-tag-list', - kwargs={'document_pk': self.document.pk} + kwargs={'document_id': self.test_document.pk} ) def test_document_tag_list_view_no_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) response = self._request_api_document_tag_list_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_document_tag_list_view_with_document_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.document, permission=permission_tag_view) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_document, permission=permission_tag_view) response = self._request_api_document_tag_list_view() self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.data['count'], 0) def test_document_tag_list_view_with_tag_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.tag, permission=permission_tag_view) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_tag, permission=permission_tag_view) response = self._request_api_document_tag_list_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) def test_document_tag_list_view_with_full_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.document, permission=permission_tag_view) - self.grant_access(obj=self.tag, permission=permission_tag_view) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_document, permission=permission_tag_view) + self.grant_access(obj=self.test_tag, permission=permission_tag_view) response = self._request_api_document_tag_list_view() self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertEqual(response.data['results'][0]['label'], self.tag.label) + self.assertEqual( + response.data['results'][0]['label'], self.test_tag.label + ) def _request_api_document_tag_remove_view(self): return self.delete( viewname='rest_api:document-tag-detail', kwargs={ - 'document_pk': self.document.pk, 'tag_pk': self.tag.pk + 'document_id': self.test_document.pk, 'tag_id': self.test_tag.pk } ) def test_document_tag_remove_view_no_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) response = self._request_api_document_tag_remove_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertTrue(self.tag in self.document.tags.all()) + self.assertTrue(self.test_tag in self.test_document.tags.all()) self.assertEqual(Tag.objects.all().count(), 1) def test_document_tag_remove_view_with_document_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.document, permission=permission_tag_remove) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_document, permission=permission_tag_remove) response = self._request_api_document_tag_remove_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertTrue(self.tag in self.document.tags.all()) + self.assertTrue(self.test_tag in self.test_document.tags.all()) self.assertEqual(Tag.objects.all().count(), 1) def test_document_tag_remove_view_with_tag_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) - self.grant_access(obj=self.tag, permission=permission_tag_remove) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_tag, permission=permission_tag_remove) response = self._request_api_document_tag_remove_view() self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) - self.assertTrue(self.tag in self.document.tags.all()) + self.assertTrue(self.test_tag in self.test_document.tags.all()) self.assertEqual(Tag.objects.all().count(), 1) def test_document_tag_remove_view_with_full_access(self): - self._create_tag() - self.document = self.upload_document() - self.tag.documents.add(self.document) + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) self.grant_access( - obj=self.document, permission=permission_tag_remove + obj=self.test_document, permission=permission_tag_remove ) - self.grant_access(obj=self.tag, permission=permission_tag_remove) + self.grant_access(obj=self.test_tag, permission=permission_tag_remove) response = self._request_api_document_tag_remove_view() self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) - self.assertFalse(self.tag in self.document.tags.all()) + self.assertFalse(self.test_tag in self.test_document.tags.all()) self.assertEqual(Tag.objects.all().count(), 1) + + +class TagDocumentAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): + auto_upload_document = False + + def _request_api_tag_document_list_view(self): + return self.get( + viewname='rest_api:tag-document-list', + kwargs={'tag_id': self.test_tag.pk} + ) + + def test_tag_document_list_view_no_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + response = self._request_api_tag_document_list_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_tag_document_list_view_with_tag_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_tag, permission=permission_tag_view) + response = self._request_api_tag_document_list_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual(response.data['count'], 0) + + def test_tag_document_list_view_with_document_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access( + obj=self.test_document, permission=permission_document_view + ) + response = self._request_api_tag_document_list_view() + self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) + + def test_tag_document_list_view_with_access(self): + self._create_test_tag() + self.test_document = self.upload_document() + self.test_tag.documents.add(self.test_document) + self.grant_access(obj=self.test_tag, permission=permission_tag_view) + self.grant_access( + obj=self.test_document, permission=permission_document_view + ) + response = self._request_api_tag_document_list_view() + self.assertEqual(response.status_code, status.HTTP_200_OK) + self.assertEqual( + response.data['results'][0]['uuid'], + force_text(self.test_document.uuid) + ) diff --git a/mayan/apps/tags/tests/test_events.py b/mayan/apps/tags/tests/test_events.py index 33fa2bf95d..46fcce25e8 100644 --- a/mayan/apps/tags/tests/test_events.py +++ b/mayan/apps/tags/tests/test_events.py @@ -10,7 +10,7 @@ from ..permissions import permission_tag_create, permission_tag_edit from .mixins import TagTestMixin - +#TODO: Add tests for event_tag_remove and event_tag_attach class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase): def setUp(self): super(TagEventsTestCase, self).setUp() @@ -38,10 +38,10 @@ class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase): self.assertEqual(event.verb, event_tag_created.id) self.assertEqual(event.target, tag) - self.assertEqual(event.actor, self.user) + self.assertEqual(event.actor, self._test_case_user) def test_tag_edit_event_no_permissions(self): - self._create_tag() + self._create_test_tag() Action.objects.all().delete() response = self._request_tag_edit_view() @@ -49,11 +49,11 @@ class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase): self.assertEqual(Action.objects.count(), 0) def test_tag_edit_event_with_access(self): - self._create_tag() + self._create_test_tag() Action.objects.all().delete() self.grant_access( - permission=permission_tag_edit, obj=self.tag + permission=permission_tag_edit, obj=self.test_tag ) response = self._request_tag_edit_view() @@ -63,5 +63,5 @@ class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase): event = Action.objects.first() self.assertEqual(event.verb, event_tag_edited.id) - self.assertEqual(event.target, self.tag) - self.assertEqual(event.actor, self.user) + self.assertEqual(event.target, self.test_tag) + self.assertEqual(event.actor, self._test_case_user) diff --git a/mayan/apps/tags/tests/test_indexing.py b/mayan/apps/tags/tests/test_indexing.py index 415ec251d0..ddd7925e16 100644 --- a/mayan/apps/tags/tests/test_indexing.py +++ b/mayan/apps/tags/tests/test_indexing.py @@ -11,9 +11,10 @@ from .literals import ( TEST_TAG_COLOR, TEST_TAG_LABEL, TEST_TAG_INDEX_HAS_TAG, TEST_TAG_INDEX_NO_TAG, TEST_TAG_INDEX_NODE_TEMPLATE ) +from .mixins import TagTestMixin -class TagSignalIndexingTestCase(DocumentTestMixin, BaseTestCase): +class TagSignalIndexingTestCase(TagTestMixin, DocumentTestMixin, BaseTestCase): auto_upload_document = False def test_tag_indexing(self): @@ -27,7 +28,8 @@ class TagSignalIndexingTestCase(DocumentTestMixin, BaseTestCase): link_documents=True ) - tag = Tag.objects.create(color=TEST_TAG_COLOR, label=TEST_TAG_LABEL) + self._create_test_tag() + self.document = self.upload_document() self.assertTrue( @@ -36,7 +38,7 @@ class TagSignalIndexingTestCase(DocumentTestMixin, BaseTestCase): ).documents.all() ) - tag.documents.add(self.document) + self.test_tag.documents.add(self.document) self.assertTrue( self.document in IndexInstanceNode.objects.get( @@ -44,7 +46,7 @@ class TagSignalIndexingTestCase(DocumentTestMixin, BaseTestCase): ).documents.all() ) - tag.delete() + self.test_tag.delete() self.assertTrue( self.document in IndexInstanceNode.objects.get( diff --git a/mayan/apps/tags/tests/test_links.py b/mayan/apps/tags/tests/test_links.py new file mode 100644 index 0000000000..8ef56f5dcb --- /dev/null +++ b/mayan/apps/tags/tests/test_links.py @@ -0,0 +1,41 @@ +from __future__ import unicode_literals + +from django.urls import reverse + +from mayan.apps.documents.tests import GenericDocumentViewTestCase + +from ..links import link_document_tag_list +from ..permissions import permission_tag_view + +from .mixins import TagTestMixin + + +class DocumentLinksTestCase(TagTestMixin, GenericDocumentViewTestCase): + def _request_document_tag_list_link(self): + self.add_test_view(test_object=self.document) + context = self.get_test_view() + return link_document_tag_list.resolve(context=context) + + def test_document_tag_list_no_permission(self): + self._create_test_tag() + resolved_link = self._request_document_tag_list_link() + self.assertEqual(resolved_link, None) + + def test_document_tag_list_with_full_access(self): + self._create_test_tag() + self.grant_access( + obj=self.document, permission=permission_tag_view + ) + self.grant_access( + obj=self.test_tag, permission=permission_tag_view + ) + resolved_link = self._request_document_tag_list_link() + + self.assertNotEqual(resolved_link, None) + self.assertEqual( + resolved_link.url, + reverse( + viewname='tags:document_tag_list', + kwargs={'document_id': self.document.pk} + ) + ) diff --git a/mayan/apps/tags/tests/test_views.py b/mayan/apps/tags/tests/test_views.py index 7f699357ca..bdfa59e73a 100644 --- a/mayan/apps/tags/tests/test_views.py +++ b/mayan/apps/tags/tests/test_views.py @@ -1,5 +1,7 @@ from __future__ import unicode_literals +from django.utils.encoding import force_text + from mayan.apps.common.tests import GenericViewTestCase from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.tests import GenericDocumentViewTestCase @@ -14,21 +16,17 @@ from .literals import ( TEST_TAG_COLOR, TEST_TAG_COLOR_EDITED, TEST_TAG_LABEL, TEST_TAG_LABEL_EDITED ) -from .mixins import TagTestMixin +from .mixins import TagTestMixin, TagViewTestMixin -class TagViewTestCase(TagTestMixin, GenericViewTestCase): +class TagViewTestCase(TagViewTestMixin, TagTestMixin, GenericViewTestCase): def test_tag_create_view_no_permissions(self): - self.login_user() - response = self._request_tag_create_view() self.assertEqual(response.status_code, 403) self.assertEqual(Tag.objects.count(), 0) def test_tag_create_view_with_permissions(self): - self.login_user() - self.grant_permission(permission=permission_tag_create) response = self._request_tag_create_view() self.assertEqual(response.status_code, 302) @@ -39,18 +37,16 @@ class TagViewTestCase(TagTestMixin, GenericViewTestCase): self.assertEqual(tag.color, TEST_TAG_COLOR) def test_tag_delete_view_no_permissions(self): - self.login_user() - self._create_tag() + self._create_test_tag() response = self._request_tag_delete_view() - self.assertEqual(response.status_code, 302) + self.assertEqual(response.status_code, 404) self.assertEqual(Tag.objects.count(), 1) def test_tag_delete_view_with_access(self): - self.login_user() - self._create_tag() + self._create_test_tag() - self.grant_access(obj=self.tag, permission=permission_tag_delete) + self.grant_access(obj=self.test_tag, permission=permission_tag_delete) response = self._request_tag_delete_view() self.assertEqual(response.status_code, 302) @@ -58,190 +54,295 @@ class TagViewTestCase(TagTestMixin, GenericViewTestCase): self.assertEqual(Tag.objects.count(), 0) def test_tag_multiple_delete_view_no_permissions(self): - self.login_user() - self._create_tag() + self._create_test_tag() - response = self._request_multiple_delete_view() - self.assertEqual(response.status_code, 302) + response = self._request_tag_multiple_delete_view() + self.assertEqual(response.status_code, 404) self.assertEqual(Tag.objects.count(), 1) def test_tag_multiple_delete_view_with_access(self): - self.login_user() - self._create_tag() + self._create_test_tag() - self.grant_access(obj=self.tag, permission=permission_tag_delete) + self.grant_access(obj=self.test_tag, permission=permission_tag_delete) - response = self._request_multiple_delete_view() + response = self._request_tag_multiple_delete_view() self.assertEqual(response.status_code, 302) - self.assertEqual(Tag.objects.count(), 0) def test_tag_edit_view_no_permissions(self): - self.login_user() - self._create_tag() + self._create_test_tag() - response = self._request_edit_tag_view() + response = self._request_tag_edit_view() self.assertEqual(response.status_code, 404) - tag = Tag.objects.get(pk=self.tag.pk) + tag = Tag.objects.get(pk=self.test_tag.pk) self.assertEqual(tag.label, TEST_TAG_LABEL) self.assertEqual(tag.color, TEST_TAG_COLOR) def test_tag_edit_view_with_access(self): - self.login_user() - self._create_tag() + self._create_test_tag() - self.grant_access(obj=self.tag, permission=permission_tag_edit) + self.grant_access(obj=self.test_tag, permission=permission_tag_edit) - response = self._request_edit_tag_view() + response = self._request_tag_edit_view() self.assertEqual(response.status_code, 302) - tag = Tag.objects.get(pk=self.tag.pk) + tag = Tag.objects.get(pk=self.test_tag.pk) self.assertEqual(tag.label, TEST_TAG_LABEL_EDITED) self.assertEqual(tag.color, TEST_TAG_COLOR_EDITED) -class TagDocumentsViewTestCase(TagTestMixin, GenericDocumentViewTestCase): - def _request_document_list_view(self): - return self.get(viewname='documents:document_list') +class TagDocumentsViewTestCase(TagViewTestMixin, TagTestMixin, GenericDocumentViewTestCase): + def test_document_tag_attach_view_no_permission(self): + self._create_test_tag() - def test_document_tags_widget_no_permissions(self): - self.login_user() - self._create_tag() + response = self._request_document_tag_multiple_attach_view() + self.assertEqual(response.status_code, 404) + self.assertEqual(self.test_document.tags.count(), 0) - self.tag.documents.add(self.document) - response = self._request_document_list_view() - self.assertNotContains( - response=response, text=TEST_TAG_LABEL, status_code=200 - ) + def test_document_tag_attach_view_with_document_access(self): + self._create_test_tag() - def test_document_tags_widget_with_access(self): - self.login_user() - self._create_tag() - - self.tag.documents.add(self.document) - - self.grant_access(obj=self.tag, permission=permission_tag_view) self.grant_access( - obj=self.document, permission=permission_document_view + obj=self.test_document, permission=permission_tag_attach ) - - response = self._request_document_list_view() + response = self._request_document_tag_multiple_attach_view() self.assertContains( - response=response, text=TEST_TAG_LABEL, status_code=200 + response=response, text=force_text(self.test_document), + status_code=200 + ) + self.assertNotContains( + response=response, text=force_text(self.test_tag), status_code=200 ) - def test_document_attach_tag_view_no_permission(self): - self.login_user() - self._create_tag() + self.assertEqual(self.test_document.tags.count(), 0) - self.assertEqual(self.document.tags.count(), 0) + def test_document_tag_attach_view_with_tag_access(self): + self._create_test_tag() - self.grant_access(obj=self.tag, permission=permission_tag_attach) + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + response = self._request_document_tag_multiple_attach_view() + self.assertEqual(response.status_code, 404) - response = self._request_attach_tag_view() - # Redirect to previous URL and show warning message about having to - # select at least one object. - self.assertEqual(response.status_code, 302) - self.assertEqual(self.document.tags.count(), 0) + self.assertEqual(self.test_document.tags.count(), 0) - def test_document_attach_tag_view_with_access(self): - self.login_user() - self._create_tag() + def test_document_tag_attach_view_with_full_access(self): + self._create_test_tag() - self.assertEqual(self.document.tags.count(), 0) - - self.grant_access(obj=self.document, permission=permission_tag_attach) - self.grant_access(obj=self.tag, permission=permission_tag_attach) - # permission_tag_view is needed because the form filters the - # choices - self.grant_access(obj=self.tag, permission=permission_tag_view) - - response = self._request_attach_tag_view() + self.grant_access( + obj=self.test_document, permission=permission_tag_attach + ) + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + response = self._request_document_tag_multiple_attach_view() self.assertEqual(response.status_code, 302) self.assertQuerysetEqual( - self.document.tags.all(), (repr(self.tag),) + self.test_document.tags.all(), (repr(self.test_tag),) ) - def test_document_multiple_attach_tag_view_no_permission(self): - self.login_user() - self._create_tag() - self.grant_permission(permission=permission_tag_view) + def test_document_single_tag_attach_view_with_full_access(self): + """ + Test to make sure only the tag is attached to the selected document + """ + self._create_test_tag() + self.test_document_2 = self.upload_document() - response = self._request_multiple_attach_tag_view() - self.assertEqual(response.status_code, 200) - self.assertEqual(self.document.tags.count(), 0) - - def test_document_multiple_attach_tag_view_with_access(self): - self.login_user() - self._create_tag() - - self.grant_access(obj=self.document, permission=permission_tag_attach) - self.grant_access(obj=self.tag, permission=permission_tag_attach) - - # permission_tag_view is needed because the form filters the - # choices - self.grant_access(obj=self.tag, permission=permission_tag_view) - - response = self._request_multiple_attach_tag_view() + self.grant_access( + obj=self.test_document, permission=permission_tag_attach + ) + self.grant_access( + obj=self.test_document_2, permission=permission_tag_attach + ) + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + response = self._request_document_tag_multiple_attach_view() self.assertEqual(response.status_code, 302) self.assertQuerysetEqual( - self.document.tags.all(), (repr(self.tag),) + self.test_document.tags.all(), (repr(self.test_tag),) ) - def test_single_document_multiple_tag_remove_view_no_permissions(self): - self.login_user() - self._create_tag() + self.assertEqual(self.test_document_2.tags.count(), 0) - self.document.tags.add(self.tag) + def test_document_multiple_tag_attach_view_no_permission(self): + self._create_test_tag() - self.grant_access(obj=self.tag, permission=permission_tag_view) + response = self._request_document_multiple_tag_multiple_attach_view() + self.assertEqual(response.status_code, 404) + self.assertEqual(self.test_document.tags.count(), 0) - response = self._request_single_document_multiple_tag_remove_view() - self.assertEqual(response.status_code, 200) + def test_document_multiple_tag_attach_view_with_document_access(self): + self._create_test_tag() - self.assertQuerysetEqual(self.document.tags.all(), (repr(self.tag),)) + self.grant_access( + obj=self.test_document, permission=permission_tag_attach + ) - def test_single_document_multiple_tag_remove_view_with_access(self): - self.login_user() - self._create_tag() + response = self._request_document_multiple_tag_multiple_attach_view() - self.document.tags.add(self.tag) + self.assertContains( + response=response, text=force_text(self.test_document), + status_code=200 + ) + self.assertNotContains( + response=response, text=force_text(self.test_tag), status_code=200 + ) - self.grant_access(obj=self.document, permission=permission_tag_remove) - self.grant_access(obj=self.tag, permission=permission_tag_remove) - self.grant_access(obj=self.tag, permission=permission_tag_view) + self.assertEqual(self.test_document.tags.count(), 0) - response = self._request_single_document_multiple_tag_remove_view() + def test_document_multiple_tag_attach_view_with_tag_access(self): + self._create_test_tag() + + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + + response = self._request_document_multiple_tag_multiple_attach_view() + self.assertEqual(response.status_code, 404) + self.assertEqual(self.test_document.tags.count(), 0) + + def test_document_multiple_tag_attach_view_with_full_access(self): + self._create_test_tag() + + self.grant_access( + obj=self.test_document, permission=permission_tag_attach + ) + self.grant_access(obj=self.test_tag, permission=permission_tag_attach) + + response = self._request_document_multiple_tag_multiple_attach_view() self.assertEqual(response.status_code, 302) - self.assertEqual(self.document.tags.count(), 0) + self.assertQuerysetEqual( + self.test_document.tags.all(), (repr(self.test_tag),) + ) - def test_multiple_documents_selection_tag_remove_view_no_permissions(self): - self.login_user() - self._create_tag() + def test_document_tag_multiple_remove_view_no_permissions(self): + self._create_test_tag() - self.document.tags.add(self.tag) + self.test_document.tags.add(self.test_tag) - self.grant_access(obj=self.tag, permission=permission_tag_view) + response = self._request_document_tag_multiple_remove_view() + self.assertEqual(response.status_code, 404) - response = self._request_multiple_documents_selection_tag_remove_view() - self.assertEqual(response.status_code, 200) + self.assertQuerysetEqual( + self.test_document.tags.all(), (repr(self.test_tag),) + ) - self.assertQuerysetEqual(self.document.tags.all(), (repr(self.tag),)) + def test_document_tag_multiple_remove_view_with_document_access(self): + self._create_test_tag() - def test_multiple_documents_selection_tag_remove_view_with_access(self): - self.login_user() - self._create_tag() + self.test_document.tags.add(self.test_tag) - self.document.tags.add(self.tag) + self.grant_access( + obj=self.test_document, permission=permission_tag_remove + ) + response = self._request_document_tag_multiple_remove_view() + self.assertNotContains( + response=response, text=self.test_tag, status_code=200 + ) + self.assertContains( + response=response, text=self.test_document, status_code=200 + ) - self.grant_access(obj=self.document, permission=permission_tag_remove) - self.grant_access(obj=self.tag, permission=permission_tag_remove) - self.grant_access(obj=self.tag, permission=permission_tag_view) + self.assertEqual(self.test_document.tags.count(), 1) - response = self._request_multiple_documents_selection_tag_remove_view() + def test_document_tag_multiple_remove_view_with_tag_access(self): + self._create_test_tag() + + self.test_document.tags.add(self.test_tag) + + self.grant_access(obj=self.test_tag, permission=permission_tag_remove) + + response = self._request_document_tag_multiple_remove_view() + self.assertEqual(response.status_code, 404) + + self.assertEqual(self.test_document.tags.count(), 1) + + def test_document_tag_multiple_remove_view_with_full_access(self): + self._create_test_tag() + + self.test_document.tags.add(self.test_tag) + + self.grant_access( + obj=self.test_document, permission=permission_tag_remove + ) + self.grant_access(obj=self.test_tag, permission=permission_tag_remove) + + response = self._request_document_tag_multiple_remove_view() self.assertEqual(response.status_code, 302) - self.assertEqual(self.document.tags.count(), 0) + self.assertEqual(self.test_document.tags.count(), 0) + + def test_document_tags_list_no_permissions(self): + self._create_test_tag() + + self.test_tag.documents.add(self.test_document) + + response = self._request_document_tag_list_view() + self.assertNotContains( + response=response, text=force_text(self.test_tag), status_code=404 + ) + + def test_document_tags_list_with_document_access(self): + self._create_test_tag() + + self.test_tag.documents.add(self.test_document) + + self.grant_access( + obj=self.test_document, permission=permission_tag_view + ) + + response = self._request_document_tag_list_view() + self.assertNotContains( + response=response, text=force_text(self.test_tag), status_code=200 + ) + + def test_document_tags_list_with_tag_access(self): + self._create_test_tag() + + self.test_tag.documents.add(self.test_document) + + self.grant_access(obj=self.test_tag, permission=permission_tag_view) + + response = self._request_document_tag_list_view() + self.assertNotContains( + response=response, text=force_text(self.test_tag), status_code=404 + ) + + def test_document_tags_list_with_full_access(self): + self._create_test_tag() + + self.test_tag.documents.add(self.test_document) + + self.grant_access(obj=self.test_tag, permission=permission_tag_view) + self.grant_access( + obj=self.test_document, permission=permission_tag_view + ) + + response = self._request_document_tag_list_view() + self.assertContains( + response=response, text=force_text(self.test_tag), status_code=200 + ) + + def test_document_multiple_tag_remove_view_no_permissions(self): + self._create_test_tag() + + self.test_document.tags.add(self.test_tag) + + response = self._request_document_multiple_tag_multiple_remove_view() + self.assertEqual(response.status_code, 404) + + self.assertQuerysetEqual( + self.test_document.tags.all(), (repr(self.test_tag),) + ) + + def test_document_multiple_tag_remove_view_with_full_access(self): + self._create_test_tag() + + self.test_document.tags.add(self.test_tag) + + self.grant_access( + obj=self.test_document, permission=permission_tag_remove + ) + self.grant_access(obj=self.test_tag, permission=permission_tag_remove) + + response = self._request_document_multiple_tag_multiple_remove_view() + self.assertEqual(response.status_code, 302) + + self.assertEqual(self.test_document.tags.count(), 0) diff --git a/mayan/apps/tags/tests/test_wizard_steps.py b/mayan/apps/tags/tests/test_wizard_steps.py index edc1732ce5..9acfd0971b 100644 --- a/mayan/apps/tags/tests/test_wizard_steps.py +++ b/mayan/apps/tags/tests/test_wizard_steps.py @@ -13,12 +13,12 @@ from mayan.apps.sources.tests.literals import ( from ..models import Tag from .literals import TEST_TAG_COLOR, TEST_TAG_LABEL +from .mixins import TagTestMixin -class TaggedDocumentUploadTestCase(GenericDocumentViewTestCase): +class TaggedDocumentUploadTestCase(TagTestMixin, GenericDocumentViewTestCase): def setUp(self): super(TaggedDocumentUploadTestCase, self).setUp() - self.login_user() self.source = WebFormSource.objects.create( enabled=True, label=TEST_SOURCE_LABEL, uncompress=TEST_SOURCE_UNCOMPRESS_N @@ -34,20 +34,15 @@ class TaggedDocumentUploadTestCase(GenericDocumentViewTestCase): data={ 'document_type_id': self.document_type.pk, 'source-file': file_object, - 'tags': self.tag.pk + 'tags': self.test_tag.pk } ) - def _create_tag(self): - self.tag = Tag.objects.create( - color=TEST_TAG_COLOR, label=TEST_TAG_LABEL - ) - def test_upload_interactive_view_with_access(self): - self._create_tag() + self._create_test_tag() self.grant_access( permission=permission_document_create, obj=self.document_type ) response = self._request_upload_interactive_document_create_view() self.assertEqual(response.status_code, 302) - self.assertTrue(self.tag in Document.objects.first().tags.all()) + self.assertTrue(self.test_tag in Document.objects.first().tags.all()) diff --git a/mayan/apps/tags/urls.py b/mayan/apps/tags/urls.py index 67857d8a46..66ac0b2659 100644 --- a/mayan/apps/tags/urls.py +++ b/mayan/apps/tags/urls.py @@ -2,66 +2,75 @@ from __future__ import unicode_literals from django.conf.urls import url -from .api_views import ( - APIDocumentTagView, APIDocumentTagListView, APITagDocumentListView, - APITagListView, APITagView -) +#from .api_views import ( +# APIDocumentTagView, APIDocumentTagListView, APITagDocumentListView, +# APITagListView, APITagView +#) +from .api_views import DocumentTagViewSet, TagViewSet + from .views import ( DocumentTagListView, TagAttachActionView, TagCreateView, - TagDeleteActionView, TagEditView, TagListView, TagRemoveActionView, - TagTaggedItemListView + TagDeleteActionView, TagDocumentListView, TagEditView, TagListView, + TagRemoveActionView ) urlpatterns = [ - url(regex=r'^tags/list/$', name='tag_list', view=TagListView.as_view()), + url( + regex=r'^documents/(?P\d+)/tags/$', + name='document_tag_list', view=DocumentTagListView.as_view() + ), + url( + regex=r'^documents/(?P\d+)/tags/multiple/attach/$', + name='document_tag_multiple_attach', view=TagAttachActionView.as_view() + ), + url( + regex=r'^documents/(?P\d+)/tags/multiple/remove/$', + name='document_tag_multiple_remove', + view=TagRemoveActionView.as_view() + ), + url( + regex=r'^documents/multiple/attach/$', + name='document_multiple_tag_multiple_attach', + view=TagAttachActionView.as_view() + ), + url( + regex=r'^documents/multiple/tags/remove/$', + name='document_multiple_tag_multiple_remove', + view=TagRemoveActionView.as_view() + ), + url(regex=r'^tags/$', name='tag_list', view=TagListView.as_view()), url( regex=r'^tags/create/$', name='tag_create', view=TagCreateView.as_view() ), url( - regex=r'^tags/(?P\d+)/delete/$', name='tag_delete', + regex=r'^tags/(?P\d+)/delete/$', name='tag_delete', view=TagDeleteActionView.as_view() ), url( - regex=r'^tags/(?P\d+)/edit/$', name='tag_edit', + regex=r'^tags/(?P\d+)/edit/$', name='tag_edit', view=TagEditView.as_view() ), url( - regex=r'^tags/(?P\d+)/documents/$', - name='tag_tagged_item_list', view=TagTaggedItemListView.as_view() + regex=r'^tags/(?P\d+)/documents/$', + name='tag_document_list', view=TagDocumentListView.as_view() ), url( regex=r'^tags/multiple/delete/$', name='tag_multiple_delete', view=TagDeleteActionView.as_view() - ), - url( - regex=r'^tags/multiple/remove/document/(?P\d+)/$', - name='single_document_multiple_tag_remove', - view=TagRemoveActionView.as_view() - ), - url( - regex=r'^tags/multiple/remove/document/multiple/$', - name='multiple_documents_selection_tag_remove', - view=TagRemoveActionView.as_view() - ), - - url( - regex=r'^documents/(?P\d+)/attach/$', - name='tag_attach', view=TagAttachActionView.as_view() - ), - url( - regex=r'^documents/multiple/attach//$', - name='multiple_documents_tag_attach', - view=TagAttachActionView.as_view() - ), - - url( - regex=r'^documents/(?P\d+)/tags/$', name='document_tags', - view=DocumentTagListView.as_view(), - ), + ) ] -api_urls = [ + +api_router_entries = ( + {'prefix': r'tags', 'viewset': TagViewSet, 'basename': 'tag'}, + { + 'prefix': r'documents/(?P\d+)/tags', + 'viewset': DocumentTagViewSet, 'basename': 'document_tag' + }, +) + +""" url( regex=r'^tags/(?P\d+)/documents/$', name='tag-document-list', view=APITagDocumentListView.as_view(), @@ -79,4 +88,4 @@ api_urls = [ regex=r'^documents/(?P\d+)/tags/(?P[0-9]+)/$', name='document-tag-detail', view=APIDocumentTagView.as_view() ), -] +""" diff --git a/mayan/apps/tags/views.py b/mayan/apps/tags/views.py index 1c0e8050f4..d36149333c 100644 --- a/mayan/apps/tags/views.py +++ b/mayan/apps/tags/views.py @@ -9,19 +9,20 @@ from django.urls import reverse_lazy from django.utils.translation import ugettext_lazy as _, ungettext from mayan.apps.acls.models import AccessControlList -from mayan.apps.common.views import ( +from mayan.apps.common.generics import ( MultipleObjectFormActionView, MultipleObjectConfirmActionView, SingleObjectCreateView, SingleObjectEditView, SingleObjectListView ) +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.documents.models import Document from mayan.apps.documents.views import DocumentListView from mayan.apps.documents.permissions import permission_document_view from .forms import TagMultipleSelectionForm from .icons import ( - icon_menu_tags, icon_tag_delete_submit, icon_tag_remove_submit + icon_menu_tags, icon_tag_delete_submit, icon_document_tag_multiple_remove_submit ) -from .links import link_tag_attach, link_tag_create +from .links import link_document_tag_multiple_attach, link_tag_create from .models import Tag from .permissions import ( permission_tag_attach, permission_tag_create, permission_tag_delete, @@ -34,7 +35,7 @@ logger = logging.getLogger(__name__) class TagAttachActionView(MultipleObjectFormActionView): form_class = TagMultipleSelectionForm model = Document - pk_url_kwarg = 'document_pk' + pk_url_kwarg = 'document_id' object_permission = permission_tag_attach success_message = _('Tag attach request performed on %(count)d document') success_message_plural = _( @@ -42,7 +43,7 @@ class TagAttachActionView(MultipleObjectFormActionView): ) def get_extra_context(self): - queryset = self.get_queryset() + queryset = self.get_object_list() result = { 'submit_label': _('Attach'), @@ -66,7 +67,7 @@ class TagAttachActionView(MultipleObjectFormActionView): return result def get_form_extra_kwargs(self): - queryset = self.get_queryset() + queryset = self.get_object_list() result = { 'help_text': _('Tags to be attached.'), 'permission': permission_tag_attach, @@ -85,44 +86,45 @@ class TagAttachActionView(MultipleObjectFormActionView): return result def get_post_action_redirect(self): - queryset = self.get_queryset() + queryset = self.get_object_list() if queryset.count() == 1: return reverse( - viewname='tags:document_tags', kwargs={ - 'document_pk': queryset.first().pk + viewname='tags:document_tag_list', kwargs={ + 'document_id': queryset.first().pk } ) else: return super(TagAttachActionView, self).get_post_action_redirect() def object_action(self, form, instance): - attached_tags = instance.get_tags() + attached_tags = instance.get_tags( + permission=permission_tag_attach, user=self.request.user + ) - for tag in form.cleaned_data['tags']: - AccessControlList.objects.check_access( - obj=tag, permissions=permission_tag_attach, - user=self.request.user - ) + tag_list = AccessControlList.objects.restrict_queryset( + permission=permission_tag_attach, queryset=form.cleaned_data['tags'], + user=self.request.user + ) + for tag in tag_list: if tag in attached_tags: messages.warning( - self.request, _( + message=_( 'Document "%(document)s" is already tagged as ' '"%(tag)s"' ) % { 'document': instance, 'tag': tag - } + }, request=self.request ) else: tag.attach_to(document=instance, user=self.request.user) messages.success( - self.request, - _( + message=_( 'Tag "%(tag)s" attached successfully to document ' '"%(document)s".' ) % { 'document': instance, 'tag': tag - } + }, request=self.request ) @@ -130,7 +132,7 @@ class TagCreateView(SingleObjectCreateView): extra_context = {'title': _('Create tag')} fields = ('label', 'color') model = Tag - post_action_redirect = reverse_lazy('tags:tag_list') + post_action_redirect = reverse_lazy(viewname='tags:tag_list') view_permission = permission_tag_create def get_save_extra_data(self): @@ -139,8 +141,8 @@ class TagCreateView(SingleObjectCreateView): class TagDeleteActionView(MultipleObjectConfirmActionView): model = Tag - pk_url_kwarg = 'tag_pk' - post_action_redirect = reverse_lazy('tags:tag_list') + pk_url_kwarg = 'tag_id' + post_action_redirect = reverse_lazy(viewname='tags:tag_list') object_permission = permission_tag_delete success_message = _('Tag delete request performed on %(count)d tag') success_message_plural = _( @@ -155,9 +157,9 @@ class TagDeleteActionView(MultipleObjectConfirmActionView): 'submit_icon_class': icon_tag_delete_submit, 'submit_label': _('Delete'), 'title': ungettext( - 'Delete the selected tag?', - 'Delete the selected tags?', - queryset.count() + singular='Delete the selected tag?', + plural='Delete the selected tags?', + number=queryset.count() ) } @@ -175,13 +177,14 @@ class TagDeleteActionView(MultipleObjectConfirmActionView): try: instance.delete() messages.success( - self.request, _('Tag "%s" deleted successfully.') % instance + message=_('Tag "%s" deleted successfully.') % instance, + request=self.request ) except Exception as exception: messages.error( - self.request, _('Error deleting tag "%(tag)s": %(error)s') % { + message=_('Error deleting tag "%(tag)s": %(error)s') % { 'tag': instance, 'error': exception - } + }, request=self.request ) @@ -189,9 +192,8 @@ class TagEditView(SingleObjectEditView): fields = ('label', 'color') model = Tag object_permission = permission_tag_edit - object_permission_raise_404 = True - pk_url_kwarg = 'tag_pk' - post_action_redirect = reverse_lazy('tags:tag_list') + pk_url_kwarg = 'tag_id' + post_action_redirect = reverse_lazy(viewname='tags:tag_list') def get_extra_context(self): return { @@ -222,22 +224,24 @@ class TagListView(SingleObjectListView): 'title': _('Tags'), } - def get_object_list(self): - return self.get_tag_queryset() - - def get_tag_queryset(self): + def get_source_queryset(self): + #return self.get_tag_queryset() return Tag.objects.all() + #def get_tag_queryset(self): + # return Tag.objects.all() -class TagTaggedItemListView(DocumentListView): - def get_tag(self): - return get_object_or_404(klass=Tag, pk=self.kwargs['tag_pk']) + +class TagDocumentListView(ExternalObjectMixin, DocumentListView): + external_object_class = Tag + external_object_permission = permission_tag_view + external_object_pk_url_kwarg = 'tag_id' def get_document_queryset(self): - return self.get_tag().documents.all() + return self.get_tag().get_documents(user=self.request.user).all() def get_extra_context(self): - context = super(TagTaggedItemListView, self).get_extra_context() + context = super(TagDocumentListView, self).get_extra_context() context.update( { 'column_class': 'col-xs-12 col-sm-6 col-md-4 col-lg-3', @@ -247,57 +251,56 @@ class TagTaggedItemListView(DocumentListView): ) return context + def get_tag(self): + return self.get_external_object() -class DocumentTagListView(TagListView): - def dispatch(self, request, *args, **kwargs): - self.document = get_object_or_404( - klass=Document, pk=self.kwargs['document_pk'] - ) - AccessControlList.objects.check_access( - permissions=permission_document_view, user=request.user, - obj=self.document - ) - - return super(DocumentTagListView, self).dispatch( - request, *args, **kwargs - ) +class DocumentTagListView(ExternalObjectMixin, TagListView): + external_object_class = Document + external_object_permission = permission_tag_view + external_object_pk_url_kwarg = 'document_id' def get_extra_context(self): context = super(DocumentTagListView, self).get_extra_context() context.update( { 'hide_link': True, - 'no_results_main_link': link_tag_attach.resolve( + 'no_results_main_link': link_document_tag_multiple_attach.resolve( context=RequestContext( - self.request, {'object': self.document} + self.request, {'object': self.get_external_object()} ) ), 'no_results_title': _('Document has no tags attached'), - 'object': self.document, - 'title': _('Tags for document: %s') % self.document, + 'object': self.get_external_object(), + 'title': _( + 'Tags for document: %s' + ) % self.get_external_object(), } ) return context - def get_tag_queryset(self): - return self.document.get_tags().all() + #def get_tag_queryset(self): + def get_source_queryset(self): + return self.get_external_object().get_tags( + permission=permission_tag_view, user=self.request.user + ).all() class TagRemoveActionView(MultipleObjectFormActionView): form_class = TagMultipleSelectionForm model = Document object_permission = permission_tag_remove + pk_url_kwarg = 'document_id' success_message = _('Tag remove request performed on %(count)d document') success_message_plural = _( 'Tag remove request performed on %(count)d documents' ) def get_extra_context(self): - queryset = self.get_queryset() + queryset = self.get_object_list() result = { - 'submit_icon_class': icon_tag_remove_submit, + 'submit_icon_class': icon_document_tag_multiple_remove_submit, 'submit_label': _('Remove'), 'title': ungettext( singular='Remove tags to %(count)d document', @@ -321,7 +324,7 @@ class TagRemoveActionView(MultipleObjectFormActionView): return result def get_form_extra_kwargs(self): - queryset = self.get_queryset() + queryset = self.get_object_list() result = { 'help_text': _('Tags to be removed.'), 'permission': permission_tag_remove, @@ -338,41 +341,43 @@ class TagRemoveActionView(MultipleObjectFormActionView): return result def get_post_action_redirect(self): - queryset = self.get_queryset() + queryset = self.get_object_list() if queryset.count() == 1: return reverse( - viewname='tags:document_tags', kwargs={ - 'document_pk': queryset.first().pk + viewname='tags:document_tag_list', kwargs={ + 'document_id': queryset.first().pk } ) else: return super(TagRemoveActionView, self).get_post_action_redirect() def object_action(self, form, instance): - attached_tags = instance.get_tags() + attached_tags = instance.get_tags( + permission=permission_tag_remove, user=self.request.user + ) - for tag in form.cleaned_data['tags']: - AccessControlList.objects.check_access( - obj=tag, permissions=permission_tag_remove, - user=self.request.user - ) + tag_list = AccessControlList.objects.restrict_queryset( + permission=permission_tag_remove, + queryset=form.cleaned_data['tags'], + user=self.request.user + ) + for tag in tag_list: if tag not in attached_tags: messages.warning( - self.request, _( + message=_( 'Document "%(document)s" wasn\'t tagged as "%(tag)s' ) % { 'document': instance, 'tag': tag - } + }, request=self.request ) else: tag.remove_from(document=instance, user=self.request.user) messages.success( - self.request, - _( + message=_( 'Tag "%(tag)s" removed successfully from document ' '"%(document)s".' ) % { 'document': instance, 'tag': tag - } + }, request=self.request ) diff --git a/mayan/apps/tags/widgets.py b/mayan/apps/tags/widgets.py index 4dc95af64e..2157429dd1 100644 --- a/mayan/apps/tags/widgets.py +++ b/mayan/apps/tags/widgets.py @@ -18,39 +18,11 @@ class TagFormWidget(forms.SelectMultiple): def create_option(self, name, value, label, selected, index, subindex=None, attrs=None): result = super(TagFormWidget, self).create_option( - name=name, value=value, label='{}'.format(conditional_escape(label)), - selected=selected, index=index, subindex=subindex, attrs=attrs + attrs=attrs, index=index, + label='{}'.format(conditional_escape(label)), name=name, + selected=selected, subindex=subindex, value=value ) result['attrs']['data-color'] = self.queryset.get(pk=value).color return result - - -def widget_document_tags(document, user): - """ - A tag widget that displays the tags for the given document - """ - AccessControlList = apps.get_model( - app_label='acls', model_name='AccessControlList' - ) - - result = ['
'] - - tags = AccessControlList.objects.filter_by_access( - permission_tag_view, user, queryset=document.get_tags().all() - ) - - for tag in tags: - result.append(widget_single_tag(tag)) - - result.append('
') - - if tags: - return mark_safe(''.join(result)) - else: - return '' - - -def widget_single_tag(tag): - return render_to_string('tags/tag_widget.html', {'tag': tag}) diff --git a/mayan/apps/tags/wizard_steps.py b/mayan/apps/tags/wizard_steps.py index 84dd5c6055..5abac4bb90 100644 --- a/mayan/apps/tags/wizard_steps.py +++ b/mayan/apps/tags/wizard_steps.py @@ -22,17 +22,10 @@ class WizardStepTags(WizardStep): Tag = apps.get_model(app_label='tags', model_name='Tag') return Tag.objects.exists() - @classmethod - def get_form_kwargs(self, wizard): - return { - 'help_text': _('Tags to be attached.'), - 'user': wizard.request.user - } - @classmethod def done(cls, wizard): result = {} - cleaned_data = wizard.get_cleaned_data_for_step(cls.name) + cleaned_data = wizard.get_cleaned_data_for_step(step=cls.name) if cleaned_data: result['tags'] = [ force_text(tag.pk) for tag in cleaned_data['tags'] @@ -40,13 +33,20 @@ class WizardStepTags(WizardStep): return result + @classmethod + def get_form_kwargs(self, wizard): + return { + 'help_text': _('Tags to be attached.'), + 'user': wizard.request.user + } + @classmethod def step_post_upload_process(cls, document, querystring=None): - furl_instance = furl(querystring) + furl_instance = furl(args=querystring) Tag = apps.get_model(app_label='tags', model_name='Tag') for tag in Tag.objects.filter(pk__in=furl_instance.args.getlist('tags')): tag.documents.add(document) -WizardStep.register(WizardStepTags) +WizardStep.register(step=WizardStepTags) diff --git a/mayan/apps/tags/workflow_actions.py b/mayan/apps/tags/workflow_actions.py index b04aeef0c5..12018f98cd 100644 --- a/mayan/apps/tags/workflow_actions.py +++ b/mayan/apps/tags/workflow_actions.py @@ -38,8 +38,9 @@ class AttachTagAction(WorkflowAction): user = request.user logger.debug('user: %s', user) - queryset = AccessControlList.objects.filter_by_access( - self.permission, user, queryset=Tag.objects.all() + queryset = AccessControlList.objects.restrict_queryset( + permission=self.permission, queryset=Tag.objects.all(), + user=user ) self.fields['tags']['kwargs']['queryset'] = queryset