Appearance: Fix form CSS media rendering
Fix the way the form CSS contained in the media attribute is rendered. This is now an interator and not a single value. Replace the current method with a for loop. Signed-off-by: Roberto Rosario <roberto.rosario.gonzalez@gmail.com>
This commit is contained in:
@@ -97,6 +97,9 @@
|
|||||||
cache invalidation is tied to index updates. This makes the
|
cache invalidation is tied to index updates. This makes the
|
||||||
timeout less relevant. The purpose of the cache timeout is
|
timeout less relevant. The purpose of the cache timeout is
|
||||||
now avoid runaway memory usage.
|
now avoid runaway memory usage.
|
||||||
|
- Refactored the workflow preview generation to work as an
|
||||||
|
background task API service. Solves GitLab issue #532.
|
||||||
|
A new task queue named "document_states_fast" was created.
|
||||||
|
|
||||||
3.1.9 (2018-11-01)
|
3.1.9 (2018-11-01)
|
||||||
==================
|
==================
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ user = root
|
|||||||
[program:mayan-worker-fast]
|
[program:mayan-worker-fast]
|
||||||
autorestart = false
|
autorestart = false
|
||||||
autostart = true
|
autostart = true
|
||||||
command = nice -n 1 /bin/bash -c "${MAYAN_BIN} celery --settings=${MAYAN_SETTINGS_MODULE} worker -Ofair -l ERROR -Q converter,sources_fast -n mayan-worker-fast.%%h ${MAYAN_WORKER_FAST_CONCURRENCY}"
|
command = nice -n 1 /bin/bash -c "${MAYAN_BIN} celery --settings=${MAYAN_SETTINGS_MODULE} worker -Ofair -l ERROR -Q converter,document_states_fast,sources_fast -n mayan-worker-fast.%%h ${MAYAN_WORKER_FAST_CONCURRENCY}"
|
||||||
killasgroup = true
|
killasgroup = true
|
||||||
numprocs = 1
|
numprocs = 1
|
||||||
priority = 998
|
priority = 998
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ Create the supervisor file at ``/etc/supervisor/conf.d/mayan.conf``:
|
|||||||
[program:mayan-worker-fast]
|
[program:mayan-worker-fast]
|
||||||
autorestart = true
|
autorestart = true
|
||||||
autostart = true
|
autostart = true
|
||||||
command = nice -n 1 /opt/mayan-edms/bin/mayan-edms.py celery worker -Ofair -l ERROR -Q converter,sources_fast -n mayan-worker-fast.%%h --concurrency=1
|
command = nice -n 1 /opt/mayan-edms/bin/mayan-edms.py celery worker -Ofair -l ERROR -Q converter,document_states_fast,sources_fast -n mayan-worker-fast.%%h --concurrency=1
|
||||||
killasgroup = true
|
killasgroup = true
|
||||||
numprocs = 1
|
numprocs = 1
|
||||||
priority = 998
|
priority = 998
|
||||||
@@ -276,7 +276,7 @@ Create the supervisor file at ``/etc/supervisor/conf.d/mayan.conf``:
|
|||||||
[program:mayan-worker-fast]
|
[program:mayan-worker-fast]
|
||||||
autorestart = true
|
autorestart = true
|
||||||
autostart = true
|
autostart = true
|
||||||
command = nice -n 1 /opt/mayan-edms/bin/mayan-edms.py celery worker -Ofair -l ERROR -Q converter,sources_fast -n mayan-worker-fast.%%h
|
command = nice -n 1 /opt/mayan-edms/bin/mayan-edms.py celery worker -Ofair -l ERROR -Q converter,document_states_fast,sources_fast -n mayan-worker-fast.%%h
|
||||||
killasgroup = true
|
killasgroup = true
|
||||||
numprocs = 1
|
numprocs = 1
|
||||||
priority = 998
|
priority = 998
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
{% load appearance_tags %}
|
{% load appearance_tags %}
|
||||||
|
|
||||||
{{ form.media.render_css|safe }}
|
{% for asset in form.media.render_css %}
|
||||||
|
{{ asset|safe }}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
{% for group, errors in form.errors.items %}
|
{% for group, errors in form.errors.items %}
|
||||||
{% for error in errors %}
|
{% for error in errors %}
|
||||||
@@ -38,83 +40,82 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tr>
|
</tr>
|
||||||
{% else %}
|
{% else %}
|
||||||
|
|
||||||
{% for field in form.hidden_fields %}
|
{% for field in form.hidden_fields %}
|
||||||
{{ field }}
|
{{ field }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% for field in form.visible_fields %}
|
{% for field in form.visible_fields %}
|
||||||
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
<div class="form-group {% if field.errors %}has-error{% endif %}">
|
||||||
{# We display the label then the field for all except checkboxes #}
|
{# We display the label then the field for all except checkboxes #}
|
||||||
{% if field|widget_type != 'checkboxinput' and not field.field.widget.attrs.hidden %}
|
{% if field|widget_type != 'checkboxinput' and not field.field.widget.attrs.hidden %}
|
||||||
{% if not hide_labels %}{{ field.label_tag }}{% if field.field.required and not read_only %} ({% trans 'required' %}){% endif %}{% endif %}
|
{% if not hide_labels %}{{ field.label_tag }}{% if field.field.required and not read_only %} ({% trans 'required' %}){% endif %}{% endif %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if field|widget_type == 'checkboxinput' %}
|
{% if field|widget_type == 'checkboxinput' %}
|
||||||
<div class="checkbox">
|
<div class="checkbox">
|
||||||
<label>
|
<label>
|
||||||
<input {% if field.value %}checked="checked"{% endif %} name="{% if form.prefix %}{{ form.prefix }}-{% endif %}{{ field.name }}" type="checkbox">
|
<input {% if field.value %}checked="checked"{% endif %} name="{% if form.prefix %}{{ form.prefix }}-{% endif %}{{ field.name }}" type="checkbox">
|
||||||
{% if not hide_labels %}{{ field.label }}{% if field.field.required and not read_only %} ({% trans 'required' %}){% endif %}{% endif %}
|
{% if not hide_labels %}{{ field.label }}{% if field.field.required and not read_only %} ({% trans 'required' %}){% endif %}{% endif %}
|
||||||
</label>
|
</label>
|
||||||
</div>
|
</div>
|
||||||
{% elif field|widget_type == 'emailinput' %}
|
{% elif field|widget_type == 'emailinput' %}
|
||||||
{% if read_only %}
|
{% if read_only %}
|
||||||
{{ field.value }}
|
{{ field.value }}
|
||||||
{% else %}
|
|
||||||
{% render_field field class+="form-control" %}
|
|
||||||
{% endif %}
|
|
||||||
{% elif field|widget_type == 'textinput' %}
|
|
||||||
{% if read_only %}
|
|
||||||
{{ field.value }}
|
|
||||||
{% else %}
|
|
||||||
{% render_field field class+="form-control" %}
|
|
||||||
{% endif %}
|
|
||||||
{% elif field|widget_type == 'textarea' %}
|
|
||||||
{% if read_only %}
|
|
||||||
{{ field.value }}
|
|
||||||
{% else %}
|
|
||||||
{% render_field field class+="form-control" %}
|
|
||||||
{% endif %}
|
|
||||||
{% elif field|widget_type == 'select' %}
|
|
||||||
{% if read_only %}
|
|
||||||
{{ field|get_choice_value }}
|
|
||||||
{% else %}
|
|
||||||
{% render_field field class+="form-control" %}
|
|
||||||
{% endif %}
|
|
||||||
{% elif field|widget_type == 'selectmultiple' %}
|
|
||||||
{% if read_only %}
|
|
||||||
{{ field|get_choice_value }}
|
|
||||||
{% else %}
|
|
||||||
{% render_field field class+="form-control" %}
|
|
||||||
{% endif %}
|
|
||||||
{% elif field|widget_type == 'clearablefileinput' %}
|
|
||||||
{# Don't add 'form-control' class to filebrowse fields #}
|
|
||||||
{% if field.errors %}
|
|
||||||
{% render_field field class+="form-control" %}
|
|
||||||
{% else %}
|
|
||||||
{% render_field field class+="" %}
|
|
||||||
{% endif %}
|
|
||||||
{% elif field|widget_type == 'radioselect' %}
|
|
||||||
<div class="radio">
|
|
||||||
{% render_field field %}
|
|
||||||
</div>
|
|
||||||
{% elif field|widget_type == 'checkboxselectmultiple' %}
|
|
||||||
{% for option in field %}
|
|
||||||
<div class="checkbox">
|
|
||||||
{{ option }}
|
|
||||||
</div>
|
|
||||||
{% endfor %}
|
|
||||||
{% elif field|widget_type == 'datetimeinput' or field|widget_type == 'dateinput' %}
|
|
||||||
{% if read_only %}
|
|
||||||
{{ field.value }}
|
|
||||||
{% else %}
|
|
||||||
{% render_field field class+="form-control" %}
|
|
||||||
{% endif %}
|
|
||||||
{% else %}
|
{% else %}
|
||||||
{% render_field field class+="form-control" %}
|
{% render_field field class+="form-control" %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
{% elif field|widget_type == 'textinput' %}
|
||||||
|
{% if read_only %}
|
||||||
|
{{ field.value }}
|
||||||
|
{% else %}
|
||||||
|
{% render_field field class+="form-control" %}
|
||||||
|
{% endif %}
|
||||||
|
{% elif field|widget_type == 'textarea' %}
|
||||||
|
{% if read_only %}
|
||||||
|
{{ field.value }}
|
||||||
|
{% else %}
|
||||||
|
{% render_field field class+="form-control" %}
|
||||||
|
{% endif %}
|
||||||
|
{% elif field|widget_type == 'select' %}
|
||||||
|
{% if read_only %}
|
||||||
|
{{ field|get_choice_value }}
|
||||||
|
{% else %}
|
||||||
|
{% render_field field class+="form-control" %}
|
||||||
|
{% endif %}
|
||||||
|
{% elif field|widget_type == 'selectmultiple' %}
|
||||||
|
{% if read_only %}
|
||||||
|
{{ field|get_choice_value }}
|
||||||
|
{% else %}
|
||||||
|
{% render_field field class+="form-control" %}
|
||||||
|
{% endif %}
|
||||||
|
{% elif field|widget_type == 'clearablefileinput' %}
|
||||||
|
{# Don't add 'form-control' class to filebrowse fields #}
|
||||||
|
{% if field.errors %}
|
||||||
|
{% render_field field class+="form-control" %}
|
||||||
|
{% else %}
|
||||||
|
{% render_field field class+="" %}
|
||||||
|
{% endif %}
|
||||||
|
{% elif field|widget_type == 'radioselect' %}
|
||||||
|
<div class="radio">
|
||||||
|
{% render_field field %}
|
||||||
|
</div>
|
||||||
|
{% elif field|widget_type == 'checkboxselectmultiple' %}
|
||||||
|
{% for option in field %}
|
||||||
|
<div class="checkbox">
|
||||||
|
{{ option }}
|
||||||
|
</div>
|
||||||
|
{% endfor %}
|
||||||
|
{% elif field|widget_type == 'datetimeinput' or field|widget_type == 'dateinput' %}
|
||||||
|
{% if read_only %}
|
||||||
|
{{ field.value }}
|
||||||
|
{% else %}
|
||||||
|
{% render_field field class+="form-control" %}
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{% render_field field class+="form-control" %}
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% if field.help_text %}<p class="help-block">{{ field.help_text|safe }}</p>{% endif %}
|
{% if field.help_text %}<p class="help-block">{{ field.help_text|safe }}</p>{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import absolute_import, unicode_literals
|
from __future__ import absolute_import, unicode_literals
|
||||||
|
|
||||||
|
from django.http import HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from rest_framework import generics
|
from rest_framework import generics
|
||||||
@@ -10,6 +11,7 @@ from documents.permissions import permission_document_type_view
|
|||||||
from rest_api.filters import MayanObjectPermissionsFilter
|
from rest_api.filters import MayanObjectPermissionsFilter
|
||||||
from rest_api.permissions import MayanPermission
|
from rest_api.permissions import MayanPermission
|
||||||
|
|
||||||
|
from .literals import WORKFLOW_IMAGE_TASK_TIMEOUT
|
||||||
from .models import Workflow
|
from .models import Workflow
|
||||||
from .permissions import (
|
from .permissions import (
|
||||||
permission_workflow_create, permission_workflow_delete,
|
permission_workflow_create, permission_workflow_delete,
|
||||||
@@ -22,6 +24,8 @@ from .serializers import (
|
|||||||
WritableWorkflowInstanceLogEntrySerializer, WritableWorkflowSerializer,
|
WritableWorkflowInstanceLogEntrySerializer, WritableWorkflowSerializer,
|
||||||
WritableWorkflowTransitionSerializer
|
WritableWorkflowTransitionSerializer
|
||||||
)
|
)
|
||||||
|
from .storages import storage_workflowimagecache
|
||||||
|
from .tasks import task_generate_workflow_image
|
||||||
|
|
||||||
|
|
||||||
class APIDocumentTypeWorkflowListView(generics.ListAPIView):
|
class APIDocumentTypeWorkflowListView(generics.ListAPIView):
|
||||||
@@ -223,6 +227,35 @@ class APIWorkflowView(generics.RetrieveUpdateDestroyAPIView):
|
|||||||
return WritableWorkflowSerializer
|
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
|
||||||
|
|
||||||
|
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')
|
||||||
|
return response
|
||||||
|
|
||||||
|
|
||||||
# Workflow state views
|
# Workflow state views
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -248,12 +248,23 @@ class DocumentStatesApp(MayanAppConfig):
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
app.conf.CELERY_QUEUES.extend(
|
||||||
|
(
|
||||||
|
Queue(
|
||||||
|
'document_states_fast', Exchange('document_states_fast'),
|
||||||
|
routing_key='document_states_fast'
|
||||||
|
),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
app.conf.CELERY_ROUTES.update(
|
app.conf.CELERY_ROUTES.update(
|
||||||
{
|
{
|
||||||
'document_states.tasks.task_launch_all_workflows': {
|
'document_states.tasks.task_generate_document_state_image': {
|
||||||
'queue': 'document_states'
|
'queue': 'document_states'
|
||||||
},
|
},
|
||||||
|
'document_states.tasks.task_launch_all_workflows': {
|
||||||
|
'queue': 'document_states_fast'
|
||||||
|
},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -178,7 +178,7 @@ class WorkflowInstanceTransitionForm(forms.Form):
|
|||||||
|
|
||||||
|
|
||||||
class WorkflowPreviewForm(forms.Form):
|
class WorkflowPreviewForm(forms.Form):
|
||||||
preview = forms.CharField(widget=WorkflowImageWidget())
|
preview = forms.IntegerField(widget=WorkflowImageWidget())
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
instance = kwargs.pop('instance', None)
|
instance = kwargs.pop('instance', None)
|
||||||
|
|||||||
@@ -9,3 +9,4 @@ WORKFLOW_ACTION_WHEN_CHOICES = (
|
|||||||
(WORKFLOW_ACTION_ON_ENTRY, _('On entry')),
|
(WORKFLOW_ACTION_ON_ENTRY, _('On entry')),
|
||||||
(WORKFLOW_ACTION_ON_EXIT, _('On exit')),
|
(WORKFLOW_ACTION_ON_EXIT, _('On exit')),
|
||||||
)
|
)
|
||||||
|
WORKFLOW_IMAGE_TASK_TIMEOUT = 60
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import logging
|
|||||||
from graphviz import Digraph
|
from graphviz import Digraph
|
||||||
|
|
||||||
from django.conf import settings
|
from django.conf import settings
|
||||||
|
from django.core.files.base import ContentFile
|
||||||
from django.core.exceptions import PermissionDenied, ValidationError
|
from django.core.exceptions import PermissionDenied, ValidationError
|
||||||
from django.db import IntegrityError, models
|
from django.db import IntegrityError, models
|
||||||
from django.db.models import F, Max, Q
|
from django.db.models import F, Max, Q
|
||||||
@@ -26,6 +27,7 @@ from .literals import (
|
|||||||
)
|
)
|
||||||
from .managers import WorkflowManager
|
from .managers import WorkflowManager
|
||||||
from .permissions import permission_workflow_transition
|
from .permissions import permission_workflow_transition
|
||||||
|
from .storages import storage_workflowimagecache
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
@@ -62,6 +64,22 @@ class Workflow(models.Model):
|
|||||||
def __str__(self):
|
def __str__(self):
|
||||||
return self.label
|
return self.label
|
||||||
|
|
||||||
|
def generate_image(self):
|
||||||
|
cache_filename = '{}'.format(self.id)
|
||||||
|
image = self.render()
|
||||||
|
|
||||||
|
# Since open "wb+" doesn't create files, check if the file
|
||||||
|
# exists, if not then create it
|
||||||
|
if not storage_workflowimagecache.exists(cache_filename):
|
||||||
|
storage_workflowimagecache.save(
|
||||||
|
name=cache_filename, content=ContentFile(content='')
|
||||||
|
)
|
||||||
|
|
||||||
|
with storage_workflowimagecache.open(cache_filename, 'wb+') as file_object:
|
||||||
|
file_object.write(image)
|
||||||
|
|
||||||
|
return cache_filename
|
||||||
|
|
||||||
def get_document_types_not_in_workflow(self):
|
def get_document_types_not_in_workflow(self):
|
||||||
return DocumentType.objects.exclude(pk__in=self.document_types.all())
|
return DocumentType.objects.exclude(pk__in=self.document_types.all())
|
||||||
|
|
||||||
|
|||||||
@@ -4,10 +4,19 @@ from django.utils.translation import ugettext_lazy as _
|
|||||||
|
|
||||||
from task_manager.classes import CeleryQueue
|
from task_manager.classes import CeleryQueue
|
||||||
|
|
||||||
|
|
||||||
queue_document_states = CeleryQueue(
|
queue_document_states = CeleryQueue(
|
||||||
name='document_states', label=_('Document states')
|
name='document_states', label=_('Document states')
|
||||||
)
|
)
|
||||||
|
queue_document_states_fast = CeleryQueue(
|
||||||
|
name='document_states_fast', label=_('Document states fast')
|
||||||
|
)
|
||||||
|
|
||||||
queue_document_states.add_task_type(
|
queue_document_states.add_task_type(
|
||||||
name='document_states.tasks.task_launch_all_workflows',
|
name='document_states.tasks.task_launch_all_workflows',
|
||||||
label=_('Launch all workflows')
|
label=_('Launch all workflows')
|
||||||
)
|
)
|
||||||
|
queue_document_states_fast.add_task_type(
|
||||||
|
name='document_states.tasks.task_generate_document_state_image',
|
||||||
|
label=_('Generate workflow previews')
|
||||||
|
)
|
||||||
|
|||||||
@@ -174,6 +174,7 @@ class WorkflowSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
document_types_url = serializers.HyperlinkedIdentityField(
|
document_types_url = serializers.HyperlinkedIdentityField(
|
||||||
view_name='rest_api:workflow-document-type-list'
|
view_name='rest_api:workflow-document-type-list'
|
||||||
)
|
)
|
||||||
|
image_url = serializers.SerializerMethodField()
|
||||||
states = WorkflowStateSerializer(many=True, required=False)
|
states = WorkflowStateSerializer(many=True, required=False)
|
||||||
transitions = WorkflowTransitionSerializer(many=True, required=False)
|
transitions = WorkflowTransitionSerializer(many=True, required=False)
|
||||||
|
|
||||||
@@ -182,11 +183,18 @@ class WorkflowSerializer(serializers.HyperlinkedModelSerializer):
|
|||||||
'url': {'view_name': 'rest_api:workflow-detail'},
|
'url': {'view_name': 'rest_api:workflow-detail'},
|
||||||
}
|
}
|
||||||
fields = (
|
fields = (
|
||||||
'document_types_url', 'id', 'internal_name', 'label', 'states',
|
'document_types_url', 'image_url', 'id', 'internal_name', 'label',
|
||||||
'transitions', 'url'
|
'states', 'transitions', 'url'
|
||||||
)
|
)
|
||||||
model = Workflow
|
model = Workflow
|
||||||
|
|
||||||
|
def get_image_url(self, instance):
|
||||||
|
return reverse(
|
||||||
|
'rest_api:workflow-image', args=(
|
||||||
|
instance.pk,
|
||||||
|
), request=self.context['request'], format=self.context['format']
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
class WorkflowInstanceLogEntrySerializer(serializers.ModelSerializer):
|
class WorkflowInstanceLogEntrySerializer(serializers.ModelSerializer):
|
||||||
document_workflow_url = serializers.SerializerMethodField()
|
document_workflow_url = serializers.SerializerMethodField()
|
||||||
|
|||||||
25
mayan/apps/document_states/settings.py
Normal file
25
mayan/apps/document_states/settings.py
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from django.conf import settings
|
||||||
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
|
from smart_settings import Namespace
|
||||||
|
|
||||||
|
namespace = Namespace(name='document_states', label=_('Workflows'))
|
||||||
|
|
||||||
|
setting_workflowimagecache_storage = namespace.add_setting(
|
||||||
|
global_name='WORKFLOWS_IMAGE_CACHE_STORAGE_BACKEND',
|
||||||
|
default='django.core.files.storage.FileSystemStorage', help_text=_(
|
||||||
|
'Path to the Storage subclass to use when storing the cached '
|
||||||
|
'workflow image files.'
|
||||||
|
)
|
||||||
|
)
|
||||||
|
setting_workflowimagecache_storage_arguments = namespace.add_setting(
|
||||||
|
global_name='WORKFLOWS_IMAGE_CACHE_STORAGE_BACKEND_ARGUMENTS',
|
||||||
|
default={'location': os.path.join(settings.MEDIA_ROOT, 'workflows')},
|
||||||
|
help_text=_(
|
||||||
|
'Arguments to pass to the WORKFLOWS_IMAGE_CACHE_STORAGE_BACKEND.'
|
||||||
|
)
|
||||||
|
)
|
||||||
12
mayan/apps/document_states/storages.py
Normal file
12
mayan/apps/document_states/storages.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
|
from django.utils.module_loading import import_string
|
||||||
|
|
||||||
|
from .settings import (
|
||||||
|
setting_workflowimagecache_storage,
|
||||||
|
setting_workflowimagecache_storage_arguments
|
||||||
|
)
|
||||||
|
|
||||||
|
storage_workflowimagecache = import_string(
|
||||||
|
dotted_path=setting_workflowimagecache_storage.value
|
||||||
|
)(**setting_workflowimagecache_storage_arguments.value)
|
||||||
@@ -9,6 +9,17 @@ from mayan.celery import app
|
|||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
@app.task()
|
||||||
|
def task_generate_workflow_image(document_state_id):
|
||||||
|
Workflow = apps.get_model(
|
||||||
|
app_label='document_states', model_name='Workflow'
|
||||||
|
)
|
||||||
|
|
||||||
|
workflow = Workflow.objects.get(pk=document_state_id)
|
||||||
|
|
||||||
|
return workflow.generate_image()
|
||||||
|
|
||||||
|
|
||||||
@app.task(ignore_result=True)
|
@app.task(ignore_result=True)
|
||||||
def task_launch_all_workflows():
|
def task_launch_all_workflows():
|
||||||
Document = apps.get_model(app_label='documents', model_name='Document')
|
Document = apps.get_model(app_label='documents', model_name='Document')
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
<img class="img-responsive" name="{{ widget.name }}" {% include "django/forms/widgets/attrs.html" %} src="{% url 'rest_api:workflow-image' widget.value.pk %}" style="margin:auto;" />
|
||||||
@@ -4,9 +4,10 @@ from django.conf.urls import url
|
|||||||
|
|
||||||
from .api_views import (
|
from .api_views import (
|
||||||
APIDocumentTypeWorkflowListView, APIWorkflowDocumentTypeList,
|
APIDocumentTypeWorkflowListView, APIWorkflowDocumentTypeList,
|
||||||
APIWorkflowDocumentTypeView, APIWorkflowInstanceListView,
|
APIWorkflowDocumentTypeView, APIWorkflowImageView,
|
||||||
APIWorkflowInstanceView, APIWorkflowInstanceLogEntryListView,
|
APIWorkflowInstanceListView, APIWorkflowInstanceView,
|
||||||
APIWorkflowListView, APIWorkflowStateListView, APIWorkflowStateView,
|
APIWorkflowInstanceLogEntryListView, APIWorkflowListView,
|
||||||
|
APIWorkflowStateListView, APIWorkflowStateView,
|
||||||
APIWorkflowTransitionListView, APIWorkflowTransitionView, APIWorkflowView
|
APIWorkflowTransitionListView, APIWorkflowTransitionView, APIWorkflowView
|
||||||
)
|
)
|
||||||
from .views import (
|
from .views import (
|
||||||
@@ -188,6 +189,10 @@ api_urls = [
|
|||||||
r'^workflows/(?P<pk>[0-9]+)/$', APIWorkflowView.as_view(),
|
r'^workflows/(?P<pk>[0-9]+)/$', APIWorkflowView.as_view(),
|
||||||
name='workflow-detail'
|
name='workflow-detail'
|
||||||
),
|
),
|
||||||
|
url(
|
||||||
|
r'^workflows/(?P<pk>[0-9]+)/image/$',
|
||||||
|
APIWorkflowImageView.as_view(), name='workflow-image'
|
||||||
|
),
|
||||||
url(
|
url(
|
||||||
r'^workflows/(?P<pk>[0-9]+)/document_types/$',
|
r'^workflows/(?P<pk>[0-9]+)/document_types/$',
|
||||||
APIWorkflowDocumentTypeList.as_view(),
|
APIWorkflowDocumentTypeList.as_view(),
|
||||||
|
|||||||
@@ -1,8 +1,7 @@
|
|||||||
from __future__ import unicode_literals
|
from __future__ import unicode_literals
|
||||||
|
|
||||||
from django import forms
|
from django import forms
|
||||||
from django.urls import reverse
|
from django.utils.html import format_html_join
|
||||||
from django.utils.html import format_html_join, mark_safe
|
|
||||||
|
|
||||||
|
|
||||||
def widget_transition_events(transition):
|
def widget_transition_events(transition):
|
||||||
@@ -15,19 +14,10 @@ def widget_transition_events(transition):
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def widget_workflow_diagram(workflow):
|
|
||||||
return mark_safe(
|
|
||||||
'<img class="img-responsive" src="{}" style="margin:auto;">'.format(
|
|
||||||
reverse('document_states:workflow_image', args=(workflow.pk,))
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
class WorkflowImageWidget(forms.widgets.Widget):
|
class WorkflowImageWidget(forms.widgets.Widget):
|
||||||
def render(self, name, value, attrs=None):
|
template_name = 'document_states/forms/widgets/workflow_image.html'
|
||||||
if value:
|
|
||||||
output = []
|
def format_value(self, value):
|
||||||
output.append(widget_workflow_diagram(value))
|
if value == '' or value is None:
|
||||||
return mark_safe(''.join(output))
|
return None
|
||||||
else:
|
return value
|
||||||
return ''
|
|
||||||
|
|||||||
Reference in New Issue
Block a user