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) |