Refactor the tags app

Remove the widget from the model.

Add keyword arguments.

Separate form widgets from html widgets. HTML widgets now go
in the html_widgets module.

Update the TagMultipleSelectionForm class to be a subclass of
FilteredSelectionForm.

Move Select2 specific JavaScript from the appearence app to the
tags app.

Update tag attachment and removal view names.

Modernize tests.

Add more tests.

Consolidate repeated test code into test mixins.

Update views to comply with MERCs 5 and 6.

Use uniform nomeclature for URLs.

Update URLs parameters to use the '_id' form.

Signed-off-by: Roberto Rosario <Roberto.Rosario@mayan-edms.com>
This commit is contained in:
Roberto Rosario
2019-01-29 04:20:54 -04:00
parent ef5e0c2d86
commit b5839c0662
30 changed files with 1489 additions and 818 deletions

View File

@@ -8,10 +8,10 @@ class MayanApp {
ajaxMenusOptions: [] ajaxMenusOptions: []
} }
this.ajaxSpinnerSeletor = '#ajax-spinner';
this.ajaxExecuting = false; this.ajaxExecuting = false;
this.ajaxMenusOptions = options.ajaxMenusOptions; this.ajaxMenusOptions = options.ajaxMenusOptions;
this.ajaxMenuHashes = {}; this.ajaxMenuHashes = {};
this.ajaxSpinnerSeletor = '#ajax-spinner';
this.window = $(window); this.window = $(window);
} }
@@ -81,22 +81,6 @@ class MayanApp {
}); });
} }
static tagSelectionTemplate (tag, container) {
var $tag = $(
'<span class="label label-tag" style="background: ' + tag.element.dataset.color + ';"> ' + tag.text + '</span>'
);
container[0].style.background = tag.element.dataset.color;
return $tag;
}
static tagResultTemplate (tag) {
if (!tag.element) { return ''; }
var $tag = $(
'<span class="label label-tag" style="background: ' + tag.element.dataset.color + ';"> ' + tag.text + '</span>'
);
return $tag;
}
static updateNavbarState () { static updateNavbarState () {
var uri = new URI(window.location.hash); var uri = new URI(window.location.hash);
var uriFragment = uri.fragment(); var uriFragment = uri.fragment();
@@ -445,12 +429,6 @@ class MayanApp {
dropdownAutoWidth: true, dropdownAutoWidth: true,
width: '100%' width: '100%'
}); });
$('.select2-tags').select2({
templateSelection: MayanApp.tagSelectionTemplate,
templateResult: MayanApp.tagResultTemplate,
width: '100%'
});
} }
resizeFullHeight () { resizeFullHeight () {

View File

@@ -8,4 +8,4 @@ from .models import Tag
@admin.register(Tag) @admin.register(Tag)
class TagAdmin(admin.ModelAdmin): class TagAdmin(admin.ModelAdmin):
filter_horizontal = ('documents',) filter_horizontal = ('documents',)
list_display = ('label', 'color', 'get_preview_widget') list_display = ('label', 'color')

View File

@@ -1,14 +1,20 @@
from __future__ import absolute_import, unicode_literals 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.exceptions import ValidationError
from rest_framework.response import Response 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.models import Document
from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.permissions import permission_document_view
from mayan.apps.documents.serializers import DocumentSerializer from mayan.apps.documents.serializers import DocumentSerializer
from mayan.apps.rest_api.filters import MayanObjectPermissionsFilter 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 mayan.apps.rest_api.permissions import MayanPermission
from .models import Tag from .models import Tag
@@ -17,116 +23,295 @@ from .permissions import (
permission_tag_edit, permission_tag_remove, permission_tag_view permission_tag_edit, permission_tag_remove, permission_tag_view
) )
from .serializers import ( 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. get: Returns a list of all the tags.
post: Create a new tag. post: Create a new tag.
""" """
filter_backends = (MayanObjectPermissionsFilter,) object_permission = {'GET': permission_tag_view}
mayan_object_permissions = {'GET': (permission_tag_view,)}
mayan_view_permissions = {'POST': (permission_tag_create,)}
permission_classes = (MayanPermission,)
queryset = Tag.objects.all() queryset = Tag.objects.all()
serializer_class = TagSerializer
def get_serializer(self, *args, **kwargs): view_permission = {'POST': permission_tag_create}
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
class APITagView(generics.RetrieveUpdateDestroyAPIView): class APITagView(RetrieveUpdateDestroyAPIView):
""" """
delete: Delete the selected tag. delete: Delete the selected tag.
get: Return the details of the selected tag. get: Return the details of the selected tag.
patch: Edit the selected tag. patch: Edit the selected tag.
put: Edit the selected tag. put: Edit the selected tag.
""" """
filter_backends = (MayanObjectPermissionsFilter,)
lookup_url_kwarg = 'tag_pk' lookup_url_kwarg = 'tag_pk'
mayan_object_permissions = { object_permission = {
'DELETE': (permission_tag_delete,), 'DELETE': permission_tag_delete,
'GET': (permission_tag_view,), 'GET': permission_tag_view,
'PATCH': (permission_tag_edit,), 'PATCH': permission_tag_edit,
'PUT': (permission_tag_edit,) 'PUT': permission_tag_edit
} }
queryset = Tag.objects.all() 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) class APITagDocumentListView(ExternalObjectMixin, ListCreateAPIView):
def get_serializer_class(self):
if self.request.method == 'GET':
return TagSerializer
else:
return WritableTagSerializer
class APITagDocumentListView(ExternalObjectViewMixin, generics.ListAPIView):
""" """
get: Returns a list of all the documents tagged by a particular tag. get: Returns a list of all the documents tagged by a particular tag.
""" """
external_object_class = Tag external_object_class = Tag
external_object_pk_url_kwarg = 'tag_pk' external_object_pk_url_kwarg = 'tag_pk'
external_object_permission = permission_tag_view external_object_permission = permission_tag_view
filter_backends = (MayanObjectPermissionsFilter,) object_permission = {'GET': permission_document_view}
mayan_object_permissions = {'GET': (permission_document_view,)} serializer_class = TagDocumentSerializer
serializer_class = DocumentSerializer
def get_queryset(self): 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): def get_tag(self):
return self.get_external_object() 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. delete: Delete the selected tag document.
post: Attach a tag to a 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_class = Document
external_object_pk_url_kwarg = 'document_pk' external_object_pk_url_kwarg = 'document_pk'
filter_backends = (MayanObjectPermissionsFilter,) serializer_class = DocumentTagSerializer
mayan_object_permissions = {
'GET': (permission_tag_view,),
'POST': (permission_tag_attach,)
}
def get_document(self): def get_document(self):
return self.get_external_object() return self.get_external_object()
def get_external_object_permission(self): def get_external_object_permission(self):
if self.request.method == 'POST': permission_dictionary = {
return permission_tag_attach 'DELETE': permission_tag_remove,
else: 'GET': permission_tag_view,
return permission_tag_view 'POST': permission_tag_attach
}
return permission_dictionary.get(self.request.method)
def get_queryset(self): def get_queryset(self):
return self.get_document().get_tags().all() return self.get_document().tags.all()
def get_serializer(self, *args, **kwargs): def get_serializer_context(self):
if not self.request: context = super(DocumentSourceMixin, self).get_serializer_context()
return None 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): def get_serializer_context(self):
""" """
Extra context provided to the serializer class. Extra context provided to the serializer class.
@@ -140,41 +325,44 @@ class APIDocumentTagListView(ExternalObjectViewMixin, generics.ListCreateAPIView
) )
return context return context
"""
class APIDocumentTagView(DocumentSourceMixin, RetrieveDestroyAPIView):
class APIDocumentTagView(ExternalObjectViewMixin, generics.RetrieveDestroyAPIView):
""" """
delete: Remove a tag from the selected document. delete: Remove a tag from the selected document.
get: Returns the details of the selected document tag. get: Returns the details of the selected document tag.
""" """
external_object_class = Document #external_object_class = Document
external_object_pk_url_kwarg = 'document_pk' #external_object_pk_url_kwarg = 'document_pk'
filter_backends = (MayanObjectPermissionsFilter,)
lookup_url_kwarg = 'tag_pk' lookup_url_kwarg = 'tag_pk'
mayan_object_permissions = { mayan_object_permission = {
'GET': (permission_tag_view,), 'GET': permission_tag_view,
'DELETE': (permission_tag_remove,) 'DELETE': permission_tag_remove
} }
serializer_class = DocumentTagSerializer #serializer_class = DocumentTagSerializer
def get_document(self): #def get_document(self):
return self.get_external_object() # return self.get_external_object()
def get_external_object_permission(self): #def get_external_object_permission(self):
if self.request.method == 'DELETE': # if self.request.method == 'DELETE':
return permission_tag_remove # return permission_tag_remove
else: # else:
return permission_tag_view # return permission_tag_view
"""
def get_queryset(self): 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): return self.get_document().get_tags(
if not self.request: permission=permission, user=self.request.user
return None ).all()
"""
return super(APIDocumentTagView, self).get_serializer(*args, **kwargs)
"""
def get_serializer_context(self): def get_serializer_context(self):
""" """
Extra context provided to the serializer class. Extra context provided to the serializer class.
@@ -188,15 +376,33 @@ class APIDocumentTagView(ExternalObjectViewMixin, generics.RetrieveDestroyAPIVie
) )
return context return context
"""
def perform_destroy(self, instance): def perform_destroy(self, instance):
try: # try:
instance.documents.remove(self.get_document()) from mayan.apps.acls.models import AccessControlList
except Exception as exception: from rest_framework.generics import get_object_or_404
raise ValidationError(exception)
def retrieve(self, request, *args, **kwargs): queryset = AccessControlList.objects.restrict_queryset(
instance = self.get_object() 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) instance.remove_from(
return Response(serializer.data) 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)
'''

View File

@@ -24,12 +24,13 @@ from .events import (
event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove
) )
from .handlers import handler_index_document, handler_tag_pre_delete from .handlers import handler_index_document, handler_tag_pre_delete
from .html_widgets import DocumentTagsWidget, TagWidget
from .links import ( from .links import (
link_document_tag_list, link_multiple_documents_attach_tag, link_document_tag_list, link_document_multiple_tag_multiple_attach,
link_multiple_documents_tag_remove, link_document_multiple_tag_multiple_remove,
link_single_document_multiple_tag_remove, link_tag_attach, link_document_tag_multiple_remove, link_document_tag_multiple_attach,
link_tag_create, link_tag_delete, link_tag_edit, link_tag_list, link_tag_create, link_tag_delete, link_tag_document_list, link_tag_edit,
link_tag_multiple_delete, link_tag_tagged_item_list link_tag_list, link_tag_multiple_delete
) )
from .menus import menu_tags from .menus import menu_tags
from .methods import method_get_tags from .methods import method_get_tags
@@ -38,7 +39,6 @@ from .permissions import (
permission_tag_remove, permission_tag_view permission_tag_remove, permission_tag_view
) )
from .search import tag_search # NOQA from .search import tag_search # NOQA
from .widgets import widget_document_tags
class TagsApp(MayanAppConfig): class TagsApp(MayanAppConfig):
@@ -63,13 +63,11 @@ class TagsApp(MayanAppConfig):
app_label='documents', model_name='DocumentPageSearchResult' app_label='documents', model_name='DocumentPageSearchResult'
) )
DocumentTag = self.get_model('DocumentTag') DocumentTag = self.get_model(model_name='DocumentTag')
Tag = self.get_model('Tag') Tag = self.get_model(model_name='Tag')
Document.add_to_class(name='get_tags', value=method_get_tags) Document.add_to_class(name='get_tags', value=method_get_tags)
ModelAttribute(model=Document, name='get_tags')
ModelEventType.register( ModelEventType.register(
model=Tag, event_types=( model=Tag, event_types=(
event_tag_attach, event_tag_created, event_tag_edited, event_tag_attach, event_tag_created, event_tag_edited,
@@ -78,10 +76,10 @@ class TagsApp(MayanAppConfig):
) )
ModelField( ModelField(
Document, name='tags__label' model=Document, name='tags__label'
) )
ModelField( ModelField(
Document, name='tags__color' model=Document, name='tags__color'
) )
ModelPermission.register( ModelPermission.register(
@@ -96,29 +94,31 @@ class TagsApp(MayanAppConfig):
permission_acl_edit, permission_acl_view, permission_acl_edit, permission_acl_view,
permission_events_view, permission_tag_attach, permission_events_view, permission_tag_attach,
permission_tag_delete, permission_tag_edit, permission_tag_delete, permission_tag_edit,
permission_tag_remove, permission_tag_view, permission_tag_remove, permission_tag_view
) )
) )
SourceColumn( SourceColumn(
attribute='label', is_identifier=True, is_sortable=True, attribute='label', is_identifier=True, is_sortable=True,
source=DocumentTag, source=DocumentTag
) )
SourceColumn( SourceColumn(
attribute='get_preview_widget', source=DocumentTag label=_('Preview'), source=DocumentTag, widget=TagWidget
) )
SourceColumn( SourceColumn(
func=lambda context: widget_document_tags( func=lambda context: context['object'].get_tags(
document=context['object'], user=context['request'].user permission=permission_tag_view,
), label=_('Tags'), source=Document
)
SourceColumn(
func=lambda context: widget_document_tags(
document=context['object'].document,
user=context['request'].user 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( SourceColumn(
@@ -126,12 +126,12 @@ class TagsApp(MayanAppConfig):
source=Tag source=Tag
) )
SourceColumn( SourceColumn(
attribute='get_preview_widget', source=Tag label=_('Preview'), source=Tag, widget=TagWidget
) )
SourceColumn( SourceColumn(
func=lambda context: context['object'].get_document_count( func=lambda context: context['object'].get_document_count(
user=context['request'].user user=context['request'].user
), label=_('Documents'), source=Tag ), include_label=True, label=_('Documents'), source=Tag
) )
document_page_search.add_model_field( document_page_search.add_model_field(
@@ -147,19 +147,17 @@ class TagsApp(MayanAppConfig):
links=( links=(
link_acl_list, link_events_for_object, link_acl_list, link_events_for_object,
link_object_event_types_user_subcriptions_list, link_object_event_types_user_subcriptions_list,
link_tag_tagged_item_list, link_tag_document_list,
), ), sources=(Tag,)
sources=(Tag,)
) )
menu_main.bind_links(links=(menu_tags,), position=98) menu_main.bind_links(links=(menu_tags,), position=98)
menu_multi_item.bind_links( menu_multi_item.bind_links(
links=( links=(
link_multiple_documents_attach_tag, link_document_multiple_tag_multiple_attach,
link_multiple_documents_tag_remove link_document_multiple_tag_multiple_remove
), ), sources=(Document,)
sources=(Document,)
) )
menu_multi_item.bind_links( menu_multi_item.bind_links(
links=(link_tag_multiple_delete,), sources=(Tag,) links=(link_tag_multiple_delete,), sources=(Tag,)
@@ -167,14 +165,14 @@ class TagsApp(MayanAppConfig):
menu_object.bind_links( menu_object.bind_links(
links=( links=(
link_tag_edit, link_tag_delete link_tag_edit, link_tag_delete
), ), sources=(Tag,)
sources=(Tag,)
) )
menu_sidebar.bind_links( menu_sidebar.bind_links(
links=(link_tag_attach, link_single_document_multiple_tag_remove), links=(
sources=( link_document_tag_multiple_attach, link_document_tag_multiple_remove
'tags:tag_attach', 'tags:document_tags', ), sources=(
'tags:single_document_multiple_tag_remove' 'tags:document_tag_multiple_attach', 'tags:document_tag_list',
'tags:document_tag_multiple_remove'
) )
) )
menu_tags.bind_links( menu_tags.bind_links(
@@ -188,13 +186,11 @@ class TagsApp(MayanAppConfig):
# Index update # Index update
m2m_changed.connect( m2m_changed.connect(
handler_index_document,
dispatch_uid='tags_handler_index_document', dispatch_uid='tags_handler_index_document',
sender=Tag.documents.through receiver=handler_index_document, sender=Tag.documents.through
) )
pre_delete.connect( pre_delete.connect(
handler_tag_pre_delete,
dispatch_uid='tags_handler_tag_pre_delete', dispatch_uid='tags_handler_tag_pre_delete',
sender=Tag receiver=handler_tag_pre_delete, sender=Tag
) )

View File

@@ -4,7 +4,7 @@ from django.utils.translation import ugettext_lazy as _
from mayan.apps.events import EventTypeNamespace 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( event_tag_attach = namespace.add_event_type(
label=_('Tag attached to document'), name='attach' label=_('Tag attached to document'), name='attach'

View File

@@ -1,37 +1,22 @@
from __future__ import absolute_import, unicode_literals from __future__ import absolute_import, unicode_literals
import logging
from django import forms from django import forms
from django.utils.translation import ugettext_lazy as _ from django.utils.translation import ugettext_lazy as _
from mayan.apps.acls.models import AccessControlList from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.forms import FilteredSelectionForm
from .models import Tag from .models import Tag
from .permissions import permission_tag_view from .permissions import permission_tag_view
from .widgets import TagFormWidget from .widgets import TagFormWidget
logger = logging.getLogger(__name__)
class TagMultipleSelectionForm(FilteredSelectionForm):
class Media:
js = ('tags/js/tags_form.js',)
class TagMultipleSelectionForm(forms.Form): class Meta:
def __init__(self, *args, **kwargs): allow_multiple = True
help_text = kwargs.pop('help_text', None) field_name = 'tags'
permission = kwargs.pop('permission', permission_tag_view) label = _('Tags')
queryset = kwargs.pop('queryset', Tag.objects.all()) widget_attributes = {'class': 'select2-tags'}
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
)
)

View File

@@ -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,
}
)

View File

@@ -2,19 +2,22 @@ from __future__ import absolute_import, unicode_literals
from mayan.apps.appearance.classes import Icon from mayan.apps.appearance.classes import Icon
icon_menu_tags = Icon(driver_name='fontawesome', symbol='tags') icon_document_multiple_tag_multiple_remove = Icon(
icon_multiple_documents_tag_attach = Icon(
driver_name='fontawesome-dual', primary_symbol='tag',
secondary_symbol='arrow-right'
)
icon_multiple_documents_tag_remove = Icon(
driver_name='fontawesome-dual', primary_symbol='tag', driver_name='fontawesome-dual', primary_symbol='tag',
secondary_symbol='minus' secondary_symbol='minus'
) )
icon_tag_attach = Icon( icon_document_tag_multiple_attach = Icon(
driver_name='fontawesome-dual', primary_symbol='tag', driver_name='fontawesome-dual', primary_symbol='tag',
secondary_symbol='arrow-right' 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( icon_tag_create = Icon(
driver_name='fontawesome-dual', primary_symbol='tag', driver_name='fontawesome-dual', primary_symbol='tag',
secondary_symbol='plus' 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_document_list = Icon(driver_name='fontawesome', symbol='tags')
icon_tag_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_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')

View File

@@ -6,10 +6,10 @@ from mayan.apps.documents.icons import icon_document_list
from mayan.apps.navigation import Link, get_cascade_condition from mayan.apps.navigation import Link, get_cascade_condition
from .icons import ( from .icons import (
icon_document_multiple_tag_multiple_remove, 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_document_tag_multiple_attach, icon_tag_create, icon_tag_delete,
icon_tag_document_list, icon_tag_list, icon_tag_multiple_delete, icon_tag_edit, icon_tag_document_list, icon_tag_list,
icon_document_tag_multiple_remove icon_tag_multiple_delete, icon_document_tag_multiple_remove
) )
from .permissions import ( from .permissions import (
permission_tag_attach, permission_tag_create, permission_tag_delete, 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( link_document_multiple_tag_multiple_attach = Link(
icon_class=icon_document_multiple_tag_multiple_remove, text=_('Attach tags'), icon_class=icon_document_multiple_tag_multiple_remove, text=_('Attach tags'),
view='tags:document_multiple_tag_multiple_attach' 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'), icon_class=icon_document_multiple_tag_multiple_remove, text=_('Remove tag'),
view='tags:document_multiple_tag_multiple_remove' 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( link_document_tag_multiple_attach = Link(
args='object.pk', icon_class=icon_document_tag_multiple_attach, icon_class=icon_document_tag_multiple_attach,
permission=permission_tag_attach, text=_('Attach tags'), kwargs={'document_id': 'object.pk'}, permission=permission_tag_attach,
view='tags:document_tag_multiple_attach' text=_('Attach tags'), view='tags:document_tag_multiple_attach'
) )
link_document_tag_multiple_remove = Link( link_document_tag_multiple_remove = Link(
args='object.id', icon_class=icon_document_tag_multiple_remove, icon_class=icon_document_tag_multiple_remove,
permission=permission_tag_remove, text=_('Remove tags'), kwargs={'document_id': 'object.pk'}, permission=permission_tag_remove,
view='tags:document_tag_multiple_remove' text=_('Remove tags'), view='tags:document_tag_multiple_remove'
) )
link_tag_create = Link( link_tag_create = Link(
icon_class=icon_tag_create, permission=permission_tag_create, icon_class=icon_tag_create, permission=permission_tag_create,
text=_('Create new tag'), view='tags:tag_create' text=_('Create new tag'), view='tags:tag_create'
) )
link_tag_delete = Link( 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'), permission=permission_tag_delete, tags='dangerous', text=_('Delete'),
view='tags:tag_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( 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' permission=permission_tag_edit, text=_('Edit'), view='tags:tag_edit'
) )
link_tag_list = Link( link_tag_list = Link(
@@ -62,7 +68,3 @@ link_tag_multiple_delete = Link(
icon_class=icon_tag_multiple_delete, permission=permission_tag_delete, icon_class=icon_tag_multiple_delete, permission=permission_tag_delete,
text=_('Delete'), view='tags:tag_multiple_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'
)

View File

@@ -3,10 +3,18 @@ from __future__ import unicode_literals
from django.apps import apps from django.apps import apps
from django.utils.translation import ugettext_lazy as _ 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') 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.') method_get_tags.help_text = _('Return a the tags attached to the document.')

View File

@@ -15,7 +15,6 @@ from .events import (
event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove event_tag_attach, event_tag_created, event_tag_edited, event_tag_remove
) )
from .managers import DocumentTagManager from .managers import DocumentTagManager
from .widgets import widget_single_tag
@python_2_unicode_compatible @python_2_unicode_compatible
@@ -56,23 +55,22 @@ class Tag(models.Model):
def get_absolute_url(self): def get_absolute_url(self):
return reverse( 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): def get_document_count(self, user):
""" return self.get_documents(user=user).count()
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')
def remove_from(self, document, user=None): def remove_from(self, document, user=None):
""" """

View File

@@ -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')

View File

@@ -7,9 +7,8 @@ from mayan.apps.dynamic_search.classes import SearchModel
from .permissions import permission_tag_view from .permissions import permission_tag_view
tag_search = SearchModel( tag_search = SearchModel(
app_label='tags', model_name='Tag', app_label='tags', model_name='Tag', permission=permission_tag_view,
permission=permission_tag_view, serializer_path='mayan.apps.tags.serializers.TagSerializer'
serializer_string='mayan.apps.tags.serializers.TagSerializer'
) )
tag_search.add_model_field( tag_search.add_model_field(

View File

@@ -8,34 +8,42 @@ from rest_framework.reverse import reverse
from mayan.apps.acls.models import AccessControlList from mayan.apps.acls.models import AccessControlList
from mayan.apps.documents.models import Document 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 .models import Tag
from .permissions import permission_tag_attach from .permissions import permission_tag_attach
class TagSerializer(serializers.HyperlinkedModelSerializer): class TagSerializer(serializers.HyperlinkedModelSerializer):
documents_url = serializers.HyperlinkedIdentityField( attach_url = serializers.HyperlinkedIdentityField(
lookup_field='pk', lookup_url_kwarg='tag_pk', lookup_url_kwarg='tag_id', view_name='rest_api:tag-document-attach'
view_name='rest_api:tag-document-list' )
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: class Meta:
extra_kwargs = { extra_kwargs = {
'url': { 'url': {
'lookup_field': 'pk', 'lookup_url_kwarg': 'tag_pk', 'lookup_url_kwarg': 'tag_id',
'view_name': 'rest_api:tag-detail' 'view_name': 'rest_api:tag-detail'
} },
} }
fields = ( fields = (
'color', 'documents_count', 'documents_url', 'id', 'label', 'url' 'attach_url', 'color', 'documents_url', 'label', 'id',
'remove_url', 'url'
) )
model = Tag model = Tag
def get_documents_count(self, instance):
return instance.documents.count()
"""
class WritableTagSerializer(serializers.ModelSerializer): class WritableTagSerializer(serializers.ModelSerializer):
documents_pk_list = serializers.CharField( documents_pk_list = serializers.CharField(
help_text=_( help_text=_(
@@ -81,37 +89,303 @@ class WritableTagSerializer(serializers.ModelSerializer):
) )
return instance return instance
"""
class DocumentTagSerializer(TagSerializer): class DocumentTagSerializer(TagSerializer):
document_tag_url = serializers.SerializerMethodField( #document_attach_url = serializers.HyperlinkedIdentityField(
help_text=_( # lookup_url_kwarg='document_id', view_name='rest_api:document-tag-attach'
'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
)
class Meta(TagSerializer.Meta): class Meta(TagSerializer.Meta):
fields = TagSerializer.Meta.fields + ('document_tag_url', 'tag_pk') #fields = TagSerializer.Meta.fields + ('document_attach_url',)
read_only_fields = TagSerializer.Meta.fields + ('document_tag_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): #related_models = ('tags',)
return reverse( #related_models_kwargs = {
viewname='rest_api:document-tag-detail', kwargs={ # 'documents': {
'document_pk': self.context['document'].pk, ## #'pk_list': 'tags_pk_list', 'model': Tag,
'tag_pk': instance.pk # #'model': Tag,
}, request=self.context['request'], format=self.context['format'] # '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): for tag in queryset.all():
queryset = AccessControlList.objects.filter_by_access( 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(), permission=permission_tag_attach, queryset=Tag.objects.all(),
user=self.context['request'].user user=self.context['request'].user
) )
tag = get_object_or_404(queryset=queryset, pk=validated_data['tag_pk']) 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 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
'''

View File

@@ -0,0 +1,26 @@
'use strict';
var tagSelectionTemplate = function (tag, container) {
var $tag = $(
'<span class="label label-tag" style="background: ' + tag.element.dataset.color + ';"> ' + escape(tag.text) + '</span>'
);
container[0].style.background = tag.element.dataset.color;
return $tag;
}
var tagResultTemplate = function (tag) {
if (!tag.element) { return ''; }
var $tag = $(
'<span class="label label-tag" style="background: ' + tag.element.dataset.color + ';"> ' + escape(tag.text) + '</span>'
);
return $tag;
}
jQuery(document).ready(function() {
$('.select2-tags').select2({
templateSelection: tagSelectionTemplate,
templateResult: tagResultTemplate,
width: '100%'
});
});

View File

@@ -0,0 +1,5 @@
<div class="tag-container">
{% for tag in tags %}
{% include 'tags/tag_widget.html' with tag=tag %}
{% endfor %}
</div>

View File

@@ -1,2 +1 @@
{% include 'django/forms/widgets/select_option.html' %} {% include 'django/forms/widgets/select_option.html' %}

View File

@@ -9,15 +9,15 @@ TEST_TAG_INDEX_HAS_TAG = 'HAS_TAG'
TEST_TAG_INDEX_NO_TAG = 'NO_TAG' TEST_TAG_INDEX_NO_TAG = 'NO_TAG'
TEST_TAG_INDEX_NODE_TEMPLATE = ''' TEST_TAG_INDEX_NODE_TEMPLATE = '''
{{% for tag in document.get_tags().all() %}} {{% for tag in document.get_tags().all() %}}
{{% if tag.label == "{}" %}} {{% if tag.label == "{label}" %}}
{} {has_tag}
{{% else %}}
{not_tagged}
{{% endif %}}
{{% else %}} {{% else %}}
NO_TAG {not_tagged}
{{% endif %}}
{{% else %}}
NO_TAG
{{% endfor %}} {{% endfor %}}
'''.format( '''.format(
TEST_TAG_LABEL, TEST_TAG_INDEX_HAS_TAG, TEST_TAG_INDEX_NO_TAG, label=TEST_TAG_LABEL, has_tag=TEST_TAG_INDEX_HAS_TAG,
TEST_TAG_INDEX_NO_TAG not_tagged=TEST_TAG_INDEX_NO_TAG
).replace('\n', '') ).replace('\n', '')

View File

@@ -9,39 +9,100 @@ from .literals import (
class TagTestMixin(object): class TagTestMixin(object):
def _create_tag(self): def _create_test_tag(self):
self.tag = Tag.objects.create( self.test_tag = Tag.objects.create(
color=TEST_TAG_COLOR, label=TEST_TAG_LABEL color=TEST_TAG_COLOR, label=TEST_TAG_LABEL
) )
class TagAPITestMixin(object):
def _request_api_tag_create_view(self): def _request_api_tag_create_view(self):
return self.post( return self.post(
viewname='rest_api:tag-list', data={ 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): def _request_api_tag_delete_view(self):
return self.delete( 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( 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, 'label': TEST_TAG_LABEL_EDITED,
'color': TEST_TAG_COLOR_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( 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, 'label': TEST_TAG_LABEL_EDITED,
'color': TEST_TAG_COLOR_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): def _request_tag_create_view(self):
return self.post( return self.post(
viewname='tags:tag_create', data={ viewname='tags:tag_create', data={
@@ -52,68 +113,19 @@ class TagTestMixin(object):
def _request_tag_delete_view(self): def _request_tag_delete_view(self):
return self.post( 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): def _request_tag_edit_view(self):
return self.post( 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 '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( return self.post(
viewname='tags:tag_multiple_delete', viewname='tags:tag_multiple_delete',
data={'id_list': self.tag.pk}, data={'id_list': self.test_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,
}
) )

View File

@@ -21,11 +21,7 @@ from .literals import (
from .mixins import TagTestMixin from .mixins import TagTestMixin
class TagAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): class TagAPITestCase(TagTestMixin, BaseAPITestCase):
def setUp(self):
super(TagAPITestCase, self).setUp()
self.login_user()
def test_tag_create_view_no_permission(self): def test_tag_create_view_no_permission(self):
response = self._request_api_tag_create_view() response = self._request_api_tag_create_view()
self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN) 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) self.assertEqual(tag.color, TEST_TAG_COLOR)
def test_tag_delete_view_no_access(self): def test_tag_delete_view_no_access(self):
self._create_tag() self._create_test_tag()
response = self._request_api_tag_delete_view() response = self._request_api_tag_delete_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 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) self.assertEqual(Tag.objects.all().count(), 1)
def test_tag_delete_view_with_access(self): def test_tag_delete_view_with_access(self):
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_api_tag_delete_view() response = self._request_api_tag_delete_view()
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT)
self.assertEqual(Tag.objects.all().count(), 0) self.assertEqual(Tag.objects.all().count(), 0)
def test_tag_edit_via_patch_no_access(self): def test_tag_edit_patch_view_no_access(self):
self._create_tag() self._create_test_tag()
response = self._request_api_tag_edit_via_patch_view() response = self._request_api_tag_edit_patch_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.tag.refresh_from_db() self.test_tag.refresh_from_db()
self.assertEqual(self.tag.label, TEST_TAG_LABEL) self.assertEqual(self.test_tag.label, TEST_TAG_LABEL)
self.assertEqual(self.tag.color, TEST_TAG_COLOR) self.assertEqual(self.test_tag.color, TEST_TAG_COLOR)
self.assertEqual(Tag.objects.all().count(), 1) self.assertEqual(Tag.objects.all().count(), 1)
def test_tag_edit_via_patch_with_access(self): def test_tag_edit_patch_view_with_access(self):
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_api_tag_edit_via_patch_view() response = self._request_api_tag_edit_patch_view()
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.tag.refresh_from_db() self.test_tag.refresh_from_db()
self.assertEqual(self.tag.label, TEST_TAG_LABEL_EDITED) self.assertEqual(self.test_tag.label, TEST_TAG_LABEL_EDITED)
self.assertEqual(self.tag.color, TEST_TAG_COLOR_EDITED) self.assertEqual(self.test_tag.color, TEST_TAG_COLOR_EDITED)
self.assertEqual(Tag.objects.all().count(), 1) self.assertEqual(Tag.objects.all().count(), 1)
def test_tag_edit_via_put_no_access(self): def test_tag_edit_put_view_no_access(self):
self._create_tag() self._create_test_tag()
response = self._request_api_tag_edit_via_put_view() response = self._request_api_tag_edit_put_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
self.tag.refresh_from_db() self.test_tag.refresh_from_db()
self.assertEqual(self.tag.label, TEST_TAG_LABEL) self.assertEqual(self.test_tag.label, TEST_TAG_LABEL)
self.assertEqual(self.tag.color, TEST_TAG_COLOR) self.assertEqual(self.test_tag.color, TEST_TAG_COLOR)
self.assertEqual(Tag.objects.all().count(), 1) self.assertEqual(Tag.objects.all().count(), 1)
def test_tag_edit_via_put_with_access(self): def test_tag_edit_put_view_with_access(self):
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_api_tag_edit_via_put_view() response = self._request_api_tag_edit_put_view()
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.tag.refresh_from_db() self.test_tag.refresh_from_db()
self.assertEqual(self.tag.label, TEST_TAG_LABEL_EDITED) self.assertEqual(self.test_tag.label, TEST_TAG_LABEL_EDITED)
self.assertEqual(self.tag.color, TEST_TAG_COLOR_EDITED) self.assertEqual(self.test_tag.color, TEST_TAG_COLOR_EDITED)
self.assertEqual(Tag.objects.all().count(), 1) self.assertEqual(Tag.objects.all().count(), 1)
def test_tag_list_view_no_access(self):
class DocumentAPITestCase(TagTestMixin, DocumentTestMixin, BaseAPITestCase): self._create_test_tag()
auto_upload_document = False response = self._request_api_tag_list_view()
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()
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 0) self.assertEqual(response.data['count'], 0)
def test_tag_document_list_view_with_document_access(self): def test_tag_list_view_with_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.grant_access(obj=self.test_tag, permission=permission_tag_view)
self.tag.documents.add(self.document) response = self._request_api_tag_list_view()
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()
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual( self.assertEqual(response.data['count'], 1)
response.data['results'][0]['uuid'],
force_text(self.document.uuid)
)
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( return self.post(
viewname='rest_api:document-tag-list', viewname='rest_api:document-tag-list',
kwargs={'document_pk': self.document.pk}, kwargs={'document_id': self.test_document.pk},
data={'tag_pk': self.tag.pk} data={'tag_id': self.test_tag.pk}
) )
def test_document_attach_tag_view_no_access(self): def test_document_tag_attach_view_no_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
response = self._request_api_document_attach_tag_view() response = self._request_api_document_tag_attach_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 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) self.assertEqual(Tag.objects.all().count(), 1)
def test_document_attach_tag_view_with_document_access(self): def test_document_tag_attach_view_with_document_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.grant_access(obj=self.document, permission=permission_tag_attach) self.grant_access(obj=self.test_document, permission=permission_tag_attach)
response = self._request_api_document_attach_tag_view() response = self._request_api_document_tag_attach_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 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) self.assertEqual(Tag.objects.all().count(), 1)
def test_document_attach_tag_view_with_tag_access(self): def test_document_tag_attach_view_with_tag_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.grant_access(obj=self.tag, permission=permission_tag_attach) self.grant_access(obj=self.test_tag, permission=permission_tag_attach)
response = self._request_api_document_attach_tag_view() response = self._request_api_document_tag_attach_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 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) self.assertEqual(Tag.objects.all().count(), 1)
def test_document_attach_tag_view_with_full_access(self): def test_document_tag_attach_view_with_full_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.grant_access(obj=self.document, permission=permission_tag_attach) self.grant_access(
self.grant_access(obj=self.tag, permission=permission_tag_attach) obj=self.test_document, permission=permission_tag_attach
response = self._request_api_document_attach_tag_view() )
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.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) self.assertEqual(Tag.objects.all().count(), 1)
def _request_api_document_tag_detail_view(self): def _request_api_document_tag_detail_view(self):
return self.get( return self.get(
viewname='rest_api:document-tag-detail', kwargs={ 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): def test_document_tag_detail_view_no_permission(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
response = self._request_api_document_tag_detail_view() response = self._request_api_document_tag_detail_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_document_tag_detail_view_with_document_access(self): def test_document_tag_detail_view_with_document_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
self.grant_access( 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() response = self._request_api_document_tag_detail_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_document_tag_detail_view_with_tag_access(self): def test_document_tag_detail_view_with_tag_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
self.grant_access(obj=self.tag, permission=permission_tag_view) self.grant_access(obj=self.test_tag, permission=permission_tag_view)
response = self._request_api_document_tag_detail_view() response = self._request_api_document_tag_detail_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_document_tag_detail_view_with_full_access(self): def test_document_tag_detail_view_with_full_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
self.grant_access(obj=self.tag, permission=permission_tag_view) self.grant_access(obj=self.test_tag, permission=permission_tag_view)
self.grant_access( 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() response = self._request_api_document_tag_detail_view()
self.assertEqual(response.status_code, status.HTTP_200_OK) 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): def _request_api_document_tag_list_view(self):
return self.get( return self.get(
viewname='rest_api:document-tag-list', 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): def test_document_tag_list_view_no_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
response = self._request_api_document_tag_list_view() response = self._request_api_document_tag_list_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_document_tag_list_view_with_document_access(self): def test_document_tag_list_view_with_document_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
self.grant_access(obj=self.document, permission=permission_tag_view) self.grant_access(obj=self.test_document, permission=permission_tag_view)
response = self._request_api_document_tag_list_view() response = self._request_api_document_tag_list_view()
self.assertEqual(response.status_code, status.HTTP_200_OK) self.assertEqual(response.status_code, status.HTTP_200_OK)
self.assertEqual(response.data['count'], 0) self.assertEqual(response.data['count'], 0)
def test_document_tag_list_view_with_tag_access(self): def test_document_tag_list_view_with_tag_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
self.grant_access(obj=self.tag, permission=permission_tag_view) self.grant_access(obj=self.test_tag, permission=permission_tag_view)
response = self._request_api_document_tag_list_view() response = self._request_api_document_tag_list_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND)
def test_document_tag_list_view_with_full_access(self): def test_document_tag_list_view_with_full_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
self.grant_access(obj=self.document, permission=permission_tag_view) self.grant_access(obj=self.test_document, permission=permission_tag_view)
self.grant_access(obj=self.tag, permission=permission_tag_view) self.grant_access(obj=self.test_tag, permission=permission_tag_view)
response = self._request_api_document_tag_list_view() response = self._request_api_document_tag_list_view()
self.assertEqual(response.status_code, status.HTTP_200_OK) 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): def _request_api_document_tag_remove_view(self):
return self.delete( return self.delete(
viewname='rest_api:document-tag-detail', kwargs={ 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): def test_document_tag_remove_view_no_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
response = self._request_api_document_tag_remove_view() response = self._request_api_document_tag_remove_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 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) self.assertEqual(Tag.objects.all().count(), 1)
def test_document_tag_remove_view_with_document_access(self): def test_document_tag_remove_view_with_document_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
self.grant_access(obj=self.document, permission=permission_tag_remove) self.grant_access(obj=self.test_document, permission=permission_tag_remove)
response = self._request_api_document_tag_remove_view() response = self._request_api_document_tag_remove_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 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) self.assertEqual(Tag.objects.all().count(), 1)
def test_document_tag_remove_view_with_tag_access(self): def test_document_tag_remove_view_with_tag_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
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() response = self._request_api_document_tag_remove_view()
self.assertEqual(response.status_code, status.HTTP_404_NOT_FOUND) 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) self.assertEqual(Tag.objects.all().count(), 1)
def test_document_tag_remove_view_with_full_access(self): def test_document_tag_remove_view_with_full_access(self):
self._create_tag() self._create_test_tag()
self.document = self.upload_document() self.test_document = self.upload_document()
self.tag.documents.add(self.document) self.test_tag.documents.add(self.test_document)
self.grant_access( 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() response = self._request_api_document_tag_remove_view()
self.assertEqual(response.status_code, status.HTTP_204_NO_CONTENT) 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) 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)
)

View File

@@ -10,7 +10,7 @@ from ..permissions import permission_tag_create, permission_tag_edit
from .mixins import TagTestMixin from .mixins import TagTestMixin
#TODO: Add tests for event_tag_remove and event_tag_attach
class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase): class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase):
def setUp(self): def setUp(self):
super(TagEventsTestCase, self).setUp() super(TagEventsTestCase, self).setUp()
@@ -38,10 +38,10 @@ class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase):
self.assertEqual(event.verb, event_tag_created.id) self.assertEqual(event.verb, event_tag_created.id)
self.assertEqual(event.target, tag) 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): def test_tag_edit_event_no_permissions(self):
self._create_tag() self._create_test_tag()
Action.objects.all().delete() Action.objects.all().delete()
response = self._request_tag_edit_view() response = self._request_tag_edit_view()
@@ -49,11 +49,11 @@ class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase):
self.assertEqual(Action.objects.count(), 0) self.assertEqual(Action.objects.count(), 0)
def test_tag_edit_event_with_access(self): def test_tag_edit_event_with_access(self):
self._create_tag() self._create_test_tag()
Action.objects.all().delete() Action.objects.all().delete()
self.grant_access( self.grant_access(
permission=permission_tag_edit, obj=self.tag permission=permission_tag_edit, obj=self.test_tag
) )
response = self._request_tag_edit_view() response = self._request_tag_edit_view()
@@ -63,5 +63,5 @@ class TagEventsTestCase(TagTestMixin, GenericDocumentViewTestCase):
event = Action.objects.first() event = Action.objects.first()
self.assertEqual(event.verb, event_tag_edited.id) self.assertEqual(event.verb, event_tag_edited.id)
self.assertEqual(event.target, self.tag) self.assertEqual(event.target, self.test_tag)
self.assertEqual(event.actor, self.user) self.assertEqual(event.actor, self._test_case_user)

View File

@@ -11,9 +11,10 @@ from .literals import (
TEST_TAG_COLOR, TEST_TAG_LABEL, TEST_TAG_INDEX_HAS_TAG, TEST_TAG_COLOR, TEST_TAG_LABEL, TEST_TAG_INDEX_HAS_TAG,
TEST_TAG_INDEX_NO_TAG, TEST_TAG_INDEX_NODE_TEMPLATE 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 auto_upload_document = False
def test_tag_indexing(self): def test_tag_indexing(self):
@@ -27,7 +28,8 @@ class TagSignalIndexingTestCase(DocumentTestMixin, BaseTestCase):
link_documents=True 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.document = self.upload_document()
self.assertTrue( self.assertTrue(
@@ -36,7 +38,7 @@ class TagSignalIndexingTestCase(DocumentTestMixin, BaseTestCase):
).documents.all() ).documents.all()
) )
tag.documents.add(self.document) self.test_tag.documents.add(self.document)
self.assertTrue( self.assertTrue(
self.document in IndexInstanceNode.objects.get( self.document in IndexInstanceNode.objects.get(
@@ -44,7 +46,7 @@ class TagSignalIndexingTestCase(DocumentTestMixin, BaseTestCase):
).documents.all() ).documents.all()
) )
tag.delete() self.test_tag.delete()
self.assertTrue( self.assertTrue(
self.document in IndexInstanceNode.objects.get( self.document in IndexInstanceNode.objects.get(

View File

@@ -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}
)
)

View File

@@ -1,5 +1,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
from django.utils.encoding import force_text
from mayan.apps.common.tests import GenericViewTestCase from mayan.apps.common.tests import GenericViewTestCase
from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.permissions import permission_document_view
from mayan.apps.documents.tests import GenericDocumentViewTestCase 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_COLOR, TEST_TAG_COLOR_EDITED, TEST_TAG_LABEL,
TEST_TAG_LABEL_EDITED 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): def test_tag_create_view_no_permissions(self):
self.login_user()
response = self._request_tag_create_view() response = self._request_tag_create_view()
self.assertEqual(response.status_code, 403) self.assertEqual(response.status_code, 403)
self.assertEqual(Tag.objects.count(), 0) self.assertEqual(Tag.objects.count(), 0)
def test_tag_create_view_with_permissions(self): def test_tag_create_view_with_permissions(self):
self.login_user()
self.grant_permission(permission=permission_tag_create) self.grant_permission(permission=permission_tag_create)
response = self._request_tag_create_view() response = self._request_tag_create_view()
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
@@ -39,18 +37,16 @@ class TagViewTestCase(TagTestMixin, GenericViewTestCase):
self.assertEqual(tag.color, TEST_TAG_COLOR) self.assertEqual(tag.color, TEST_TAG_COLOR)
def test_tag_delete_view_no_permissions(self): def test_tag_delete_view_no_permissions(self):
self.login_user() self._create_test_tag()
self._create_tag()
response = self._request_tag_delete_view() response = self._request_tag_delete_view()
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 404)
self.assertEqual(Tag.objects.count(), 1) self.assertEqual(Tag.objects.count(), 1)
def test_tag_delete_view_with_access(self): def test_tag_delete_view_with_access(self):
self.login_user() self._create_test_tag()
self._create_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() response = self._request_tag_delete_view()
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
@@ -58,190 +54,295 @@ class TagViewTestCase(TagTestMixin, GenericViewTestCase):
self.assertEqual(Tag.objects.count(), 0) self.assertEqual(Tag.objects.count(), 0)
def test_tag_multiple_delete_view_no_permissions(self): def test_tag_multiple_delete_view_no_permissions(self):
self.login_user() self._create_test_tag()
self._create_tag()
response = self._request_multiple_delete_view() response = self._request_tag_multiple_delete_view()
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 404)
self.assertEqual(Tag.objects.count(), 1) self.assertEqual(Tag.objects.count(), 1)
def test_tag_multiple_delete_view_with_access(self): def test_tag_multiple_delete_view_with_access(self):
self.login_user() self._create_test_tag()
self._create_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(response.status_code, 302)
self.assertEqual(Tag.objects.count(), 0) self.assertEqual(Tag.objects.count(), 0)
def test_tag_edit_view_no_permissions(self): def test_tag_edit_view_no_permissions(self):
self.login_user() self._create_test_tag()
self._create_tag()
response = self._request_edit_tag_view() response = self._request_tag_edit_view()
self.assertEqual(response.status_code, 404) 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.label, TEST_TAG_LABEL)
self.assertEqual(tag.color, TEST_TAG_COLOR) self.assertEqual(tag.color, TEST_TAG_COLOR)
def test_tag_edit_view_with_access(self): def test_tag_edit_view_with_access(self):
self.login_user() self._create_test_tag()
self._create_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) 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.label, TEST_TAG_LABEL_EDITED)
self.assertEqual(tag.color, TEST_TAG_COLOR_EDITED) self.assertEqual(tag.color, TEST_TAG_COLOR_EDITED)
class TagDocumentsViewTestCase(TagTestMixin, GenericDocumentViewTestCase): class TagDocumentsViewTestCase(TagViewTestMixin, TagTestMixin, GenericDocumentViewTestCase):
def _request_document_list_view(self): def test_document_tag_attach_view_no_permission(self):
return self.get(viewname='documents:document_list') self._create_test_tag()
def test_document_tags_widget_no_permissions(self): response = self._request_document_tag_multiple_attach_view()
self.login_user() self.assertEqual(response.status_code, 404)
self._create_tag() self.assertEqual(self.test_document.tags.count(), 0)
self.tag.documents.add(self.document) def test_document_tag_attach_view_with_document_access(self):
response = self._request_document_list_view() self._create_test_tag()
self.assertNotContains(
response=response, text=TEST_TAG_LABEL, status_code=200
)
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( self.grant_access(
obj=self.document, permission=permission_document_view obj=self.test_document, permission=permission_tag_attach
) )
response = self._request_document_tag_multiple_attach_view()
response = self._request_document_list_view()
self.assertContains( 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.assertEqual(self.test_document.tags.count(), 0)
self.login_user()
self._create_tag()
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() self.assertEqual(self.test_document.tags.count(), 0)
# 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)
def test_document_attach_tag_view_with_access(self): def test_document_tag_attach_view_with_full_access(self):
self.login_user() self._create_test_tag()
self._create_tag()
self.assertEqual(self.document.tags.count(), 0) self.grant_access(
obj=self.test_document, permission=permission_tag_attach
self.grant_access(obj=self.document, permission=permission_tag_attach) )
self.grant_access(obj=self.tag, permission=permission_tag_attach) self.grant_access(obj=self.test_tag, permission=permission_tag_attach)
# permission_tag_view is needed because the form filters the response = self._request_document_tag_multiple_attach_view()
# choices
self.grant_access(obj=self.tag, permission=permission_tag_view)
response = self._request_attach_tag_view()
self.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertQuerysetEqual( 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.grant_access(
self.assertEqual(response.status_code, 200) obj=self.test_document, permission=permission_tag_attach
self.assertEqual(self.document.tags.count(), 0) )
self.grant_access(
def test_document_multiple_attach_tag_view_with_access(self): obj=self.test_document_2, permission=permission_tag_attach
self.login_user() )
self._create_tag() self.grant_access(obj=self.test_tag, permission=permission_tag_attach)
response = self._request_document_tag_multiple_attach_view()
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.assertEqual(response.status_code, 302) self.assertEqual(response.status_code, 302)
self.assertQuerysetEqual( 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.assertEqual(self.test_document_2.tags.count(), 0)
self.login_user()
self._create_tag()
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() def test_document_multiple_tag_attach_view_with_document_access(self):
self.assertEqual(response.status_code, 200) 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): response = self._request_document_multiple_tag_multiple_attach_view()
self.login_user()
self._create_tag()
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.assertEqual(self.test_document.tags.count(), 0)
self.grant_access(obj=self.tag, permission=permission_tag_remove)
self.grant_access(obj=self.tag, permission=permission_tag_view)
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(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): def test_document_tag_multiple_remove_view_no_permissions(self):
self.login_user() self._create_test_tag()
self._create_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.assertQuerysetEqual(
self.assertEqual(response.status_code, 200) 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.test_document.tags.add(self.test_tag)
self.login_user()
self._create_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.assertEqual(self.test_document.tags.count(), 1)
self.grant_access(obj=self.tag, permission=permission_tag_remove)
self.grant_access(obj=self.tag, permission=permission_tag_view)
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(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)

View File

@@ -13,12 +13,12 @@ from mayan.apps.sources.tests.literals import (
from ..models import Tag from ..models import Tag
from .literals import TEST_TAG_COLOR, TEST_TAG_LABEL from .literals import TEST_TAG_COLOR, TEST_TAG_LABEL
from .mixins import TagTestMixin
class TaggedDocumentUploadTestCase(GenericDocumentViewTestCase): class TaggedDocumentUploadTestCase(TagTestMixin, GenericDocumentViewTestCase):
def setUp(self): def setUp(self):
super(TaggedDocumentUploadTestCase, self).setUp() super(TaggedDocumentUploadTestCase, self).setUp()
self.login_user()
self.source = WebFormSource.objects.create( self.source = WebFormSource.objects.create(
enabled=True, label=TEST_SOURCE_LABEL, enabled=True, label=TEST_SOURCE_LABEL,
uncompress=TEST_SOURCE_UNCOMPRESS_N uncompress=TEST_SOURCE_UNCOMPRESS_N
@@ -34,20 +34,15 @@ class TaggedDocumentUploadTestCase(GenericDocumentViewTestCase):
data={ data={
'document_type_id': self.document_type.pk, 'document_type_id': self.document_type.pk,
'source-file': file_object, '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): def test_upload_interactive_view_with_access(self):
self._create_tag() self._create_test_tag()
self.grant_access( self.grant_access(
permission=permission_document_create, obj=self.document_type permission=permission_document_create, obj=self.document_type
) )
response = self._request_upload_interactive_document_create_view() response = self._request_upload_interactive_document_create_view()
self.assertEqual(response.status_code, 302) 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())

View File

@@ -2,66 +2,75 @@ from __future__ import unicode_literals
from django.conf.urls import url from django.conf.urls import url
from .api_views import ( #from .api_views import (
APIDocumentTagView, APIDocumentTagListView, APITagDocumentListView, # APIDocumentTagView, APIDocumentTagListView, APITagDocumentListView,
APITagListView, APITagView # APITagListView, APITagView
) #)
from .api_views import DocumentTagViewSet, TagViewSet
from .views import ( from .views import (
DocumentTagListView, TagAttachActionView, TagCreateView, DocumentTagListView, TagAttachActionView, TagCreateView,
TagDeleteActionView, TagEditView, TagListView, TagRemoveActionView, TagDeleteActionView, TagDocumentListView, TagEditView, TagListView,
TagTaggedItemListView TagRemoveActionView
) )
urlpatterns = [ urlpatterns = [
url(regex=r'^tags/list/$', name='tag_list', view=TagListView.as_view()), url(
regex=r'^documents/(?P<document_id>\d+)/tags/$',
name='document_tag_list', view=DocumentTagListView.as_view()
),
url(
regex=r'^documents/(?P<document_id>\d+)/tags/multiple/attach/$',
name='document_tag_multiple_attach', view=TagAttachActionView.as_view()
),
url(
regex=r'^documents/(?P<document_id>\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( url(
regex=r'^tags/create/$', name='tag_create', regex=r'^tags/create/$', name='tag_create',
view=TagCreateView.as_view() view=TagCreateView.as_view()
), ),
url( url(
regex=r'^tags/(?P<tag_pk>\d+)/delete/$', name='tag_delete', regex=r'^tags/(?P<tag_id>\d+)/delete/$', name='tag_delete',
view=TagDeleteActionView.as_view() view=TagDeleteActionView.as_view()
), ),
url( url(
regex=r'^tags/(?P<tag_pk>\d+)/edit/$', name='tag_edit', regex=r'^tags/(?P<tag_id>\d+)/edit/$', name='tag_edit',
view=TagEditView.as_view() view=TagEditView.as_view()
), ),
url( url(
regex=r'^tags/(?P<tag_pk>\d+)/documents/$', regex=r'^tags/(?P<tag_id>\d+)/documents/$',
name='tag_tagged_item_list', view=TagTaggedItemListView.as_view() name='tag_document_list', view=TagDocumentListView.as_view()
), ),
url( url(
regex=r'^tags/multiple/delete/$', name='tag_multiple_delete', regex=r'^tags/multiple/delete/$', name='tag_multiple_delete',
view=TagDeleteActionView.as_view() view=TagDeleteActionView.as_view()
), )
url(
regex=r'^tags/multiple/remove/document/(?P<document_pk>\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<document_pk>\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<document_pk>\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<document_id>\d+)/tags',
'viewset': DocumentTagViewSet, 'basename': 'document_tag'
},
)
"""
url( url(
regex=r'^tags/(?P<tag_pk>\d+)/documents/$', regex=r'^tags/(?P<tag_pk>\d+)/documents/$',
name='tag-document-list', view=APITagDocumentListView.as_view(), name='tag-document-list', view=APITagDocumentListView.as_view(),
@@ -79,4 +88,4 @@ api_urls = [
regex=r'^documents/(?P<document_pk>\d+)/tags/(?P<tag_pk>[0-9]+)/$', regex=r'^documents/(?P<document_pk>\d+)/tags/(?P<tag_pk>[0-9]+)/$',
name='document-tag-detail', view=APIDocumentTagView.as_view() name='document-tag-detail', view=APIDocumentTagView.as_view()
), ),
] """

View File

@@ -9,19 +9,20 @@ from django.urls import reverse_lazy
from django.utils.translation import ugettext_lazy as _, ungettext from django.utils.translation import ugettext_lazy as _, ungettext
from mayan.apps.acls.models import AccessControlList from mayan.apps.acls.models import AccessControlList
from mayan.apps.common.views import ( from mayan.apps.common.generics import (
MultipleObjectFormActionView, MultipleObjectConfirmActionView, MultipleObjectFormActionView, MultipleObjectConfirmActionView,
SingleObjectCreateView, SingleObjectEditView, SingleObjectListView SingleObjectCreateView, SingleObjectEditView, SingleObjectListView
) )
from mayan.apps.common.mixins import ExternalObjectMixin
from mayan.apps.documents.models import Document from mayan.apps.documents.models import Document
from mayan.apps.documents.views import DocumentListView from mayan.apps.documents.views import DocumentListView
from mayan.apps.documents.permissions import permission_document_view from mayan.apps.documents.permissions import permission_document_view
from .forms import TagMultipleSelectionForm from .forms import TagMultipleSelectionForm
from .icons import ( 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 .models import Tag
from .permissions import ( from .permissions import (
permission_tag_attach, permission_tag_create, permission_tag_delete, permission_tag_attach, permission_tag_create, permission_tag_delete,
@@ -34,7 +35,7 @@ logger = logging.getLogger(__name__)
class TagAttachActionView(MultipleObjectFormActionView): class TagAttachActionView(MultipleObjectFormActionView):
form_class = TagMultipleSelectionForm form_class = TagMultipleSelectionForm
model = Document model = Document
pk_url_kwarg = 'document_pk' pk_url_kwarg = 'document_id'
object_permission = permission_tag_attach object_permission = permission_tag_attach
success_message = _('Tag attach request performed on %(count)d document') success_message = _('Tag attach request performed on %(count)d document')
success_message_plural = _( success_message_plural = _(
@@ -42,7 +43,7 @@ class TagAttachActionView(MultipleObjectFormActionView):
) )
def get_extra_context(self): def get_extra_context(self):
queryset = self.get_queryset() queryset = self.get_object_list()
result = { result = {
'submit_label': _('Attach'), 'submit_label': _('Attach'),
@@ -66,7 +67,7 @@ class TagAttachActionView(MultipleObjectFormActionView):
return result return result
def get_form_extra_kwargs(self): def get_form_extra_kwargs(self):
queryset = self.get_queryset() queryset = self.get_object_list()
result = { result = {
'help_text': _('Tags to be attached.'), 'help_text': _('Tags to be attached.'),
'permission': permission_tag_attach, 'permission': permission_tag_attach,
@@ -85,44 +86,45 @@ class TagAttachActionView(MultipleObjectFormActionView):
return result return result
def get_post_action_redirect(self): def get_post_action_redirect(self):
queryset = self.get_queryset() queryset = self.get_object_list()
if queryset.count() == 1: if queryset.count() == 1:
return reverse( return reverse(
viewname='tags:document_tags', kwargs={ viewname='tags:document_tag_list', kwargs={
'document_pk': queryset.first().pk 'document_id': queryset.first().pk
} }
) )
else: else:
return super(TagAttachActionView, self).get_post_action_redirect() return super(TagAttachActionView, self).get_post_action_redirect()
def object_action(self, form, instance): 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']: tag_list = AccessControlList.objects.restrict_queryset(
AccessControlList.objects.check_access( permission=permission_tag_attach, queryset=form.cleaned_data['tags'],
obj=tag, permissions=permission_tag_attach, user=self.request.user
user=self.request.user )
)
for tag in tag_list:
if tag in attached_tags: if tag in attached_tags:
messages.warning( messages.warning(
self.request, _( message=_(
'Document "%(document)s" is already tagged as ' 'Document "%(document)s" is already tagged as '
'"%(tag)s"' '"%(tag)s"'
) % { ) % {
'document': instance, 'tag': tag 'document': instance, 'tag': tag
} }, request=self.request
) )
else: else:
tag.attach_to(document=instance, user=self.request.user) tag.attach_to(document=instance, user=self.request.user)
messages.success( messages.success(
self.request, message=_(
_(
'Tag "%(tag)s" attached successfully to document ' 'Tag "%(tag)s" attached successfully to document '
'"%(document)s".' '"%(document)s".'
) % { ) % {
'document': instance, 'tag': tag 'document': instance, 'tag': tag
} }, request=self.request
) )
@@ -130,7 +132,7 @@ class TagCreateView(SingleObjectCreateView):
extra_context = {'title': _('Create tag')} extra_context = {'title': _('Create tag')}
fields = ('label', 'color') fields = ('label', 'color')
model = Tag model = Tag
post_action_redirect = reverse_lazy('tags:tag_list') post_action_redirect = reverse_lazy(viewname='tags:tag_list')
view_permission = permission_tag_create view_permission = permission_tag_create
def get_save_extra_data(self): def get_save_extra_data(self):
@@ -139,8 +141,8 @@ class TagCreateView(SingleObjectCreateView):
class TagDeleteActionView(MultipleObjectConfirmActionView): class TagDeleteActionView(MultipleObjectConfirmActionView):
model = Tag model = Tag
pk_url_kwarg = 'tag_pk' pk_url_kwarg = 'tag_id'
post_action_redirect = reverse_lazy('tags:tag_list') post_action_redirect = reverse_lazy(viewname='tags:tag_list')
object_permission = permission_tag_delete object_permission = permission_tag_delete
success_message = _('Tag delete request performed on %(count)d tag') success_message = _('Tag delete request performed on %(count)d tag')
success_message_plural = _( success_message_plural = _(
@@ -155,9 +157,9 @@ class TagDeleteActionView(MultipleObjectConfirmActionView):
'submit_icon_class': icon_tag_delete_submit, 'submit_icon_class': icon_tag_delete_submit,
'submit_label': _('Delete'), 'submit_label': _('Delete'),
'title': ungettext( 'title': ungettext(
'Delete the selected tag?', singular='Delete the selected tag?',
'Delete the selected tags?', plural='Delete the selected tags?',
queryset.count() number=queryset.count()
) )
} }
@@ -175,13 +177,14 @@ class TagDeleteActionView(MultipleObjectConfirmActionView):
try: try:
instance.delete() instance.delete()
messages.success( messages.success(
self.request, _('Tag "%s" deleted successfully.') % instance message=_('Tag "%s" deleted successfully.') % instance,
request=self.request
) )
except Exception as exception: except Exception as exception:
messages.error( messages.error(
self.request, _('Error deleting tag "%(tag)s": %(error)s') % { message=_('Error deleting tag "%(tag)s": %(error)s') % {
'tag': instance, 'error': exception 'tag': instance, 'error': exception
} }, request=self.request
) )
@@ -189,9 +192,8 @@ class TagEditView(SingleObjectEditView):
fields = ('label', 'color') fields = ('label', 'color')
model = Tag model = Tag
object_permission = permission_tag_edit object_permission = permission_tag_edit
object_permission_raise_404 = True pk_url_kwarg = 'tag_id'
pk_url_kwarg = 'tag_pk' post_action_redirect = reverse_lazy(viewname='tags:tag_list')
post_action_redirect = reverse_lazy('tags:tag_list')
def get_extra_context(self): def get_extra_context(self):
return { return {
@@ -222,22 +224,24 @@ class TagListView(SingleObjectListView):
'title': _('Tags'), 'title': _('Tags'),
} }
def get_object_list(self): def get_source_queryset(self):
return self.get_tag_queryset() #return self.get_tag_queryset()
def get_tag_queryset(self):
return Tag.objects.all() return Tag.objects.all()
#def get_tag_queryset(self):
# return Tag.objects.all()
class TagTaggedItemListView(DocumentListView):
def get_tag(self): class TagDocumentListView(ExternalObjectMixin, DocumentListView):
return get_object_or_404(klass=Tag, pk=self.kwargs['tag_pk']) external_object_class = Tag
external_object_permission = permission_tag_view
external_object_pk_url_kwarg = 'tag_id'
def get_document_queryset(self): 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): def get_extra_context(self):
context = super(TagTaggedItemListView, self).get_extra_context() context = super(TagDocumentListView, self).get_extra_context()
context.update( context.update(
{ {
'column_class': 'col-xs-12 col-sm-6 col-md-4 col-lg-3', 'column_class': 'col-xs-12 col-sm-6 col-md-4 col-lg-3',
@@ -247,57 +251,56 @@ class TagTaggedItemListView(DocumentListView):
) )
return context 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( class DocumentTagListView(ExternalObjectMixin, TagListView):
permissions=permission_document_view, user=request.user, external_object_class = Document
obj=self.document external_object_permission = permission_tag_view
) external_object_pk_url_kwarg = 'document_id'
return super(DocumentTagListView, self).dispatch(
request, *args, **kwargs
)
def get_extra_context(self): def get_extra_context(self):
context = super(DocumentTagListView, self).get_extra_context() context = super(DocumentTagListView, self).get_extra_context()
context.update( context.update(
{ {
'hide_link': True, 'hide_link': True,
'no_results_main_link': link_tag_attach.resolve( 'no_results_main_link': link_document_tag_multiple_attach.resolve(
context=RequestContext( context=RequestContext(
self.request, {'object': self.document} self.request, {'object': self.get_external_object()}
) )
), ),
'no_results_title': _('Document has no tags attached'), 'no_results_title': _('Document has no tags attached'),
'object': self.document, 'object': self.get_external_object(),
'title': _('Tags for document: %s') % self.document, 'title': _(
'Tags for document: %s'
) % self.get_external_object(),
} }
) )
return context return context
def get_tag_queryset(self): #def get_tag_queryset(self):
return self.document.get_tags().all() def get_source_queryset(self):
return self.get_external_object().get_tags(
permission=permission_tag_view, user=self.request.user
).all()
class TagRemoveActionView(MultipleObjectFormActionView): class TagRemoveActionView(MultipleObjectFormActionView):
form_class = TagMultipleSelectionForm form_class = TagMultipleSelectionForm
model = Document model = Document
object_permission = permission_tag_remove object_permission = permission_tag_remove
pk_url_kwarg = 'document_id'
success_message = _('Tag remove request performed on %(count)d document') success_message = _('Tag remove request performed on %(count)d document')
success_message_plural = _( success_message_plural = _(
'Tag remove request performed on %(count)d documents' 'Tag remove request performed on %(count)d documents'
) )
def get_extra_context(self): def get_extra_context(self):
queryset = self.get_queryset() queryset = self.get_object_list()
result = { result = {
'submit_icon_class': icon_tag_remove_submit, 'submit_icon_class': icon_document_tag_multiple_remove_submit,
'submit_label': _('Remove'), 'submit_label': _('Remove'),
'title': ungettext( 'title': ungettext(
singular='Remove tags to %(count)d document', singular='Remove tags to %(count)d document',
@@ -321,7 +324,7 @@ class TagRemoveActionView(MultipleObjectFormActionView):
return result return result
def get_form_extra_kwargs(self): def get_form_extra_kwargs(self):
queryset = self.get_queryset() queryset = self.get_object_list()
result = { result = {
'help_text': _('Tags to be removed.'), 'help_text': _('Tags to be removed.'),
'permission': permission_tag_remove, 'permission': permission_tag_remove,
@@ -338,41 +341,43 @@ class TagRemoveActionView(MultipleObjectFormActionView):
return result return result
def get_post_action_redirect(self): def get_post_action_redirect(self):
queryset = self.get_queryset() queryset = self.get_object_list()
if queryset.count() == 1: if queryset.count() == 1:
return reverse( return reverse(
viewname='tags:document_tags', kwargs={ viewname='tags:document_tag_list', kwargs={
'document_pk': queryset.first().pk 'document_id': queryset.first().pk
} }
) )
else: else:
return super(TagRemoveActionView, self).get_post_action_redirect() return super(TagRemoveActionView, self).get_post_action_redirect()
def object_action(self, form, instance): 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']: tag_list = AccessControlList.objects.restrict_queryset(
AccessControlList.objects.check_access( permission=permission_tag_remove,
obj=tag, permissions=permission_tag_remove, queryset=form.cleaned_data['tags'],
user=self.request.user user=self.request.user
) )
for tag in tag_list:
if tag not in attached_tags: if tag not in attached_tags:
messages.warning( messages.warning(
self.request, _( message=_(
'Document "%(document)s" wasn\'t tagged as "%(tag)s' 'Document "%(document)s" wasn\'t tagged as "%(tag)s'
) % { ) % {
'document': instance, 'tag': tag 'document': instance, 'tag': tag
} }, request=self.request
) )
else: else:
tag.remove_from(document=instance, user=self.request.user) tag.remove_from(document=instance, user=self.request.user)
messages.success( messages.success(
self.request, message=_(
_(
'Tag "%(tag)s" removed successfully from document ' 'Tag "%(tag)s" removed successfully from document '
'"%(document)s".' '"%(document)s".'
) % { ) % {
'document': instance, 'tag': tag 'document': instance, 'tag': tag
} }, request=self.request
) )

View File

@@ -18,39 +18,11 @@ class TagFormWidget(forms.SelectMultiple):
def create_option(self, name, value, label, selected, index, subindex=None, attrs=None): def create_option(self, name, value, label, selected, index, subindex=None, attrs=None):
result = super(TagFormWidget, self).create_option( result = super(TagFormWidget, self).create_option(
name=name, value=value, label='{}'.format(conditional_escape(label)), attrs=attrs, index=index,
selected=selected, index=index, subindex=subindex, attrs=attrs label='{}'.format(conditional_escape(label)), name=name,
selected=selected, subindex=subindex, value=value
) )
result['attrs']['data-color'] = self.queryset.get(pk=value).color result['attrs']['data-color'] = self.queryset.get(pk=value).color
return result 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 = ['<div class="tag-container">']
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('</div>')
if tags:
return mark_safe(''.join(result))
else:
return ''
def widget_single_tag(tag):
return render_to_string('tags/tag_widget.html', {'tag': tag})

View File

@@ -22,17 +22,10 @@ class WizardStepTags(WizardStep):
Tag = apps.get_model(app_label='tags', model_name='Tag') Tag = apps.get_model(app_label='tags', model_name='Tag')
return Tag.objects.exists() return Tag.objects.exists()
@classmethod
def get_form_kwargs(self, wizard):
return {
'help_text': _('Tags to be attached.'),
'user': wizard.request.user
}
@classmethod @classmethod
def done(cls, wizard): def done(cls, wizard):
result = {} 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: if cleaned_data:
result['tags'] = [ result['tags'] = [
force_text(tag.pk) for tag in cleaned_data['tags'] force_text(tag.pk) for tag in cleaned_data['tags']
@@ -40,13 +33,20 @@ class WizardStepTags(WizardStep):
return result return result
@classmethod
def get_form_kwargs(self, wizard):
return {
'help_text': _('Tags to be attached.'),
'user': wizard.request.user
}
@classmethod @classmethod
def step_post_upload_process(cls, document, querystring=None): 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') Tag = apps.get_model(app_label='tags', model_name='Tag')
for tag in Tag.objects.filter(pk__in=furl_instance.args.getlist('tags')): for tag in Tag.objects.filter(pk__in=furl_instance.args.getlist('tags')):
tag.documents.add(document) tag.documents.add(document)
WizardStep.register(WizardStepTags) WizardStep.register(step=WizardStepTags)

View File

@@ -38,8 +38,9 @@ class AttachTagAction(WorkflowAction):
user = request.user user = request.user
logger.debug('user: %s', user) logger.debug('user: %s', user)
queryset = AccessControlList.objects.filter_by_access( queryset = AccessControlList.objects.restrict_queryset(
self.permission, user, queryset=Tag.objects.all() permission=self.permission, queryset=Tag.objects.all(),
user=user
) )
self.fields['tags']['kwargs']['queryset'] = queryset self.fields['tags']['kwargs']['queryset'] = queryset