Files
mayan-edms/mayan/apps/document_states/api_views.py
Roberto Rosario 8e69178e07 Project: Switch to full app paths
Instead of inserting the path of the apps into the Python app,
the apps are now referenced by their full import path.

This app name claves with external or native Python libraries.
Example: Mayan statistics app vs. Python new statistics library.

Every app reference is now prepended with 'mayan.apps'.

Existing config.yml files need to be updated manually.

Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
2018-12-05 02:04:20 -04:00

569 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 rest_framework import generics
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.filters import MayanObjectPermissionsFilter
from mayan.apps.rest_api.permissions import MayanPermission
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 .storages import storage_workflowimagecache
from .tasks import task_generate_workflow_image
class APIDocumentTypeWorkflowListView(generics.ListAPIView):
"""
get: Returns a list of all the document type workflows.
"""
filter_backends = (MayanObjectPermissionsFilter,)
mayan_object_permissions = {
'GET': (permission_workflow_view,),
}
serializer_class = WorkflowSerializer
def get_document_type(self):
document_type = get_object_or_404(DocumentType, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_document_type_view, user=self.request.user,
obj=document_type
)
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.
"""
filter_backends = (MayanObjectPermissionsFilter,)
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(Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user,
obj=workflow
)
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.
"""
filter_backends = (MayanObjectPermissionsFilter,)
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(Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user,
obj=workflow
)
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 APIWorkflowListView(generics.ListCreateAPIView):
"""
get: Returns a list of all the workflows.
post: Create a new workflow.
"""
filter_backends = (MayanObjectPermissionsFilter,)
mayan_object_permissions = {'GET': (permission_workflow_view,)}
mayan_view_permissions = {'POST': (permission_workflow_create,)}
permission_classes = (MayanPermission,)
queryset = Workflow.objects.all()
def get_serializer(self, *args, **kwargs):
if not self.request:
return None
return super(APIWorkflowListView, 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.
"""
filter_backends = (MayanObjectPermissionsFilter,)
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
class APIWorkflowImageView(generics.RetrieveAPIView):
"""
get: Returns an image representation of the selected workflow.
"""
filter_backends = (MayanObjectPermissionsFilter,)
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)
with storage_workflowimagecache.open(cache_filename) 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
# 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(Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user,
obj=workflow
)
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(Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user,
obj=workflow
)
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(Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user,
obj=workflow
)
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(Workflow, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_required, user=self.request.user,
obj=workflow
)
return workflow
# Document workflow views
class APIWorkflowInstanceListView(generics.ListAPIView):
"""
get: Returns a list of all the document workflows.
"""
filter_backends = (MayanObjectPermissionsFilter,)
serializer_class = WorkflowInstanceSerializer
mayan_object_permissions = {
'GET': (permission_workflow_view,),
}
def get_document(self):
document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=self.request.user,
obj=document
)
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.
"""
filter_backends = (MayanObjectPermissionsFilter,)
lookup_url_kwarg = 'workflow_pk'
mayan_object_permissions = {
'GET': (permission_workflow_view,),
}
serializer_class = WorkflowInstanceSerializer
def get_document(self):
document = get_object_or_404(Document, pk=self.kwargs['pk'])
AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=self.request.user,
obj=document
)
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(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
"""
AccessControlList.objects.check_access(
permissions=permission_workflow_view, user=self.request.user,
obj=document
)
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(
self.get_document().workflows, pk=self.kwargs['workflow_pk']
)
return workflow