web/views/workflow.py
changeset 3230 1d25e928c299
parent 2968 0e3460341023
parent 3228 7b05b2709439
child 3293 69c0ba095536
equal deleted inserted replaced
3199:fc63b80ec979 3230:1d25e928c299
    13 
    13 
    14 from logilab.mtconverter import xml_escape
    14 from logilab.mtconverter import xml_escape
    15 from logilab.common.graph import escape, GraphGenerator, DotBackend
    15 from logilab.common.graph import escape, GraphGenerator, DotBackend
    16 
    16 
    17 from cubicweb import Unauthorized, view
    17 from cubicweb import Unauthorized, view
    18 from cubicweb.selectors import (implements, has_related_entities,
    18 from cubicweb.selectors import (implements, has_related_entities, one_line_rset,
    19                                 relation_possible, match_form_params)
    19                                 relation_possible, match_form_params)
    20 from cubicweb.interfaces import IWorkflowable
    20 from cubicweb.interfaces import IWorkflowable
    21 from cubicweb.view import EntityView
    21 from cubicweb.view import EntityView
    22 from cubicweb.web import stdmsgs, action, component, form
    22 from cubicweb.schema import display_name
       
    23 from cubicweb.web import stdmsgs, action, component, form, action
    23 from cubicweb.web import formfields as ff, formwidgets as fwdgs
    24 from cubicweb.web import formfields as ff, formwidgets as fwdgs
    24 from cubicweb.web.views import TmpFileViewMixin, forms
    25 from cubicweb.web.views import TmpFileViewMixin, forms, primary
    25 
    26 
    26 
    27 
    27 # IWorkflowable views #########################################################
    28 # IWorkflowable views #########################################################
    28 
    29 
    29 class ChangeStateForm(forms.CompositeEntityForm):
    30 class ChangeStateForm(forms.CompositeEntityForm):
   107     title = _('Workflow history')
   108     title = _('Workflow history')
   108 
   109 
   109     def cell_call(self, row, col, view=None):
   110     def cell_call(self, row, col, view=None):
   110         self.wview('wfhistory', self.rset, row=row, col=col, view=view)
   111         self.wview('wfhistory', self.rset, row=row, col=col, view=view)
   111 
   112 
   112 # workflow entity types views #################################################
   113 
       
   114 # workflow actions #############################################################
       
   115 
       
   116 class WorkflowActions(action.Action):
       
   117     """fill 'workflow' sub-menu of the actions box"""
       
   118     id = 'workflow'
       
   119     __select__ = (action.Action.__select__ & one_line_rset() &
       
   120                   relation_possible('in_state'))
       
   121 
       
   122     submenu = _('workflow')
       
   123     order = 10
       
   124 
       
   125     def fill_menu(self, box, menu):
       
   126         entity = self.rset.get_entity(self.row or 0, self.col or 0)
       
   127         menu.label = u'%s: %s' % (self.req._('state'), entity.printable_state)
       
   128         menu.append_anyway = True
       
   129         super(WorkflowActions, self).fill_menu(box, menu)
       
   130 
       
   131     def actual_actions(self):
       
   132         entity = self.rset.get_entity(self.row or 0, self.col or 0)
       
   133         hastr = False
       
   134         for tr in entity.possible_transitions():
       
   135             url = entity.absolute_url(vid='statuschange', treid=tr.eid)
       
   136             yield self.build_action(self.req._(tr.name), url)
       
   137             hastr = True
       
   138         # don't propose to see wf if user can't pass any transition
       
   139         if hastr:
       
   140             wfurl = entity.current_workflow.absolute_url()
       
   141             yield self.build_action(self.req._('view workflow'), wfurl)
       
   142         if entity.workflow_history:
       
   143             wfurl = entity.absolute_url(vid='wfhistory')
       
   144             yield self.build_action(self.req._('view history'), wfurl)
       
   145 
       
   146 
       
   147 # workflow entity types views ##################################################
   113 
   148 
   114 class CellView(view.EntityView):
   149 class CellView(view.EntityView):
   115     id = 'cell'
   150     id = 'cell'
   116     __select__ = implements('TrInfo')
   151     __select__ = implements('TrInfo')
   117 
   152 
   127     def cell_call(self, row, col):
   162     def cell_call(self, row, col):
   128         self.w(xml_escape(self.view('textincontext', self.rset,
   163         self.w(xml_escape(self.view('textincontext', self.rset,
   129                                      row=row, col=col)))
   164                                      row=row, col=col)))
   130 
   165 
   131 
   166 
   132 # workflow images #############################################################
   167 class WorkflowPrimaryView(primary.PrimaryView):
   133 
   168     __select__ = implements('Workflow')
   134 class ViewWorkflowAction(action.Action):
   169 
   135     id = 'workflow'
   170     def render_entity_attributes(self, entity):
   136     __select__ = implements('CWEType') & has_related_entities('workflow_of', 'object')
   171         self.w(entity.view('reledit', rtype='description'))
   137 
       
   138     category = 'mainactions'
       
   139     title = _('view workflow')
       
   140     def url(self):
       
   141         entity = self.rset.get_entity(self.row or 0, self.col or 0)
       
   142         return entity.absolute_url(vid='workflow')
       
   143 
       
   144 
       
   145 class CWETypeWorkflowView(view.EntityView):
       
   146     id = 'workflow'
       
   147     __select__ = implements('CWEType')
       
   148     cache_max_age = 60*60*2 # stay in http cache for 2 hours by default
       
   149 
       
   150     def cell_call(self, row, col, **kwargs):
       
   151         entity = self.rset.get_entity(row, col)
       
   152         self.w(u'<h1>%s</h1>' % (self.req._('workflow for %s')
       
   153                                  % display_name(self.req, entity.name)))
       
   154         self.w(u'<img src="%s" alt="%s"/>' % (
   172         self.w(u'<img src="%s" alt="%s"/>' % (
   155             xml_escape(entity.absolute_url(vid='ewfgraph')),
   173             xml_escape(entity.absolute_url(vid='wfgraph')),
   156             xml_escape(self.req._('graphical workflow for %s') % entity.name)))
   174             xml_escape(self.req._('graphical workflow for %s') % entity.name)))
   157 
   175 
       
   176 
       
   177 # workflow images ##############################################################
   158 
   178 
   159 class WorkflowDotPropsHandler(object):
   179 class WorkflowDotPropsHandler(object):
   160     def __init__(self, req):
   180     def __init__(self, req):
   161         self._ = req._
   181         self._ = req._
   162 
   182 
   176             if tr.require_group:
   196             if tr.require_group:
   177                 descr.append('%s %s'% (
   197                 descr.append('%s %s'% (
   178                     self._('groups:'),
   198                     self._('groups:'),
   179                     ','.join(g.name for g in tr.require_group)))
   199                     ','.join(g.name for g in tr.require_group)))
   180             if tr.condition:
   200             if tr.condition:
   181                 descr.append('%s %s'% (self._('condition:'), tr.condition))
   201                 descr.append('%s %s'% (
       
   202                     self._('condition:'),
       
   203                     ' | '.join(e.expression for e in tr.condition)))
   182             if descr:
   204             if descr:
   183                 props['label'] += escape('\n'.join(descr))
   205                 props['label'] += escape('\n'.join(descr))
   184         return props
   206         return props
   185 
   207 
   186     def edge_properties(self, transition, fromstate, tostate):
   208     def edge_properties(self, transition, fromstate, tostate):
   206             for incomingstate in transition.reverse_allowed_transition:
   228             for incomingstate in transition.reverse_allowed_transition:
   207                 yield incomingstate.eid, transition.eid, transition
   229                 yield incomingstate.eid, transition.eid, transition
   208             yield transition.eid, transition.destination().eid, transition
   230             yield transition.eid, transition.destination().eid, transition
   209 
   231 
   210 
   232 
   211 class CWETypeWorkflowImageView(TmpFileViewMixin, view.EntityView):
   233 class WorkflowImageView(TmpFileViewMixin, view.EntityView):
   212     id = 'ewfgraph'
   234     id = 'wfgraph'
   213     content_type = 'image/png'
   235     content_type = 'image/png'
   214     __select__ = implements('CWEType')
   236     __select__ = implements('Workflow')
   215 
   237 
   216     def _generate(self, tmpfile):
   238     def _generate(self, tmpfile):
   217         """display schema information for an entity"""
   239         """display schema information for an entity"""
   218         entity = self.rset.get_entity(self.row, self.col)
   240         entity = self.rset.get_entity(self.row, self.col)
   219         visitor = WorkflowVisitor(entity)
   241         visitor = WorkflowVisitor(entity)