- Move all generic API classes definitions to the rest_api.generics module. - Update API status code on insufficient access for the apps: indexes, parsing, documents, metadata, ocr, permission, user management. - Update API tests. Signed-off-by: Roberto Rosario <roberto.rosario@mayan-edms.com>
561 lines
17 KiB
Python
561 lines
17 KiB
Python
from __future__ import absolute_import, unicode_literals
|
|
|
|
from django.http import HttpResponse
|
|
from django.shortcuts import get_object_or_404
|
|
from django.views.decorators.cache import cache_control, patch_cache_control
|
|
|
|
from mayan.apps.acls.models import AccessControlList
|
|
from mayan.apps.documents.models import Document, DocumentType
|
|
from mayan.apps.documents.permissions import permission_document_type_view
|
|
from mayan.apps.rest_api import generics
|
|
|
|
from .literals import WORKFLOW_IMAGE_TASK_TIMEOUT
|
|
from .models import Workflow
|
|
from .permissions import (
|
|
permission_workflow_create, permission_workflow_delete,
|
|
permission_workflow_edit, permission_workflow_view
|
|
)
|
|
from .serializers import (
|
|
NewWorkflowDocumentTypeSerializer, WorkflowDocumentTypeSerializer,
|
|
WorkflowInstanceSerializer, WorkflowInstanceLogEntrySerializer,
|
|
WorkflowSerializer, WorkflowStateSerializer, WorkflowTransitionSerializer,
|
|
WritableWorkflowInstanceLogEntrySerializer, WritableWorkflowSerializer,
|
|
WritableWorkflowTransitionSerializer
|
|
)
|
|
|
|
from .settings import settings_workflow_image_cache_time
|
|
from .tasks import task_generate_workflow_image
|
|
|
|
|
|
class APIDocumentTypeWorkflowRuntimeProxyListView(generics.ListAPIView):
|
|
"""
|
|
get: Returns a list of all the document type workflows.
|
|
"""
|
|
mayan_object_permissions = {
|
|
'GET': (permission_workflow_view,),
|
|
}
|
|
serializer_class = WorkflowSerializer
|
|
|
|
def get_document_type(self):
|
|
document_type = get_object_or_404(
|
|
klass=DocumentType, pk=self.kwargs['pk']
|
|
)
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=document_type, permissions=(permission_document_type_view,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return document_type
|
|
|
|
def get_queryset(self):
|
|
return self.get_document_type().workflows.all()
|
|
|
|
|
|
class APIWorkflowDocumentTypeList(generics.ListCreateAPIView):
|
|
"""
|
|
get: Returns a list of all the document types attached to a workflow.
|
|
post: Attach a document type to a specified workflow.
|
|
"""
|
|
mayan_object_permissions = {
|
|
'GET': (permission_document_type_view,),
|
|
}
|
|
|
|
def get_queryset(self):
|
|
"""
|
|
This view returns a list of document types that belong to a workflow.
|
|
"""
|
|
return self.get_workflow().document_types.all()
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
if not self.request:
|
|
return None
|
|
|
|
return super(APIWorkflowDocumentTypeList, self).get_serializer(*args, **kwargs)
|
|
|
|
def get_serializer_class(self):
|
|
if self.request.method == 'GET':
|
|
return WorkflowDocumentTypeSerializer
|
|
elif self.request.method == 'POST':
|
|
return NewWorkflowDocumentTypeSerializer
|
|
|
|
def get_serializer_context(self):
|
|
"""
|
|
Extra context provided to the serializer class.
|
|
"""
|
|
context = super(APIWorkflowDocumentTypeList, self).get_serializer_context()
|
|
if self.kwargs:
|
|
context.update(
|
|
{
|
|
'workflow': self.get_workflow(),
|
|
}
|
|
)
|
|
|
|
return context
|
|
|
|
def get_workflow(self):
|
|
"""
|
|
Retrieve the parent workflow of the workflow document type.
|
|
Perform custom permission and access check.
|
|
"""
|
|
if self.request.method == 'GET':
|
|
permission_required = permission_workflow_view
|
|
else:
|
|
permission_required = permission_workflow_edit
|
|
|
|
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=workflow, permissions=(permission_required,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return workflow
|
|
|
|
|
|
class APIWorkflowDocumentTypeView(generics.RetrieveDestroyAPIView):
|
|
"""
|
|
delete: Remove a document type from the selected workflow.
|
|
get: Returns the details of the selected workflow document type.
|
|
"""
|
|
lookup_url_kwarg = 'document_type_pk'
|
|
mayan_object_permissions = {
|
|
'GET': (permission_document_type_view,),
|
|
}
|
|
serializer_class = WorkflowDocumentTypeSerializer
|
|
|
|
def get_queryset(self):
|
|
return self.get_workflow().document_types.all()
|
|
|
|
def get_serializer_context(self):
|
|
"""
|
|
Extra context provided to the serializer class.
|
|
"""
|
|
context = super(APIWorkflowDocumentTypeView, self).get_serializer_context()
|
|
if self.kwargs:
|
|
context.update(
|
|
{
|
|
'workflow': self.get_workflow(),
|
|
}
|
|
)
|
|
|
|
return context
|
|
|
|
def get_workflow(self):
|
|
"""
|
|
This view returns a document types that belongs to a workflow
|
|
RESEARCH: Could the documents.api_views.APIDocumentTypeView class
|
|
be subclasses for this?
|
|
RESEARCH: Since this is a parent-child API view could this be made
|
|
into a generic API class?
|
|
RESEARCH: Reuse get_workflow method from APIWorkflowDocumentTypeList?
|
|
"""
|
|
if self.request.method == 'GET':
|
|
permission_required = permission_workflow_view
|
|
else:
|
|
permission_required = permission_workflow_edit
|
|
|
|
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=workflow, permissions=(permission_required,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return workflow
|
|
|
|
def perform_destroy(self, instance):
|
|
"""
|
|
RESEARCH: Move this kind of methods to the serializer instead it that
|
|
ability becomes available in Django REST framework
|
|
"""
|
|
self.get_workflow().document_types.remove(instance)
|
|
|
|
|
|
class APIWorkflowImageView(generics.RetrieveAPIView):
|
|
"""
|
|
get: Returns an image representation of the selected workflow.
|
|
"""
|
|
mayan_object_permissions = {
|
|
'GET': (permission_workflow_view,),
|
|
}
|
|
queryset = Workflow.objects.all()
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
return None
|
|
|
|
def get_serializer_class(self):
|
|
return None
|
|
|
|
@cache_control(private=True)
|
|
def retrieve(self, request, *args, **kwargs):
|
|
task = task_generate_workflow_image.apply_async(
|
|
kwargs=dict(
|
|
document_state_id=self.get_object().pk,
|
|
)
|
|
)
|
|
|
|
cache_filename = task.get(timeout=WORKFLOW_IMAGE_TASK_TIMEOUT)
|
|
cache_file = self.get_object().cache_partition.get_file(filename=cache_filename)
|
|
with cache_file.open() as file_object:
|
|
response = HttpResponse(file_object.read(), content_type='image')
|
|
if '_hash' in request.GET:
|
|
patch_cache_control(
|
|
response,
|
|
max_age=settings_workflow_image_cache_time.value
|
|
)
|
|
return response
|
|
|
|
|
|
class APIWorkflowRuntimeProxyListView(generics.ListCreateAPIView):
|
|
"""
|
|
get: Returns a list of all the workflows.
|
|
post: Create a new workflow.
|
|
"""
|
|
mayan_object_permissions = {'GET': (permission_workflow_view,)}
|
|
mayan_view_permissions = {'POST': (permission_workflow_create,)}
|
|
queryset = Workflow.objects.all()
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
if not self.request:
|
|
return None
|
|
|
|
return super(APIWorkflowRuntimeProxyListView, self).get_serializer(*args, **kwargs)
|
|
|
|
def get_serializer_class(self):
|
|
if self.request.method == 'GET':
|
|
return WorkflowSerializer
|
|
else:
|
|
return WritableWorkflowSerializer
|
|
|
|
|
|
class APIWorkflowView(generics.RetrieveUpdateDestroyAPIView):
|
|
"""
|
|
delete: Delete the selected workflow.
|
|
get: Return the details of the selected workflow.
|
|
patch: Edit the selected workflow.
|
|
put: Edit the selected workflow.
|
|
"""
|
|
mayan_object_permissions = {
|
|
'DELETE': (permission_workflow_delete,),
|
|
'GET': (permission_workflow_view,),
|
|
'PATCH': (permission_workflow_edit,),
|
|
'PUT': (permission_workflow_edit,)
|
|
}
|
|
queryset = Workflow.objects.all()
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
if not self.request:
|
|
return None
|
|
|
|
return super(APIWorkflowView, self).get_serializer(*args, **kwargs)
|
|
|
|
def get_serializer_class(self):
|
|
if self.request.method == 'GET':
|
|
return WorkflowSerializer
|
|
else:
|
|
return WritableWorkflowSerializer
|
|
|
|
|
|
# Workflow state views
|
|
|
|
|
|
class APIWorkflowStateListView(generics.ListCreateAPIView):
|
|
"""
|
|
get: Returns a list of all the workflow states.
|
|
post: Create a new workflow state.
|
|
"""
|
|
serializer_class = WorkflowStateSerializer
|
|
|
|
def get_queryset(self):
|
|
return self.get_workflow().states.all()
|
|
|
|
def get_serializer_context(self):
|
|
"""
|
|
Extra context provided to the serializer class.
|
|
"""
|
|
context = super(APIWorkflowStateListView, self).get_serializer_context()
|
|
if self.kwargs:
|
|
context.update(
|
|
{
|
|
'workflow': self.get_workflow(),
|
|
}
|
|
)
|
|
|
|
return context
|
|
|
|
def get_workflow(self):
|
|
if self.request.method == 'GET':
|
|
permission_required = permission_workflow_view
|
|
else:
|
|
permission_required = permission_workflow_edit
|
|
|
|
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=workflow, permissions=(permission_required,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return workflow
|
|
|
|
|
|
class APIWorkflowStateView(generics.RetrieveUpdateDestroyAPIView):
|
|
"""
|
|
delete: Delete the selected workflow state.
|
|
get: Return the details of the selected workflow state.
|
|
patch: Edit the selected workflow state.
|
|
put: Edit the selected workflow state.
|
|
"""
|
|
lookup_url_kwarg = 'state_pk'
|
|
serializer_class = WorkflowStateSerializer
|
|
|
|
def get_queryset(self):
|
|
return self.get_workflow().states.all()
|
|
|
|
def get_serializer_context(self):
|
|
"""
|
|
Extra context provided to the serializer class.
|
|
"""
|
|
context = super(APIWorkflowStateView, self).get_serializer_context()
|
|
if self.kwargs:
|
|
context.update(
|
|
{
|
|
'workflow': self.get_workflow(),
|
|
}
|
|
)
|
|
|
|
return context
|
|
|
|
def get_workflow(self):
|
|
if self.request.method == 'GET':
|
|
permission_required = permission_workflow_view
|
|
else:
|
|
permission_required = permission_workflow_edit
|
|
|
|
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=workflow, permissions=(permission_required,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return workflow
|
|
|
|
|
|
# Workflow transition views
|
|
|
|
|
|
class APIWorkflowTransitionListView(generics.ListCreateAPIView):
|
|
"""
|
|
get: Returns a list of all the workflow transitions.
|
|
post: Create a new workflow transition.
|
|
"""
|
|
def get_queryset(self):
|
|
return self.get_workflow().transitions.all()
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
if not self.request:
|
|
return None
|
|
|
|
return super(APIWorkflowTransitionListView, self).get_serializer(*args, **kwargs)
|
|
|
|
def get_serializer_class(self):
|
|
if self.request.method == 'GET':
|
|
return WorkflowTransitionSerializer
|
|
else:
|
|
return WritableWorkflowTransitionSerializer
|
|
|
|
def get_serializer_context(self):
|
|
"""
|
|
Extra context provided to the serializer class.
|
|
"""
|
|
context = super(APIWorkflowTransitionListView, self).get_serializer_context()
|
|
if self.kwargs:
|
|
context.update(
|
|
{
|
|
'workflow': self.get_workflow(),
|
|
}
|
|
)
|
|
|
|
return context
|
|
|
|
def get_workflow(self):
|
|
if self.request.method == 'GET':
|
|
permission_required = permission_workflow_view
|
|
else:
|
|
permission_required = permission_workflow_edit
|
|
|
|
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=workflow, permissions=(permission_required,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return workflow
|
|
|
|
|
|
class APIWorkflowTransitionView(generics.RetrieveUpdateDestroyAPIView):
|
|
"""
|
|
delete: Delete the selected workflow transition.
|
|
get: Return the details of the selected workflow transition.
|
|
patch: Edit the selected workflow transition.
|
|
put: Edit the selected workflow transition.
|
|
"""
|
|
lookup_url_kwarg = 'transition_pk'
|
|
|
|
def get_queryset(self):
|
|
return self.get_workflow().transitions.all()
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
if not self.request:
|
|
return None
|
|
|
|
return super(APIWorkflowTransitionView, self).get_serializer(*args, **kwargs)
|
|
|
|
def get_serializer_class(self):
|
|
if self.request.method == 'GET':
|
|
return WorkflowTransitionSerializer
|
|
else:
|
|
return WritableWorkflowTransitionSerializer
|
|
|
|
def get_serializer_context(self):
|
|
"""
|
|
Extra context provided to the serializer class.
|
|
"""
|
|
context = super(APIWorkflowTransitionView, self).get_serializer_context()
|
|
if self.kwargs:
|
|
context.update(
|
|
{
|
|
'workflow': self.get_workflow(),
|
|
}
|
|
)
|
|
|
|
return context
|
|
|
|
def get_workflow(self):
|
|
if self.request.method == 'GET':
|
|
permission_required = permission_workflow_view
|
|
else:
|
|
permission_required = permission_workflow_edit
|
|
|
|
workflow = get_object_or_404(klass=Workflow, pk=self.kwargs['pk'])
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=workflow, permissions=(permission_required,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return workflow
|
|
|
|
|
|
# Document workflow views
|
|
|
|
|
|
class APIWorkflowInstanceListView(generics.ListAPIView):
|
|
"""
|
|
get: Returns a list of all the document workflows.
|
|
"""
|
|
serializer_class = WorkflowInstanceSerializer
|
|
mayan_object_permissions = {
|
|
'GET': (permission_workflow_view,),
|
|
}
|
|
|
|
def get_document(self):
|
|
document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=document, permissions=(permission_workflow_view,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return document
|
|
|
|
def get_queryset(self):
|
|
return self.get_document().workflows.all()
|
|
|
|
|
|
class APIWorkflowInstanceView(generics.RetrieveAPIView):
|
|
"""
|
|
get: Return the details of the selected document workflow.
|
|
"""
|
|
lookup_url_kwarg = 'workflow_pk'
|
|
mayan_object_permissions = {
|
|
'GET': (permission_workflow_view,),
|
|
}
|
|
serializer_class = WorkflowInstanceSerializer
|
|
|
|
def get_document(self):
|
|
document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
|
|
|
|
AccessControlList.objects.check_access(
|
|
obj=document, permissions=(permission_workflow_view,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return document
|
|
|
|
def get_queryset(self):
|
|
return self.get_document().workflows.all()
|
|
|
|
|
|
class APIWorkflowInstanceLogEntryListView(generics.ListCreateAPIView):
|
|
"""
|
|
get: Returns a list of all the document workflows log entries.
|
|
post: Transition a document workflow by creating a new document workflow log entry.
|
|
"""
|
|
def get_document(self):
|
|
document = get_object_or_404(klass=Document, pk=self.kwargs['pk'])
|
|
|
|
if self.request.method == 'GET':
|
|
"""
|
|
Only test for permission if reading. If writing, the permission
|
|
will be checked in the serializer
|
|
|
|
IMPROVEMENT:
|
|
When writing, add check for permission or ACL for the workflow.
|
|
Failing that, check for ACLs for any of the workflow's transitions.
|
|
Failing that, then raise PermissionDenied
|
|
"""
|
|
# TODO: Improvement above
|
|
AccessControlList.objects.check_access(
|
|
obj=document, permissions=(permission_workflow_view,),
|
|
user=self.request.user
|
|
)
|
|
|
|
return document
|
|
|
|
def get_serializer(self, *args, **kwargs):
|
|
if not self.request:
|
|
return None
|
|
|
|
return super(APIWorkflowInstanceLogEntryListView, self).get_serializer(*args, **kwargs)
|
|
|
|
def get_serializer_class(self):
|
|
if self.request.method == 'GET':
|
|
return WorkflowInstanceLogEntrySerializer
|
|
else:
|
|
return WritableWorkflowInstanceLogEntrySerializer
|
|
|
|
def get_serializer_context(self):
|
|
context = super(APIWorkflowInstanceLogEntryListView, self).get_serializer_context()
|
|
if self.kwargs:
|
|
context.update(
|
|
{
|
|
'workflow_instance': self.get_workflow_instance(),
|
|
}
|
|
)
|
|
|
|
return context
|
|
|
|
def get_queryset(self):
|
|
return self.get_workflow_instance().log_entries.all()
|
|
|
|
def get_workflow_instance(self):
|
|
workflow = get_object_or_404(
|
|
klass=self.get_document().workflows, pk=self.kwargs['workflow_pk']
|
|
)
|
|
|
|
return workflow
|