21 from cubicweb.web.formfields import StringField, RichTextField |
21 from cubicweb.web.formfields import StringField, RichTextField |
22 from cubicweb.web.formwidgets import HiddenInput, SubmitButton, Button |
22 from cubicweb.web.formwidgets import HiddenInput, SubmitButton, Button |
23 from cubicweb.web.views import TmpFileViewMixin |
23 from cubicweb.web.views import TmpFileViewMixin |
24 from cubicweb.web.views.boxes import EditBox |
24 from cubicweb.web.views.boxes import EditBox |
25 |
25 |
|
26 _ = unicode |
26 |
27 |
27 EditBox.rmode.set_rtag('create', 'destination_state', 'subject', 'Transition') |
28 EditBox.rmode.set_rtag('create', 'destination_state', 'subject', 'Transition') |
28 EditBox.rmode.set_rtag('create', 'allowed_transition', 'object', 'Transition') |
29 EditBox.rmode.set_rtag('create', 'allowed_transition', 'object', 'Transition') |
29 EditBox.rmode.set_rtag('create', 'destination_state', 'object', 'State') |
30 EditBox.rmode.set_rtag('create', 'destination_state', 'object', 'State') |
30 EditBox.rmode.set_rtag('create', 'allowed_transition', 'subject', 'State') |
31 EditBox.rmode.set_rtag('create', 'allowed_transition', 'subject', 'State') |
32 |
33 |
33 # IWorkflowable views ######################################################### |
34 # IWorkflowable views ######################################################### |
34 |
35 |
35 class ChangeStateForm(form.EntityFieldsForm): |
36 class ChangeStateForm(form.EntityFieldsForm): |
36 id = 'changestate' |
37 id = 'changestate' |
37 |
38 |
38 __method = StringField(name='__method', initial='set_state', |
39 __method = StringField(name='__method', initial='set_state', |
39 widget=HiddenInput) |
40 widget=HiddenInput) |
40 state = StringField(widget=HiddenInput, eidparam=True) |
41 state = StringField(label=_('state'), eidparam=True, widget=HiddenInput) |
41 trcomment = RichTextField(eidparam=True) |
42 trcomment = RichTextField(label=_('trcomment'), eidparam=True) |
42 form_buttons = [SubmitButton(stdmsgs.YES), |
43 form_buttons = [SubmitButton(stdmsgs.YES), |
43 Button(stdmsgs.NO, cwaction='cancel')] |
44 Button(stdmsgs.NO, cwaction='cancel')] |
44 |
45 |
45 |
46 |
46 class ChangeStateFormView(FormViewMixIn, view.EntityView): |
47 class ChangeStateFormView(FormViewMixIn, view.EntityView): |
47 id = 'statuschange' |
48 id = 'statuschange' |
48 title = _('status change') |
49 title = _('status change') |
49 __select__ = implements(IWorkflowable) & match_form_params('treid') |
50 __select__ = implements(IWorkflowable) & match_form_params('treid') |
50 |
51 |
89 if self.vreg.schema.eschema('CWUser').has_perm(self.req, 'read'): |
90 if self.vreg.schema.eschema('CWUser').has_perm(self.req, 'read'): |
90 sel += ',U,C' |
91 sel += ',U,C' |
91 rql += ', WF owned_by U?' |
92 rql += ', WF owned_by U?' |
92 displaycols = range(5) |
93 displaycols = range(5) |
93 headers = (_('from_state'), _('to_state'), _('comment'), _('date'), |
94 headers = (_('from_state'), _('to_state'), _('comment'), _('date'), |
94 _('CWUser')) |
95 _('CWUser')) |
95 else: |
96 else: |
96 sel += ',C' |
97 sel += ',C' |
97 displaycols = range(4) |
98 displaycols = range(4) |
98 headers = (_('from_state'), _('to_state'), _('comment'), _('date')) |
99 headers = (_('from_state'), _('to_state'), _('comment'), _('date')) |
99 rql = '%s %s, X eid %%(x)s' % (sel, rql) |
100 rql = '%s %s, X eid %%(x)s' % (sel, rql) |
109 # workflow entity types views ################################################# |
110 # workflow entity types views ################################################# |
110 |
111 |
111 class CellView(view.EntityView): |
112 class CellView(view.EntityView): |
112 id = 'cell' |
113 id = 'cell' |
113 __select__ = implements('TrInfo') |
114 __select__ = implements('TrInfo') |
114 |
115 |
115 def cell_call(self, row, col, cellvid=None): |
116 def cell_call(self, row, col, cellvid=None): |
116 self.w(self.entity(row, col).printable_value('comment')) |
117 self.w(self.entity(row, col).printable_value('comment')) |
117 |
118 |
118 |
119 |
119 class StateInContextView(view.EntityView): |
120 class StateInContextView(view.EntityView): |
120 """convenience trick, State's incontext view should not be clickable""" |
121 """convenience trick, State's incontext view should not be clickable""" |
121 id = 'incontext' |
122 id = 'incontext' |
122 __select__ = implements('State') |
123 __select__ = implements('State') |
123 |
124 |
124 def cell_call(self, row, col): |
125 def cell_call(self, row, col): |
125 self.w(html_escape(self.view('textincontext', self.rset, |
126 self.w(html_escape(self.view('textincontext', self.rset, |
126 row=row, col=col))) |
127 row=row, col=col))) |
127 |
128 |
128 |
129 |
129 # workflow images ############################################################# |
130 # workflow images ############################################################# |
130 |
131 |
131 class ViewWorkflowAction(action.Action): |
132 class ViewWorkflowAction(action.Action): |
132 id = 'workflow' |
133 id = 'workflow' |
133 __select__ = implements('CWEType') & has_related_entities('state_of', 'object') |
134 __select__ = implements('CWEType') & has_related_entities('state_of', 'object') |
134 |
135 |
135 category = 'mainactions' |
136 category = 'mainactions' |
136 title = _('view workflow') |
137 title = _('view workflow') |
137 def url(self): |
138 def url(self): |
138 entity = self.rset.get_entity(self.row or 0, self.col or 0) |
139 entity = self.rset.get_entity(self.row or 0, self.col or 0) |
139 return entity.absolute_url(vid='workflow') |
140 return entity.absolute_url(vid='workflow') |
140 |
141 |
141 |
142 |
142 class CWETypeWorkflowView(view.EntityView): |
143 class CWETypeWorkflowView(view.EntityView): |
143 id = 'workflow' |
144 id = 'workflow' |
144 __select__ = implements('CWEType') |
145 __select__ = implements('CWEType') |
145 cache_max_age = 60*60*2 # stay in http cache for 2 hours by default |
146 cache_max_age = 60*60*2 # stay in http cache for 2 hours by default |
146 |
147 |
147 def cell_call(self, row, col, **kwargs): |
148 def cell_call(self, row, col, **kwargs): |
148 entity = self.entity(row, col) |
149 entity = self.entity(row, col) |
149 self.w(u'<h1>%s</h1>' % (self.req._('workflow for %s') |
150 self.w(u'<h1>%s</h1>' % (self.req._('workflow for %s') |
150 % display_name(self.req, entity.name))) |
151 % display_name(self.req, entity.name))) |
151 self.w(u'<img src="%s" alt="%s"/>' % ( |
152 self.w(u'<img src="%s" alt="%s"/>' % ( |
154 |
155 |
155 |
156 |
156 class WorkflowDotPropsHandler(object): |
157 class WorkflowDotPropsHandler(object): |
157 def __init__(self, req): |
158 def __init__(self, req): |
158 self._ = req._ |
159 self._ = req._ |
159 |
160 |
160 def node_properties(self, stateortransition): |
161 def node_properties(self, stateortransition): |
161 """return default DOT drawing options for a state or transition""" |
162 """return default DOT drawing options for a state or transition""" |
162 props = {'label': stateortransition.name, |
163 props = {'label': stateortransition.name, |
163 'fontname': 'Courier'} |
164 'fontname': 'Courier'} |
164 if hasattr(stateortransition, 'state_of'): |
165 if hasattr(stateortransition, 'state_of'): |
165 props['shape'] = 'box' |
166 props['shape'] = 'box' |
166 props['style'] = 'filled' |
167 props['style'] = 'filled' |
167 if stateortransition.reverse_initial_state: |
168 if stateortransition.reverse_initial_state: |
177 if tr.condition: |
178 if tr.condition: |
178 descr.append('%s %s'% (self._('condition:'), tr.condition)) |
179 descr.append('%s %s'% (self._('condition:'), tr.condition)) |
179 if descr: |
180 if descr: |
180 props['label'] += escape('\n'.join(descr)) |
181 props['label'] += escape('\n'.join(descr)) |
181 return props |
182 return props |
182 |
183 |
183 def edge_properties(self, transition, fromstate, tostate): |
184 def edge_properties(self, transition, fromstate, tostate): |
184 return {'label': '', 'dir': 'forward', |
185 return {'label': '', 'dir': 'forward', |
185 'color': 'black', 'style': 'filled'} |
186 'color': 'black', 'style': 'filled'} |
186 |
187 |
187 |
188 |
191 |
192 |
192 def nodes(self): |
193 def nodes(self): |
193 for state in self.entity.reverse_state_of: |
194 for state in self.entity.reverse_state_of: |
194 state.complete() |
195 state.complete() |
195 yield state.eid, state |
196 yield state.eid, state |
196 |
197 |
197 for transition in self.entity.reverse_transition_of: |
198 for transition in self.entity.reverse_transition_of: |
198 transition.complete() |
199 transition.complete() |
199 yield transition.eid, transition |
200 yield transition.eid, transition |
200 |
201 |
201 def edges(self): |
202 def edges(self): |
202 for transition in self.entity.reverse_transition_of: |
203 for transition in self.entity.reverse_transition_of: |
203 for incomingstate in transition.reverse_allowed_transition: |
204 for incomingstate in transition.reverse_allowed_transition: |
204 yield incomingstate.eid, transition.eid, transition |
205 yield incomingstate.eid, transition.eid, transition |
205 yield transition.eid, transition.destination().eid, transition |
206 yield transition.eid, transition.destination().eid, transition |
207 |
208 |
208 class CWETypeWorkflowImageView(TmpFileViewMixin, view.EntityView): |
209 class CWETypeWorkflowImageView(TmpFileViewMixin, view.EntityView): |
209 id = 'ewfgraph' |
210 id = 'ewfgraph' |
210 content_type = 'image/png' |
211 content_type = 'image/png' |
211 __select__ = implements('CWEType') |
212 __select__ = implements('CWEType') |
212 |
213 |
213 def _generate(self, tmpfile): |
214 def _generate(self, tmpfile): |
214 """display schema information for an entity""" |
215 """display schema information for an entity""" |
215 entity = self.entity(self.row, self.col) |
216 entity = self.entity(self.row, self.col) |
216 visitor = WorkflowVisitor(entity) |
217 visitor = WorkflowVisitor(entity) |
217 prophdlr = WorkflowDotPropsHandler(self.req) |
218 prophdlr = WorkflowDotPropsHandler(self.req) |