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