From 99b180f269a92a8343dbb33ffad42e861c3ee209 Mon Sep 17 00:00:00 2001 From: Roberto Rosario Date: Sat, 4 May 2019 21:26:12 -0400 Subject: [PATCH] Add workflows per document type view Make workflows, workflows states, workflow transitions column sortable. Show completion and intial state in the workflow proxy instance menu list. Signed-off-by: Roberto Rosario --- HISTORY.rst | 6 ++ docs/releases/3.2.rst | 6 ++ mayan/apps/document_states/apps.py | 62 ++++++++++++------- mayan/apps/document_states/icons.py | 5 ++ mayan/apps/document_states/links.py | 31 +++++++--- mayan/apps/document_states/models.py | 1 + mayan/apps/document_states/urls.py | 10 +++ .../views/workflow_proxy_views.py | 2 +- .../document_states/views/workflow_views.py | 56 +++++++++++++++++ 9 files changed, 148 insertions(+), 31 deletions(-) diff --git a/HISTORY.rst b/HISTORY.rst index 777918bf5d..27d6e62d03 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -227,6 +227,12 @@ the new AddRemove view. * Add the workflow created and edited events. * Remove AssignRemove View. +* Add view to setup workflows per document type + from the document type side. +* Make workflows, workflows states, workflow + transitions column sortable. +* Show completion and intial state in the + workflow proxy instance menu list. 3.1.11 (2019-04-XX) =================== diff --git a/docs/releases/3.2.rst b/docs/releases/3.2.rst index e6cd2c6854..3d806359b9 100644 --- a/docs/releases/3.2.rst +++ b/docs/releases/3.2.rst @@ -260,6 +260,12 @@ Other changes the new AddRemove view. * Add the workflow created and edited events. * Remove AssignRemove View. +* Add view to setup workflows per document type + from the document type side. +* Make workflows, workflows states, workflow + transitions column sortable. +* Show completion and intial state in the + workflow proxy instance menu list. Removals -------- diff --git a/mayan/apps/document_states/apps.py b/mayan/apps/document_states/apps.py index 1cae86216d..f784c0af85 100644 --- a/mayan/apps/document_states/apps.py +++ b/mayan/apps/document_states/apps.py @@ -31,10 +31,11 @@ from .handlers import ( handler_index_document, handler_launch_workflow, handler_trigger_transition ) from .links import ( - link_document_workflow_instance_list, link_setup_workflow_document_types, - link_setup_workflow_create, link_setup_workflow_delete, - link_setup_workflow_edit, link_setup_workflow_list, - link_setup_workflow_states, link_setup_workflow_state_action_delete, + link_document_workflow_instance_list, link_setup_document_type_workflows, + link_setup_workflow_document_types, link_setup_workflow_create, + link_setup_workflow_delete, link_setup_workflow_edit, + link_setup_workflow_list, link_setup_workflow_states, + link_setup_workflow_state_action_delete, link_setup_workflow_state_action_edit, link_setup_workflow_state_action_list, link_setup_workflow_state_action_selection, @@ -74,6 +75,9 @@ class DocumentStatesApp(MayanAppConfig): Document = apps.get_model( app_label='documents', model_name='Document' ) + DocumentType = apps.get_model( + app_label='documents', model_name='DocumentType' + ) ErrorLogEntry = apps.get_model( app_label='common', model_name='ErrorLogEntry' ) @@ -158,15 +162,14 @@ class DocumentStatesApp(MayanAppConfig): ) SourceColumn( - source=Workflow, label=_('Label'), attribute='label' + attribute='label', is_sortable=True, source=Workflow ) SourceColumn( - source=Workflow, label=_('Internal name'), - attribute='internal_name' + attribute='internal_name', is_sortable=True, source=Workflow ) SourceColumn( - source=Workflow, label=_('Initial state'), - func=lambda context: context['object'].get_initial_state() or _('None') + attribute='get_initial_state', empty_value=_('None'), + source=Workflow ) SourceColumn( @@ -213,36 +216,42 @@ class DocumentStatesApp(MayanAppConfig): ) SourceColumn( - attribute='initial', label=_('Is initial state?'), - source=WorkflowState, widget=TwoStateWidget + attribute='label', is_sortable=True, source=WorkflowState ) SourceColumn( - source=WorkflowState, label=_('Completion'), attribute='completion' + attribute='initial', is_sortable=True, source=WorkflowState, + widget=TwoStateWidget + ) + SourceColumn( + attribute='completion', source=WorkflowState, is_sortable=True, ) SourceColumn( - source=WorkflowStateAction, label=_('Label'), attribute='label' + attribute='label', is_sortable=True, source=WorkflowStateAction ) SourceColumn( - attribute='enabled', label=_('Enabled?'), - source=WorkflowStateAction, widget=TwoStateWidget + attribute='enabled', is_sortable=True, source=WorkflowStateAction, + widget=TwoStateWidget ) SourceColumn( - source=WorkflowStateAction, label=_('When?'), - attribute='get_when_display' + attribute='get_when_display', label=_('When?'), + source=WorkflowStateAction ) SourceColumn( - source=WorkflowStateAction, label=_('Action type'), - attribute='get_class_label' + attribute='get_class_label', label=_('Action type'), + source=WorkflowStateAction ) SourceColumn( - source=WorkflowTransition, label=_('Origin state'), - attribute='origin_state' + attribute='label', is_sortable=True, source=WorkflowTransition, ) SourceColumn( - source=WorkflowTransition, label=_('Destination state'), - attribute='destination_state' + attribute='origin_state', is_sortable=True, + source=WorkflowTransition + ) + SourceColumn( + attribute='destination_state', is_sortable=True, + source=WorkflowTransition ) SourceColumn( source=WorkflowTransition, label=_('Triggers'), @@ -271,6 +280,7 @@ class DocumentStatesApp(MayanAppConfig): menu_facet.bind_links( links=(link_document_workflow_instance_list,), sources=(Document,) ) + menu_list_facet.bind_links( links=( link_acl_list, link_events_for_object, @@ -280,6 +290,12 @@ class DocumentStatesApp(MayanAppConfig): link_workflow_preview ), sources=(Workflow,) ) + menu_list_facet.bind_links( + links=( + link_setup_document_type_workflows, + ), sources=(DocumentType,) + ) + menu_main.bind_links(links=(link_workflow_runtime_proxy_list,), position=10) menu_object.bind_links( links=( diff --git a/mayan/apps/document_states/icons.py b/mayan/apps/document_states/icons.py index b0d836a433..8ae3b8e990 100644 --- a/mayan/apps/document_states/icons.py +++ b/mayan/apps/document_states/icons.py @@ -3,6 +3,11 @@ from __future__ import absolute_import, unicode_literals from mayan.apps.appearance.classes import Icon from mayan.apps.documents.icons import icon_document_type + +icon_workflow = Icon(driver_name='fontawesome', symbol='sitemap') + +icon_document_type_workflow_list = icon_workflow + icon_document_workflow_instance_list = Icon( driver_name='fontawesome', symbol='sitemap' ) diff --git a/mayan/apps/document_states/links.py b/mayan/apps/document_states/links.py index f6ce50c791..3d23efec95 100644 --- a/mayan/apps/document_states/links.py +++ b/mayan/apps/document_states/links.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from django.utils.translation import ugettext_lazy as _ +from mayan.apps.documents.permissions import permission_document_type_edit from mayan.apps.navigation.classes import Link from .permissions import ( @@ -10,6 +11,12 @@ from .permissions import ( permission_workflow_view, ) +link_setup_document_type_workflows = Link( + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_document_type_workflow_list', + permissions=(permission_document_type_edit,), text=_('Workflows'), + view='document_states:document_type_workflows', +) link_setup_workflow_create = Link( icon_class_path='mayan.apps.document_states.icons.icon_workflow_create', permissions=(permission_workflow_create,), @@ -67,17 +74,21 @@ link_setup_workflow_state_action_selection = Link( ) link_setup_workflow_state_create = Link( args='resolved_object.pk', - icon_class_path='mayan.apps.document_states.icons.icon_workflow_state', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_state_create', permissions=(permission_workflow_edit,), text=_('Create state'), view='document_states:setup_workflow_state_create', ) link_setup_workflow_state_delete = Link( - args='object.pk', permissions=(permission_workflow_edit,), + args='object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_state_delete', + permissions=(permission_workflow_edit,), tags='dangerous', text=_('Delete'), view='document_states:setup_workflow_state_delete', ) link_setup_workflow_state_edit = Link( - args='resolved_object.pk', permissions=(permission_workflow_edit,), + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_state_edit', + permissions=(permission_workflow_edit,), text=_('Edit'), view='document_states:setup_workflow_state_edit', ) link_setup_workflow_states = Link( @@ -88,17 +99,21 @@ link_setup_workflow_states = Link( ) link_setup_workflow_transition_create = Link( args='resolved_object.pk', - icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition_create', permissions=(permission_workflow_edit,), text=_('Create transition'), view='document_states:setup_workflow_transition_create', ) link_setup_workflow_transition_delete = Link( - args='resolved_object.pk', permissions=(permission_workflow_edit,), + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition_delete', + permissions=(permission_workflow_edit,), tags='dangerous', text=_('Delete'), view='document_states:setup_workflow_transition_delete', ) link_setup_workflow_transition_edit = Link( - args='resolved_object.pk', permissions=(permission_workflow_edit,), + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition_edit', + permissions=(permission_workflow_edit,), text=_('Edit'), view='document_states:setup_workflow_transition_edit', ) link_setup_workflow_transitions = Link( @@ -108,7 +123,9 @@ link_setup_workflow_transitions = Link( view='document_states:setup_workflow_transition_list', ) link_workflow_transition_events = Link( - args='resolved_object.pk', permissions=(permission_workflow_edit,), + args='resolved_object.pk', + icon_class_path='mayan.apps.document_states.icons.icon_workflow_transition_triggers', + permissions=(permission_workflow_edit,), text=_('Transition triggers'), view='document_states:setup_workflow_transition_events' ) diff --git a/mayan/apps/document_states/models.py b/mayan/apps/document_states/models.py index 6d7f7c1050..0244063faf 100644 --- a/mayan/apps/document_states/models.py +++ b/mayan/apps/document_states/models.py @@ -71,6 +71,7 @@ class Workflow(models.Model): return self.states.get(initial=True) except self.states.model.DoesNotExist: return None + get_initial_state.short_description = _('Initial state') def launch_for(self, document): try: diff --git a/mayan/apps/document_states/urls.py b/mayan/apps/document_states/urls.py index 75c8573b4c..e139e3ff8a 100644 --- a/mayan/apps/document_states/urls.py +++ b/mayan/apps/document_states/urls.py @@ -25,6 +25,15 @@ from .views import ( WorkflowImageView, WorkflowInstanceTransitionView, WorkflowListView, WorkflowPreviewView, WorkflowStateDocumentListView, WorkflowStateListView, ) +from .views.workflow_views import SetupDocumentTypeWorkflowsView + +urlpatterns_workflows = [ + url( + regex=r'^document_type/(?P\d+)/workflows/$', + view=SetupDocumentTypeWorkflowsView.as_view(), + name='document_type_workflows' + ), +] urlpatterns = [ url( @@ -174,6 +183,7 @@ urlpatterns = [ name='workflow_state_document_list' ), ] +urlpatterns.extend(urlpatterns_workflows) api_urls = [ url( diff --git a/mayan/apps/document_states/views/workflow_proxy_views.py b/mayan/apps/document_states/views/workflow_proxy_views.py index 8e7a72abcd..a26ee49e0e 100644 --- a/mayan/apps/document_states/views/workflow_proxy_views.py +++ b/mayan/apps/document_states/views/workflow_proxy_views.py @@ -131,8 +131,8 @@ class WorkflowStateListView(SingleObjectListView): def get_extra_context(self): return { - 'hide_columns': True, 'hide_link': True, + 'hide_object': True, 'no_results_main_link': link_setup_workflow_state_create.resolve( context=RequestContext( request=self.request, dict_={'object': self.get_workflow()} diff --git a/mayan/apps/document_states/views/workflow_views.py b/mayan/apps/document_states/views/workflow_views.py index 62c80dfc1f..cfbefd337a 100644 --- a/mayan/apps/document_states/views/workflow_views.py +++ b/mayan/apps/document_states/views/workflow_views.py @@ -64,6 +64,60 @@ __all__ = ( ) +class SetupDocumentTypeWorkflowsView(AddRemoveView): + main_object_permission = permission_document_type_edit + main_object_model = DocumentType + main_object_pk_url_kwarg = 'pk' + secondary_object_model = Workflow + secondary_object_permission = permission_workflow_edit + list_available_title = _('Available workflows') + list_added_title = _('Workflows assigned this document type') + related_field = 'workflows' + + def get_actions_extra_kwargs(self): + return {'_user': self.request.user} + + def get_extra_context(self): + return { + 'object': self.main_object, + 'subtitle': _( + 'Removing a workflow from a document type will also ' + 'remove all running instances of that workflow.' + ), + 'title': _( + 'Workflows assigned the document type: %s' + ) % self.main_object, + } + + def action_add(self, queryset, _user): + with transaction.atomic(): + event_document_type_edited.commit( + actor=_user, target=self.main_object + ) + + for obj in queryset: + self.main_object.workflows.add(obj) + event_workflow_edited.commit( + action_object=self.main_object, actor=_user, target=obj + ) + + def action_remove(self, queryset, _user): + with transaction.atomic(): + event_document_type_edited.commit( + actor=_user, target=self.main_object + ) + + for obj in queryset: + self.main_object.workflows.remove(obj) + event_workflow_edited.commit( + action_object=self.main_object, actor=_user, + target=obj + ) + obj.instances.filter( + document__document_type=self.main_object + ).delete() + + class SetupWorkflowListView(SingleObjectListView): model = Workflow object_permission = permission_workflow_view @@ -449,6 +503,7 @@ class SetupWorkflowStateListView(SingleObjectListView): def get_extra_context(self): return { 'hide_link': True, + 'hide_object': True, 'no_results_icon': icon_workflow_state, 'no_results_main_link': link_setup_workflow_state_create.resolve( context=RequestContext( @@ -577,6 +632,7 @@ class SetupWorkflowTransitionListView(SingleObjectListView): def get_extra_context(self): return { 'hide_link': True, + 'hide_object': True, 'no_results_icon': icon_workflow_transition, 'no_results_main_link': link_setup_workflow_transition_create.resolve( context=RequestContext(