# HG changeset patch # User Sylvain Thénault # Date 1271157670 -7200 # Node ID 34e669b6fd9539286ff672dc9224c058887354d9 # Parent 6abd6e3599f46b869b92bbf42ca740b16a92fc4e [mq]: worklfow view diff -r 6abd6e3599f4 -r 34e669b6fd95 entities/schemaobjs.py --- a/entities/schemaobjs.py Tue Apr 13 12:19:24 2010 +0200 +++ b/entities/schemaobjs.py Tue Apr 13 13:21:10 2010 +0200 @@ -162,6 +162,9 @@ fetch_attrs, fetch_order = fetch_config(['exprtype', 'mainvars', 'expression']) def dc_title(self): + return self.expression or u'' + + def dc_long_title(self): return '%s(%s)' % (self.exprtype, self.expression or u'') @property diff -r 6abd6e3599f4 -r 34e669b6fd95 entities/wfobjs.py --- a/entities/wfobjs.py Tue Apr 13 12:19:24 2010 +0200 +++ b/entities/wfobjs.py Tue Apr 13 13:21:10 2010 +0200 @@ -174,7 +174,7 @@ fired by the logged user """ __regid__ = 'BaseTransition' - fetch_attrs, fetch_order = fetch_config(['name']) + fetch_attrs, fetch_order = fetch_config(['name', 'type']) def __init__(self, *args, **kwargs): if self.__regid__ == 'BaseTransition': diff -r 6abd6e3599f4 -r 34e669b6fd95 i18n/en.po --- a/i18n/en.po Tue Apr 13 12:19:24 2010 +0200 +++ b/i18n/en.po Tue Apr 13 13:21:10 2010 +0200 @@ -30,6 +30,9 @@ msgid " from state %(fromstate)s to state %(tostate)s\n" msgstr "" +msgid " :" +msgstr "" + #, python-format msgid "%(attr)s set to %(newvalue)s" msgstr "" @@ -603,6 +606,9 @@ msgid "The view %s could not be found" msgstr "" +msgid "There is no default workflow" +msgstr "" + msgid "This BaseTransition" msgstr "This abstract transition" @@ -1514,9 +1520,6 @@ msgid "condition" msgstr "condition" -msgid "condition:" -msgstr "condtion:" - msgctxt "RQLExpression" msgid "condition_object" msgstr "condition of" @@ -1524,6 +1527,9 @@ msgid "condition_object" msgstr "condition of" +msgid "conditions" +msgstr "" + msgid "config mode" msgstr "" @@ -2385,10 +2391,6 @@ msgid "granted to groups" msgstr "" -#, python-format -msgid "graphical representation of %s" -msgstr "" - msgid "graphical representation of the instance'schema" msgstr "" @@ -2412,9 +2414,6 @@ msgid "groups to which the permission is granted" msgstr "" -msgid "groups:" -msgstr "" - msgid "guests" msgstr "" @@ -2790,6 +2789,9 @@ msgid "more actions" msgstr "" +msgid "more info about this workflow" +msgstr "" + msgid "multiple edit" msgstr "" @@ -3013,6 +3015,9 @@ msgid "permission" msgstr "" +msgid "permissions" +msgstr "" + msgid "permissions for entities" msgstr "" @@ -3931,6 +3936,12 @@ msgid "wf_info_for_object" msgstr "workflow history" +msgid "wf_tab_info" +msgstr "" + +msgid "wfgraph" +msgstr "" + msgid "" "when multiple addresses are equivalent (such as python-projects@logilab.org " "and python-projects@lists.logilab.org), set this to indicate which is the " diff -r 6abd6e3599f4 -r 34e669b6fd95 i18n/es.po --- a/i18n/es.po Tue Apr 13 12:19:24 2010 +0200 +++ b/i18n/es.po Tue Apr 13 13:21:10 2010 +0200 @@ -35,6 +35,9 @@ msgid " from state %(fromstate)s to state %(tostate)s\n" msgstr " del estado %(fromstate)s hacia el estado %(tostate)s\n" +msgid " :" +msgstr "" + #, python-format msgid "%(attr)s set to %(newvalue)s" msgstr "" @@ -611,6 +614,9 @@ msgid "The view %s could not be found" msgstr "La vista %s no ha podido ser encontrada" +msgid "There is no default workflow" +msgstr "" + msgid "This BaseTransition" msgstr "" @@ -1545,9 +1551,6 @@ msgid "condition" msgstr "" -msgid "condition:" -msgstr "condición:" - msgctxt "RQLExpression" msgid "condition_object" msgstr "" @@ -1555,6 +1558,9 @@ msgid "condition_object" msgstr "condición de" +msgid "conditions" +msgstr "" + msgid "config mode" msgstr "" @@ -2435,10 +2441,6 @@ msgid "granted to groups" msgstr "Otorgado a los grupos" -#, python-format -msgid "graphical representation of %s" -msgstr "" - msgid "graphical representation of the instance'schema" msgstr "" @@ -2462,9 +2464,6 @@ msgid "groups to which the permission is granted" msgstr "Grupos quienes tienen otorgada esta autorización" -msgid "groups:" -msgstr "Grupos :" - msgid "guests" msgstr "Invitados" @@ -2858,6 +2857,9 @@ msgid "more actions" msgstr "mas acciones" +msgid "more info about this workflow" +msgstr "" + msgid "multiple edit" msgstr "Edicion multiple" @@ -3086,6 +3088,9 @@ msgid "permission" msgstr "Permiso" +msgid "permissions" +msgstr "" + msgid "permissions for entities" msgstr "autorizaciónes para entidades" @@ -4018,6 +4023,12 @@ msgid "wf_info_for_object" msgstr "historial de transiciones" +msgid "wf_tab_info" +msgstr "" + +msgid "wfgraph" +msgstr "" + msgid "" "when multiple addresses are equivalent (such as python-projects@logilab.org " "and python-projects@lists.logilab.org), set this to indicate which is the " @@ -4219,6 +4230,9 @@ #~ msgid "comment:" #~ msgstr "Comentario:" +#~ msgid "condition:" +#~ msgstr "condición:" + #~ msgid "copy edition" #~ msgstr "Edición de una copia" @@ -4314,6 +4328,9 @@ #~ msgid "groups allowed to update entities of this type" #~ msgstr "Grupos autorizados a actualizar entidades de este tipo" +#~ msgid "groups:" +#~ msgstr "Grupos :" + #~ msgid "home" #~ msgstr "Inicio" diff -r 6abd6e3599f4 -r 34e669b6fd95 i18n/fr.po --- a/i18n/fr.po Tue Apr 13 12:19:24 2010 +0200 +++ b/i18n/fr.po Tue Apr 13 13:21:10 2010 +0200 @@ -35,6 +35,9 @@ msgid " from state %(fromstate)s to state %(tostate)s\n" msgstr " de l'état %(fromstate)s vers l'état %(tostate)s\n" +msgid " :" +msgstr "" + #, python-format msgid "%(attr)s set to %(newvalue)s" msgstr "%(attr)s modifié à %(newvalue)s" @@ -622,6 +625,9 @@ msgid "The view %s could not be found" msgstr "La vue %s est introuvable" +msgid "There is no default workflow" +msgstr "Ce type d'entité n'a pas de workflow par défault" + msgid "This BaseTransition" msgstr "Cette transition abstraite" @@ -1565,9 +1571,6 @@ msgid "condition" msgstr "condition" -msgid "condition:" -msgstr "condition :" - msgctxt "RQLExpression" msgid "condition_object" msgstr "condition de" @@ -1575,6 +1578,9 @@ msgid "condition_object" msgstr "condition de" +msgid "conditions" +msgstr "conditions" + msgid "config mode" msgstr "mode de configuration" @@ -2374,7 +2380,7 @@ msgstr "suivez ce lien pour plus d'information sur ce %s" msgid "follow this link if javascript is deactivated" -msgstr "" +msgstr "suivez ce lien si javascript est désactivé" msgid "for_user" msgstr "pour l'utilisateur" @@ -2471,10 +2477,6 @@ msgid "granted to groups" msgstr "accordée aux groupes" -#, python-format -msgid "graphical representation of %s" -msgstr "représentation graphique de %s" - msgid "graphical representation of the instance'schema" msgstr "représentation graphique du schéma de l'instance" @@ -2499,9 +2501,6 @@ msgid "groups to which the permission is granted" msgstr "groupes auquels cette permission est donnée" -msgid "groups:" -msgstr "groupes :" - msgid "guests" msgstr "invités" @@ -2896,6 +2895,9 @@ msgid "more actions" msgstr "plus d'actions" +msgid "more info about this workflow" +msgstr "plus d'information sur ce workflow" + msgid "multiple edit" msgstr "édition multiple" @@ -3122,6 +3124,9 @@ msgid "permission" msgstr "permission" +msgid "permissions" +msgstr "permissions" + msgid "permissions for entities" msgstr "permissions pour les entités" @@ -4062,6 +4067,12 @@ msgid "wf_info_for_object" msgstr "historique des transitions" +msgid "wf_tab_info" +msgstr "description" + +msgid "wfgraph" +msgstr "image du workflow" + msgid "" "when multiple addresses are equivalent (such as python-projects@logilab.org " "and python-projects@lists.logilab.org), set this to indicate which is the " diff -r 6abd6e3599f4 -r 34e669b6fd95 web/data/cubicweb.css --- a/web/data/cubicweb.css Tue Apr 13 12:19:24 2010 +0200 +++ b/web/data/cubicweb.css Tue Apr 13 13:21:10 2010 +0200 @@ -63,7 +63,7 @@ text-decoration: underline; } -a img { +a img, img { border: none; text-align: center; } diff -r 6abd6e3599f4 -r 34e669b6fd95 web/views/cwuser.py --- a/web/views/cwuser.py Tue Apr 13 12:19:24 2010 +0200 +++ b/web/views/cwuser.py Tue Apr 13 13:21:10 2010 +0200 @@ -71,3 +71,12 @@ if emailaddr: self.w(u'%s\n' % xml_escape(emailaddr)) self.w(u'\n') + +class CWGroupInContextView(EntityView): + __regid__ = 'incontext' + __select__ = implements('CWGroup') + def cell_call(self, row, col): + self._cw.add_css('cubicweb.acl.css') + entity = self.cw_rset.complete_entity(row, col) + self.w(u'%s' % ( + entity.absolute_url(), entity.name, entity.printable_value('name'))) diff -r 6abd6e3599f4 -r 34e669b6fd95 web/views/schema.py --- a/web/views/schema.py Tue Apr 13 12:19:24 2010 +0200 +++ b/web/views/schema.py Tue Apr 13 13:21:10 2010 +0200 @@ -313,18 +313,25 @@ entity = self.cw_rset.get_entity(row, col) if entity.default_workflow: wf = entity.default_workflow[0] - self.w(u'

%s (%s)

' % (wf.name, self._cw._('default'))) - self.wf_image(wf) + if len(entity.reverse_workflow_of) > 1: + self.w(u'

%s (%s)

' + % (wf.name, self._cw._('default_workflow'))) + self.display_workflow(wf) + defaultwfeid = wf.eid + else: + self.w(u'
%s
' + % self._cw._('There is no default workflow')) + defaultwfeid = None for altwf in entity.reverse_workflow_of: - if altwf.eid == wf.eid: + if altwf.eid == defaultwfeid: continue self.w(u'

%s

' % altwf.name) - self.wf_image(altwf) + self.display_workflow(altwf) - def wf_image(self, wf): - self.w(u'%s' % ( - xml_escape(wf.absolute_url(vid='wfgraph')), - xml_escape(self._cw._('graphical representation of %s') % wf.name))) + def display_workflow(self, wf): + self.w(wf.view('wfgraph')) + self.w('%s' % ( + wf.absolute_url(), self._cw._('more info about this workflow'))) # CWRType ###################################################################### diff -r 6abd6e3599f4 -r 34e669b6fd95 web/views/workflow.py --- a/web/views/workflow.py Tue Apr 13 12:19:24 2010 +0200 +++ b/web/views/workflow.py Tue Apr 13 13:21:10 2010 +0200 @@ -11,6 +11,9 @@ __docformat__ = "restructuredtext en" _ = unicode +import tempfile +import os + from logilab.mtconverter import xml_escape from logilab.common.graph import escape, GraphGenerator, DotBackend @@ -24,6 +27,7 @@ from cubicweb.web import uicfg, stdmsgs, action, component, form, action from cubicweb.web import formfields as ff, formwidgets as fwdgs from cubicweb.web.views import TmpFileViewMixin, forms, primary, autoform +from cubicweb.web.views.tabs import TabbedPrimaryView, PrimaryTab _pvs = uicfg.primaryview_section _pvs.tag_subject_of(('Workflow', 'initial_state', '*'), 'hidden') @@ -187,6 +191,7 @@ _pvs.tag_subject_of(('Workflow', 'initial_state', '*'), 'hidden') _pvs.tag_object_of(('*', 'state_of', 'Workflow'), 'hidden') _pvs.tag_object_of(('*', 'transition_of', 'Workflow'), 'hidden') +_pvs.tag_object_of(('*', 'default_workflow', 'Workflow'), 'hidden') _abaa = uicfg.actionbox_appearsin_addmenu _abaa.tag_subject_of(('BaseTransition', 'condition', 'RQLExpression'), False) @@ -198,14 +203,10 @@ _abaa.tag_object_of(('Transition', 'transition_of', 'Workflow'), True) _abaa.tag_object_of(('WorkflowTransition', 'transition_of', 'Workflow'), True) -class WorkflowPrimaryView(primary.PrimaryView): +class WorkflowPrimaryView(TabbedPrimaryView): __select__ = implements('Workflow') - - def render_entity_attributes(self, entity): - self.w(entity.view('reledit', rtype='description')) - self.w(u'%s' % ( - xml_escape(entity.absolute_url(vid='wfgraph')), - xml_escape(self._cw._('graphical workflow for %s') % entity.name))) + tabs = [ _('wf_tab_info'), _('wfgraph'),] + default_tab = 'wf_tab_info' class CellView(view.EntityView): @@ -225,6 +226,59 @@ self.w(xml_escape(self._cw.view('textincontext', self.cw_rset, row=row, col=col))) +class WorkflowTabTextView(PrimaryTab): + __regid__ = 'wf_tab_info' + __select__ = PrimaryTab.__select__ & one_line_rset() & implements('Workflow') + + def render_entity_attributes(self, entity): + _ = self._cw._ + self.w(u'
%s
' % (entity.printable_value('description'))) + self.w(u'%s%s' % (_("workflow_of").capitalize(), _(" :"))) + html = [] + for e in entity.workflow_of: + view = e.view('outofcontext') + if entity.eid == e.default_workflow[0].eid: + view += u' [%s]' % _('default_workflow') + html.append(view) + self.w(', '.join(v for v in html)) + self.w(u'

%s

' % _("Transition_plural")) + rset = self._cw.execute( + 'Any T,T,DS,T,TT ORDERBY TN WHERE T transition_of WF, WF eid %(x)s,' + 'T type TT, T name TN, T destination_state DS?', {'x': entity.eid}) + self.wview('editable-table', rset, 'null', + cellvids={ 1: 'trfromstates', 2: 'outofcontext', 3:'trsecurity',}, + headers = (_('Transition'), _('from_state'), + _('to_state'), _('permissions'), _('type') ), + ) + + +class TransitionSecurityTextView(view.EntityView): + __regid__ = 'trsecurity' + __select__ = implements('Transition') + + def cell_call(self, row, col): + _ = self._cw._ + entity = self.cw_rset.get_entity(self.cw_row, self.cw_col) + if entity.require_group: + self.w(u'
%s%s %s
' % + (_('groups'), _(" :"), + u', '.join((g.view('incontext') for g + in entity.require_group)))) + if entity.condition: + self.w(u'
%s%s %s
' % + ( _('conditions'), _(" :"), + u'
'.join((e.dc_title() for e + in entity.condition)))) + +class TransitionAllowedTextView(view.EntityView): + __regid__ = 'trfromstates' + __select__ = implements('Transition') + + def cell_call(self, row, col): + entity = self.cw_rset.get_entity(self.cw_row, self.cw_col) + self.w(u', '.join((e.view('outofcontext') for e + in entity.reverse_allowed_transition))) + # workflow entity types edition ################################################ @@ -284,24 +338,18 @@ def node_properties(self, stateortransition): """return default DOT drawing options for a state or transition""" props = {'label': stateortransition.printable_value('name'), - 'fontname': 'Courier'} + 'fontname': 'Courier', 'fontsize':10, + 'href': stateortransition.absolute_url(), + } if hasattr(stateortransition, 'state_of'): props['shape'] = 'box' props['style'] = 'filled' if stateortransition.reverse_initial_state: - props['color'] = '#88CC88' + props['fillcolor'] = '#88CC88' else: props['shape'] = 'ellipse' descr = [] tr = stateortransition - if tr.require_group: - descr.append('%s %s'% ( - self._('groups:'), - ','.join(g.printable_value('name') for g in tr.require_group))) - if tr.condition: - descr.append('%s %s'% ( - self._('condition:'), - ' | '.join(e.expression for e in tr.condition))) if descr: props['label'] += escape('\n'.join(descr)) return props @@ -331,17 +379,39 @@ yield transition.eid, outgoingstate.eid, transition +class WorkflowGraphView(view.EntityView): + __regid__ = 'wfgraph' + __select__ = EntityView.__select__ & one_line_rset() & implements('Workflow') + + def cell_call(self, row, col): + entity = self.cw_rset.get_entity(row, col) + visitor = WorkflowVisitor(entity) + prophdlr = WorkflowDotPropsHandler(self._cw) + wfname = 'workflow%s' % str(entity.eid) + generator = GraphGenerator(DotBackend(wfname, None, + ratio='compress', size='30,10')) + # map file + pmap, mapfile = tempfile.mkstemp(".map", wfname) + os.close(pmap) + # image file + fd, tmpfile = tempfile.mkstemp('.png') + os.close(fd) + generator.generate(visitor, prophdlr, tmpfile, mapfile) + self.w(u'%s' % ( + xml_escape(entity.absolute_url(vid='wfimage', tmpfile=tmpfile)), + xml_escape(self._cw._('graphical workflow for %s') % entity.name), + wfname)) + stream = open(mapfile, 'r').read() + stream = stream.decode(self._cw.encoding) + self.w(stream) + os.unlink(mapfile) + class WorkflowImageView(TmpFileViewMixin, view.EntityView): - __regid__ = 'wfgraph' + __regid__ = 'wfimage' __select__ = implements('Workflow') content_type = 'image/png' - def _generate(self, tmpfile): - """display schema information for an entity""" - entity = self.cw_rset.get_entity(self.cw_row, self.cw_col) - visitor = WorkflowVisitor(entity) - prophdlr = WorkflowDotPropsHandler(self._cw) - generator = GraphGenerator(DotBackend('workflow', 'LR', - ratio='compress', size='30,12')) - return generator.generate(visitor, prophdlr, tmpfile) - + def cell_call(self, row=0, col=0): + tmpfile = self._cw.form.get('tmpfile', None) + self.w(open(tmpfile, 'rb').read()) + os.unlink(tmpfile)