diff --git a/mayan/apps/document_states/apps.py b/mayan/apps/document_states/apps.py index f7e02372d9..65186bb408 100644 --- a/mayan/apps/document_states/apps.py +++ b/mayan/apps/document_states/apps.py @@ -40,6 +40,10 @@ from .links import ( link_setup_workflow_state_edit, link_setup_workflow_transitions, link_setup_workflow_transition_create, link_setup_workflow_transition_delete, link_setup_workflow_transition_edit, + link_setup_workflow_transition_field_create, + link_setup_workflow_transition_field_delete, + link_setup_workflow_transition_field_edit, + link_setup_workflow_transition_field_list, link_tool_launch_all_workflows, link_workflow_instance_detail, link_workflow_instance_transition, link_workflow_runtime_proxy_document_list, link_workflow_runtime_proxy_list, link_workflow_preview, @@ -86,6 +90,7 @@ class DocumentStatesApp(MayanAppConfig): WorkflowStateAction = self.get_model('WorkflowStateAction') WorkflowStateRuntimeProxy = self.get_model('WorkflowStateRuntimeProxy') WorkflowTransition = self.get_model('WorkflowTransition') + WorkflowTransitionField = self.get_model('WorkflowTransitionField') WorkflowTransitionTriggerEvent = self.get_model( 'WorkflowTransitionTriggerEvent' ) @@ -257,6 +262,18 @@ class DocumentStatesApp(MayanAppConfig): ) ) + SourceColumn( + attribute='name', is_identifier=True, is_sortable=True, + source=WorkflowTransitionField + ) + SourceColumn( + attribute='label', is_sortable=True, source=WorkflowTransitionField + ) + SourceColumn( + attribute='required', is_sortable=True, source=WorkflowTransitionField, + widget=TwoStateWidget + ) + SourceColumn( source=WorkflowRuntimeProxy, label=_('Documents'), func=lambda context: context['object'].get_document_count( @@ -305,10 +322,18 @@ class DocumentStatesApp(MayanAppConfig): menu_object.bind_links( links=( link_setup_workflow_transition_edit, - link_workflow_transition_events, link_acl_list, + link_workflow_transition_events, + link_setup_workflow_transition_field_list, + link_acl_list, link_setup_workflow_transition_delete ), sources=(WorkflowTransition,) ) + menu_object.bind_links( + links=( + link_setup_workflow_transition_field_delete, + link_setup_workflow_transition_field_edit + ), sources=(WorkflowTransitionField,) + ) menu_object.bind_links( links=( link_workflow_instance_detail, @@ -342,6 +367,12 @@ class DocumentStatesApp(MayanAppConfig): 'document_states:setup_workflow_list' ) ) + menu_secondary.bind_links( + links=(link_setup_workflow_transition_field_create,), + sources=( + WorkflowTransition, + ) + ) menu_secondary.bind_links( links=(link_workflow_runtime_proxy_list,), sources=( diff --git a/mayan/apps/document_states/forms.py b/mayan/apps/document_states/forms.py index 971b17bd2d..dfb2e6fd65 100644 --- a/mayan/apps/document_states/forms.py +++ b/mayan/apps/document_states/forms.py @@ -165,11 +165,11 @@ WorkflowTransitionTriggerEventRelationshipFormSet = formset_factory( ) -class WorkflowInstanceTransitionForm(forms.Form): +class WorkflowInstanceTransitionSelectForm(forms.Form): def __init__(self, *args, **kwargs): user = kwargs.pop('user') workflow_instance = kwargs.pop('workflow_instance') - super(WorkflowInstanceTransitionForm, self).__init__(*args, **kwargs) + super(WorkflowInstanceTransitionSelectForm, self).__init__(*args, **kwargs) self.fields[ 'transition' ].queryset = workflow_instance.get_transition_choices(_user=user) @@ -177,14 +177,6 @@ class WorkflowInstanceTransitionForm(forms.Form): transition = forms.ModelChoiceField( label=_('Transition'), queryset=WorkflowTransition.objects.none() ) - comment = forms.CharField( - help_text=_('Optional comment to attach to the transition.'), - label=_('Comment'), required=False, widget=forms.widgets.Textarea( - attrs={ - 'rows': 3 - } - ) - ) class WorkflowPreviewForm(forms.Form): diff --git a/mayan/apps/document_states/icons.py b/mayan/apps/document_states/icons.py index 8ae3b8e990..db6c5b5925 100644 --- a/mayan/apps/document_states/icons.py +++ b/mayan/apps/document_states/icons.py @@ -76,6 +76,16 @@ icon_workflow_transition_delete = Icon(driver_name='fontawesome', symbol='times' icon_workflow_transition_edit = Icon( driver_name='fontawesome', symbol='pencil-alt' ) + +icon_workflow_transition_field = Icon(driver_name='fontawesome', symbol='code') +icon_workflow_transition_field_delete = Icon(driver_name='fontawesome', symbol='times') +icon_workflow_transition_field_edit = Icon(driver_name='fontawesome', symbol='pencil-alt') +icon_workflow_transition_field_create = Icon( + driver_name='fontawesome-dual', primary_symbol='code', + secondary_symbol='plus' +) +icon_workflow_transition_field_list = Icon(driver_name='fontawesome', symbol='code') + icon_workflow_transition_triggers = Icon( driver_name='fontawesome', symbol='bolt' ) diff --git a/mayan/apps/document_states/links.py b/mayan/apps/document_states/links.py index 3d23efec95..1404ff2644 100644 --- a/mayan/apps/document_states/links.py +++ b/mayan/apps/document_states/links.py @@ -129,6 +129,35 @@ link_workflow_transition_events = Link( text=_('Transition triggers'), view='document_states:setup_workflow_transition_events' ) + +# Workflow transition fields +link_setup_workflow_transition_field_create = Link( + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition_field', + permissions=(permission_workflow_edit,), text=_('Create field'), + view='document_states:setup_workflow_transition_field_create', +) +link_setup_workflow_transition_field_delete = Link( + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition_field_delete', + permissions=(permission_workflow_edit,), + tags='dangerous', text=_('Delete'), + view='document_states:setup_workflow_transition_field_delete', +) +link_setup_workflow_transition_field_edit = Link( + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition_field_edit', + permissions=(permission_workflow_edit,), + text=_('Edit'), view='document_states:setup_workflow_transition_field_edit', +) +link_setup_workflow_transition_field_list = Link( + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition_field_list', + permissions=(permission_workflow_edit,), + text=_('Fields'), + view='document_states:setup_workflow_transition_field_list', +) + link_workflow_preview = Link( args='resolved_object.pk', icon_class_path='mayan.apps.document_states.icons.icon_workflow_preview', @@ -159,7 +188,7 @@ link_workflow_instance_transition = Link( args='resolved_object.pk', icon_class_path='mayan.apps.document_states.icons.icon_workflow_instance_transition', text=_('Transition'), - view='document_states:workflow_instance_transition', + view='document_states:workflow_instance_transition_selection', ) # Runtime proxies diff --git a/mayan/apps/document_states/migrations/0014_auto_20190630_1331.py b/mayan/apps/document_states/migrations/0014_auto_20190630_1331.py new file mode 100644 index 0000000000..8f4e567f9a --- /dev/null +++ b/mayan/apps/document_states/migrations/0014_auto_20190630_1331.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.11.20 on 2019-06-30 13:31 +from __future__ import unicode_literals + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('document_states', '0013_auto_20190423_0810'), + ] + + operations = [ + migrations.CreateModel( + name='WorkflowTransitionField', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=128, verbose_name='Internal name')), + ('label', models.CharField(max_length=128, verbose_name='Label')), + ('help_text', models.TextField(blank=True, verbose_name='Help text')), + ('required', models.BooleanField(default=False, verbose_name='Required')), + ('transition', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='fields', to='document_states.WorkflowTransition', verbose_name='Transition')), + ], + options={ + 'verbose_name': 'Workflow transition trigger event', + 'verbose_name_plural': 'Workflow transitions trigger events', + }, + ), + migrations.AddField( + model_name='workflowinstance', + name='context', + field=models.TextField(blank=True, verbose_name='Backend data'), + ), + migrations.AlterUniqueTogether( + name='workflowtransitionfield', + unique_together=set([('transition', 'name')]), + ), + ] diff --git a/mayan/apps/document_states/models.py b/mayan/apps/document_states/models.py index d006dcf6c3..7a4c6647bd 100644 --- a/mayan/apps/document_states/models.py +++ b/mayan/apps/document_states/models.py @@ -257,8 +257,8 @@ class WorkflowState(models.Model): def save(self, *args, **kwargs): # Solve issue #557 "Break workflows with invalid input" # without using a migration. - # Remove blank=True, remove this, and create a migration in the next - # minor version. + # TODO: Remove blank=True, remove this, and create a migration in the + # next minor version. try: self.completion = int(self.completion) @@ -363,6 +363,44 @@ class WorkflowTransition(models.Model): return self.label +@python_2_unicode_compatible +class WorkflowTransitionField(models.Model): + transition = models.ForeignKey( + on_delete=models.CASCADE, related_name='fields', + to=WorkflowTransition, verbose_name=_('Transition') + ) + name = models.CharField( + help_text=_( + 'The name that will be used to identify this field in other parts ' + 'of the workflow system.' + ), max_length=128, verbose_name=_('Internal name') + ) + label = models.CharField( + help_text=_( + 'The field name that will be shown on the user interface.' + ), max_length=128, verbose_name=_('Label')) + help_text = models.TextField( + blank=True, help_text=_( + 'An optional message that will help users better understand the ' + 'purpose of the field and data to provide.' + ), verbose_name=_('Help text') + ) + required = models.BooleanField( + default=False, help_text=_( + 'Whether this fields needs to be filled out or not to proceed.' + ), verbose_name=_('Required') + ) + #TODO: widget, widget kwargs + + class Meta: + unique_together = ('transition', 'name') + verbose_name = _('Workflow transition trigger event') + verbose_name_plural = _('Workflow transitions trigger events') + + def __str__(self): + return self.label + + @python_2_unicode_compatible class WorkflowTransitionTriggerEvent(models.Model): transition = models.ForeignKey( @@ -392,6 +430,9 @@ class WorkflowInstance(models.Model): on_delete=models.CASCADE, related_name='workflows', to=Document, verbose_name=_('Document') ) + context = models.TextField( + blank=True, verbose_name=_('Backend data') + ) class Meta: ordering = ('workflow',) @@ -402,15 +443,30 @@ class WorkflowInstance(models.Model): def __str__(self): return force_text(self.workflow) - def do_transition(self, transition, user=None, comment=None): - try: - if transition in self.get_current_state().origin_transitions.all(): - self.log_entries.create( - comment=comment or '', transition=transition, user=user - ) - except AttributeError: - # No initial state has been set for this workflow - pass + def do_transition(self, transition, extra_data=None, user=None, comment=None): + with transaction.atomic(): + try: + if transition in self.get_current_state().origin_transitions.all(): + self.log_entries.create( + comment=comment or '', transition=transition, user=user + ) + if extra_data: + data = self.loads() + data.update(extra_data) + self.dumps(data=data) + except AttributeError: + # No initial state has been set for this workflow + pass + + # TODO: execute transition event target = document, + # action_object = self + + def dumps(self, data): + """ + Serialize the context data. + """ + self.context = json.dumps(data) + self.save() def get_absolute_url(self): return reverse( @@ -420,10 +476,12 @@ class WorkflowInstance(models.Model): ) def get_context(self): - return { + context = { 'document': self.document, 'workflow': self.workflow, 'workflow_instance': self, } + context['workflow_instance_context'] = self.loads() + return context def get_current_state(self): """ @@ -489,6 +547,12 @@ class WorkflowInstance(models.Model): """ return WorkflowTransition.objects.none() + def loads(self): + """ + Deserialize the context data. + """ + return json.loads(self.context or '{}') + @python_2_unicode_compatible class WorkflowInstanceLogEntry(models.Model): @@ -529,31 +593,32 @@ class WorkflowInstanceLogEntry(models.Model): raise ValidationError(_('Not a valid transition choice.')) def save(self, *args, **kwargs): - result = super(WorkflowInstanceLogEntry, self).save(*args, **kwargs) - context = self.workflow_instance.get_context() - context.update( - { - 'entry_log': self - } - ) - - for action in self.transition.origin_state.exit_actions.filter(enabled=True): + with transaction.atomic(): + result = super(WorkflowInstanceLogEntry, self).save(*args, **kwargs) + context = self.workflow_instance.get_context() context.update( { - 'action': action, + 'entry_log': self } ) - action.execute(context=context) - for action in self.transition.destination_state.entry_actions.filter(enabled=True): - context.update( - { - 'action': action, - } - ) - action.execute(context=context) + for action in self.transition.origin_state.exit_actions.filter(enabled=True): + context.update( + { + 'action': action, + } + ) + action.execute(context=context) - return result + for action in self.transition.destination_state.entry_actions.filter(enabled=True): + context.update( + { + 'action': action, + } + ) + action.execute(context=context) + + return result class WorkflowRuntimeProxy(Workflow): diff --git a/mayan/apps/document_states/urls.py b/mayan/apps/document_states/urls.py index e139e3ff8a..49ccd4f19c 100644 --- a/mayan/apps/document_states/urls.py +++ b/mayan/apps/document_states/urls.py @@ -22,19 +22,86 @@ from .views import ( SetupWorkflowTransitionEditView, SetupWorkflowTransitionTriggerEventListView, ToolLaunchAllWorkflows, WorkflowDocumentListView, WorkflowInstanceDetailView, - WorkflowImageView, WorkflowInstanceTransitionView, WorkflowListView, + WorkflowImageView, WorkflowInstanceTransitionExecuteView, + WorkflowInstanceTransitionSelectView, WorkflowListView, WorkflowPreviewView, WorkflowStateDocumentListView, WorkflowStateListView, ) -from .views.workflow_views import SetupDocumentTypeWorkflowsView +from .views.workflow_views import ( + SetupDocumentTypeWorkflowsView, SetupWorkflowTransitionFieldCreateView, + SetupWorkflowTransitionFieldDeleteView, + SetupWorkflowTransitionFieldEditView, SetupWorkflowTransitionFieldListView +) urlpatterns_workflows = [ url( - regex=r'^document_type/(?P\d+)/workflows/$', + regex=r'^setup/workflows/$', view=SetupWorkflowListView.as_view(), + name='setup_workflow_list' + ), + url( + regex=r'^setup/workflows/create/$', view=SetupWorkflowCreateView.as_view(), + name='setup_workflow_create' + ), + url( + regex=r'^setup/workflows/(?P\d+)/delete/$', + view=SetupWorkflowDeleteView.as_view(), name='setup_workflow_delete' + ), + url( + regex=r'^setup/workflows/(?P\d+)/edit/$', + view=SetupWorkflowEditView.as_view(), name='setup_workflow_edit' + ), + url( + regex=r'^setup/document_types/(?P\d+)/workflows/$', view=SetupDocumentTypeWorkflowsView.as_view(), name='document_type_workflows' ), ] +urlpatterns_workflow_states = [ + url( + regex=r'^setup/workflow/(?P\d+)/states/$', + view=SetupWorkflowStateListView.as_view(), + name='setup_workflow_state_list' + ), + url( + regex=r'^setup/workflow/(?P\d+)/states/create/$', + view=SetupWorkflowStateCreateView.as_view(), + name='setup_workflow_state_create' + ), + url( + regex=r'^setup/workflow/state/(?P\d+)/delete/$', + view=SetupWorkflowStateDeleteView.as_view(), + name='setup_workflow_state_delete' + ), + url( + regex=r'^setup/workflow/state/(?P\d+)/edit/$', + view=SetupWorkflowStateEditView.as_view(), + name='setup_workflow_state_edit' + ), +] + +urlpatterns_workflow_transition_fields = [ + url( + regex=r'^setup/workflows/transitions/(?P\d+)/fields/create/$', + view=SetupWorkflowTransitionFieldCreateView.as_view(), + name='setup_workflow_transition_field_create' + ), + url( + regex=r'^setup/workflows/transitions/(?P\d+)/fields/$', + view=SetupWorkflowTransitionFieldListView.as_view(), + name='setup_workflow_transition_field_list' + ), + url( + regex=r'^setup/workflows/transitions/fields/(?P\d+)/delete/$', + view=SetupWorkflowTransitionFieldDeleteView.as_view(), + name='setup_workflow_transition_field_delete' + ), + url( + regex=r'^setup/workflows/transitions/fields/(?P\d+)/edit/$', + view=SetupWorkflowTransitionFieldEditView.as_view(), + name='setup_workflow_transition_field_edit' + ), +] + urlpatterns = [ url( regex=r'^document/(?P\d+)/workflows/$', @@ -47,25 +114,14 @@ urlpatterns = [ name='workflow_instance_detail' ), url( - regex=r'^document/workflows/(?P\d+)/transition/$', - view=WorkflowInstanceTransitionView.as_view(), - name='workflow_instance_transition' + regex=r'^document/workflows/(?P\d+)/transitions/select/$', + view=WorkflowInstanceTransitionSelectView.as_view(), + name='workflow_instance_transition_selection' ), url( - regex=r'^setup/all/$', view=SetupWorkflowListView.as_view(), - name='setup_workflow_list' - ), - url( - regex=r'^setup/create/$', view=SetupWorkflowCreateView.as_view(), - name='setup_workflow_create' - ), - url( - regex=r'^setup/workflow/(?P\d+)/edit/$', - view=SetupWorkflowEditView.as_view(), name='setup_workflow_edit' - ), - url( - regex=r'^setup/workflow/(?P\d+)/delete/$', - view=SetupWorkflowDeleteView.as_view(), name='setup_workflow_delete' + regex=r'^document/workflows/(?P\d+)/transitions/(?P\d+)/execute/$', + view=WorkflowInstanceTransitionExecuteView.as_view(), + name='workflow_instance_transition_execute' ), url( regex=r'^setup/workflow/(?P\d+)/documents/$', @@ -77,16 +133,6 @@ urlpatterns = [ view=SetupWorkflowDocumentTypesView.as_view(), name='setup_workflow_document_types' ), - url( - regex=r'^setup/workflow/(?P\d+)/states/$', - view=SetupWorkflowStateListView.as_view(), - name='setup_workflow_state_list' - ), - url( - regex=r'^setup/workflow/(?P\d+)/states/create/$', - view=SetupWorkflowStateCreateView.as_view(), - name='setup_workflow_state_create' - ), url( regex=r'^setup/workflow/(?P\d+)/transitions/$', view=SetupWorkflowTransitionListView.as_view(), @@ -98,20 +144,10 @@ urlpatterns = [ name='setup_workflow_transition_create' ), url( - regex=r'^setup/workflow/(?P\d+)/transitions/events/$', + regex=r'^setup/workflow/transitions/(?P\d+)/events/$', view=SetupWorkflowTransitionTriggerEventListView.as_view(), name='setup_workflow_transition_events' ), - url( - regex=r'^setup/workflow/state/(?P\d+)/delete/$', - view=SetupWorkflowStateDeleteView.as_view(), - name='setup_workflow_state_delete' - ), - url( - regex=r'^setup/workflow/state/(?P\d+)/edit/$', - view=SetupWorkflowStateEditView.as_view(), - name='setup_workflow_state_edit' - ), url( regex=r'^setup/workflow/state/(?P\d+)/actions/$', view=SetupWorkflowStateActionListView.as_view(), @@ -184,6 +220,8 @@ urlpatterns = [ ), ] urlpatterns.extend(urlpatterns_workflows) +urlpatterns.extend(urlpatterns_workflow_states) +urlpatterns.extend(urlpatterns_workflow_transition_fields) api_urls = [ url( diff --git a/mayan/apps/document_states/views/workflow_instance_views.py b/mayan/apps/document_states/views/workflow_instance_views.py index 66750fc468..f857049829 100644 --- a/mayan/apps/document_states/views/workflow_instance_views.py +++ b/mayan/apps/document_states/views/workflow_instance_views.py @@ -4,13 +4,16 @@ from django.contrib import messages from django.http import HttpResponseRedirect from django.shortcuts import get_object_or_404 from django.template import RequestContext +from django.urls import reverse, reverse_lazy from django.utils.translation import ugettext_lazy as _ from mayan.apps.acls.models import AccessControlList +from mayan.apps.common.forms import DynamicForm from mayan.apps.common.generics import FormView, SingleObjectListView +from mayan.apps.common.mixins import ExternalObjectMixin from mayan.apps.documents.models import Document -from ..forms import WorkflowInstanceTransitionForm +from ..forms import WorkflowInstanceTransitionSelectForm from ..icons import icon_workflow_instance_detail, icon_workflow_list from ..links import link_workflow_instance_transition from ..models import WorkflowInstance @@ -18,7 +21,8 @@ from ..permissions import permission_workflow_view __all__ = ( 'DocumentWorkflowInstanceListView', 'WorkflowInstanceDetailView', - 'WorkflowInstanceTransitionView' + 'WorkflowInstanceTransitionSelectView', + 'WorkflowInstanceTransitionExecuteView' ) @@ -100,14 +104,17 @@ class WorkflowInstanceDetailView(SingleObjectListView): return get_object_or_404(klass=WorkflowInstance, pk=self.kwargs['pk']) -class WorkflowInstanceTransitionView(FormView): - form_class = WorkflowInstanceTransitionForm +class WorkflowInstanceTransitionExecuteView(FormView): + form_class = DynamicForm template_name = 'appearance/generic_form.html' def form_valid(self, form): + form_data = form.cleaned_data + comment = form_data.pop('comment') + self.get_workflow_instance().do_transition( - comment=form.cleaned_data['comment'], - transition=form.cleaned_data['transition'], user=self.request.user + comment=comment, extra_data=form_data, + transition=self.get_workflow_transition(), user=self.request.user, ) messages.success( self.request, _( @@ -122,19 +129,96 @@ class WorkflowInstanceTransitionView(FormView): 'object': self.get_workflow_instance().document, 'submit_label': _('Submit'), 'title': _( - 'Do transition for workflow: %s' - ) % self.get_workflow_instance(), + 'Execute transition "%(transition)s" for workflow: %(workflow)s' + ) % { + 'transition': self.get_workflow_transition(), + 'workflow': self.get_workflow_instance(), + }, 'workflow_instance': self.get_workflow_instance(), } def get_form_extra_kwargs(self): - return { - 'user': self.request.user, - 'workflow_instance': self.get_workflow_instance() + schema = { + 'fields': { + 'comment': { + 'label': _('Comment'), + 'class': 'django.forms.CharField', 'kwargs': { + 'help_text': _( + 'Optional comment to attach to the transition.' + ), + 'required': False, + } + } + }, + 'widgets': { + 'comment': { + 'class': 'django.forms.widgets.Textarea', + 'kwargs': { + 'attrs': { + 'rows': 3 + } + } + } + } } + for field in self.get_workflow_transition().fields.all(): + schema['fields'][field.name] = { + 'label': field.label, + 'class': 'django.forms.CharField', 'kwargs': { + } + } + + return {'schema': schema} + def get_success_url(self): return self.get_workflow_instance().get_absolute_url() def get_workflow_instance(self): - return get_object_or_404(klass=WorkflowInstance, pk=self.kwargs['pk']) + return get_object_or_404( + klass=WorkflowInstance, pk=self.kwargs['workflow_instance_pk'] + ) + + def get_workflow_transition(self): + return get_object_or_404( + klass=self.get_workflow_instance().get_transition_choices( + _user=self.request.user + ), pk=self.kwargs['workflow_transition_pk'] + ) + + +class WorkflowInstanceTransitionSelectView(ExternalObjectMixin, FormView): + external_object_class = WorkflowInstance + form_class = WorkflowInstanceTransitionSelectForm + template_name = 'appearance/generic_form.html' + + def form_valid(self, form): + return HttpResponseRedirect( + redirect_to=reverse( + viewname='document_states:workflow_instance_transition_execute', + kwargs={ + 'workflow_instance_pk': self.external_object.pk, + 'workflow_transition_pk': form.cleaned_data['transition'].pk + } + ) + ) + + def get_extra_context(self): + return { + 'navigation_object_list': ('object', 'workflow_instance'), + 'object': self.external_object.document, + 'submit_label': _('Select'), + 'title': _( + 'Select transition for workflow: %s' + ) % self.external_object, + 'workflow_instance': self.external_object, + } + + def get_form_extra_kwargs(self): + return { + 'user': self.request.user, + 'workflow_instance': self.external_object + } + + #def get_workflow_instance(self): + # return get_object_or_404(klass=WorkflowInstance, pk=self.kwargs['pk']) diff --git a/mayan/apps/document_states/views/workflow_views.py b/mayan/apps/document_states/views/workflow_views.py index 423134e0b1..b28970e932 100644 --- a/mayan/apps/document_states/views/workflow_views.py +++ b/mayan/apps/document_states/views/workflow_views.py @@ -39,7 +39,8 @@ from ..links import ( link_setup_workflow_transition_create ) from ..models import ( - Workflow, WorkflowState, WorkflowStateAction, WorkflowTransition + Workflow, WorkflowState, WorkflowStateAction, WorkflowTransition, + WorkflowTransitionField ) from ..permissions import ( permission_workflow_create, permission_workflow_delete, @@ -732,6 +733,118 @@ class SetupWorkflowTransitionTriggerEventListView(ExternalObjectMixin, FormView) ) +# Transition fields + +class SetupWorkflowTransitionFieldCreateView(ExternalObjectMixin, SingleObjectCreateView): + external_object_class = WorkflowTransition + external_object_permission = permission_workflow_edit + fields = ('name', 'label', 'help_text', 'required') + #object_permission = permission_workflow_edit + + def get_extra_context(self): + return { + 'navigation_object_list': ('transition', 'workflow'), + 'transition': self.external_object, + 'title': _( + 'Create a field for workflow transition: %s' + ) % self.external_object, + 'workflow': self.external_object.workflow + } + + def get_instance_extra_data(self): + return { + 'transition': self.external_object, + } + + def get_queryset(self): + return self.external_object.fields.all() + + def get_post_action_redirect(self): + return reverse( + viewname='document_states:setup_workflow_transition_field_list', + kwargs={'pk': self.external_object.pk} + ) + + +class SetupWorkflowTransitionFieldDeleteView(SingleObjectDeleteView): + model = WorkflowTransitionField + object_permission = permission_workflow_edit + + def get_extra_context(self): + return { + 'navigation_object_list': ( + 'object', 'workflow_transition', 'workflow' + ), + 'object': self.object, + 'title': _('Delete workflow transition field: %s') % self.object, + 'workflow': self.object.transition.workflow, + 'workflow_transition': self.object.transition, + } + + def get_post_action_redirect(self): + return reverse( + viewname='document_states:setup_workflow_transition_field_list', + kwargs={'pk': self.object.transition.pk} + ) + + +class SetupWorkflowTransitionFieldEditView(SingleObjectEditView): + fields = ('name', 'label', 'help_text', 'required',) + model = WorkflowTransitionField + object_permission = permission_workflow_edit + + def get_extra_context(self): + return { + 'navigation_object_list': ( + 'object', 'workflow_transition', 'workflow' + ), + 'object': self.object, + 'title': _('Edit workflow transition field: %s') % self.object, + 'workflow': self.object.transition.workflow, + 'workflow_transition': self.object.transition, + } + + def get_post_action_redirect(self): + return reverse( + viewname='document_states:setup_workflow_transition_field_list', + kwargs={'pk': self.object.transition.pk} + ) + + +class SetupWorkflowTransitionFieldListView(ExternalObjectMixin, SingleObjectListView): + external_object_class = WorkflowTransition + external_object_permission = permission_workflow_edit + + def get_extra_context(self): + return { + 'hide_object': True, + 'navigation_object_list': ('object', 'workflow'), + #'no_results_icon': icon_workflow_transition_action, + #'no_results_main_link': link_setup_workflow_transition_action_selection.resolve( + # context=RequestContext( + # request=self.request, dict_={ + # 'object': self.get_workflow_transition() + # } + # ) + #), + #'no_results_text': _( + # 'Workflow state actions are macros that get executed when ' + # 'documents enters or leaves the state in which they reside.' + #), + #'no_results_title': _( + # 'There are no actions for this workflow state' + #), + 'object': self.external_object, + 'title': _( + 'Fields for workflow transition: %s' + ) % self.external_object, + 'workflow': self.external_object.workflow, + } + + def get_source_queryset(self): + return self.external_object.fields.all() + + class ToolLaunchAllWorkflows(ConfirmView): extra_context = { 'title': _('Launch all workflows?'),