|
1 """Core hooks: workflow related hooks |
|
2 |
|
3 :organization: Logilab |
|
4 :copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2. |
|
5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr |
|
6 :license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses |
|
7 """ |
|
8 __docformat__ = "restructuredtext en" |
|
9 |
|
10 from cubicweb import ValidationError |
|
11 from cubicweb.interfaces import IWorkflowable |
|
12 from cubicweb.selectors import entity_implements |
|
13 from cubicweb.server.hook import Hook, match_rtype |
|
14 from cubicweb.server.pool import PreCommitOperation |
|
15 from cubicweb.server.hookhelper import previous_state |
|
16 |
|
17 |
|
18 def relation_deleted(session, eidfrom, rtype, eidto): |
|
19 session.transaction_data.setdefault('pendingrelations', []).append( |
|
20 (eidfrom, rtype, eidto)) |
|
21 |
|
22 |
|
23 class _SetInitialStateOp(PreCommitOperation): |
|
24 """make initial state be a default state""" |
|
25 |
|
26 def precommit_event(self): |
|
27 session = self.session |
|
28 entity = self.entity |
|
29 # if there is an initial state and the entity's state is not set, |
|
30 # use the initial state as a default state |
|
31 pendingeids = session.transaction_data.get('pendingeids', ()) |
|
32 if not entity.eid in pendingeids and not entity.in_state: |
|
33 rset = session.execute('Any S WHERE ET initial_state S, ET name %(name)s', |
|
34 {'name': entity.id}) |
|
35 if rset: |
|
36 session.add_relation(entity.eid, 'in_state', rset[0][0]) |
|
37 |
|
38 |
|
39 class SetInitialStateHook(Hook): |
|
40 __id__ = 'wfsetinitial' |
|
41 __select__ = Hook.__select__ & entity_implements(IWorkflowable) |
|
42 category = 'worfklow' |
|
43 events = ('after_add_entity',) |
|
44 |
|
45 def __call__(self): |
|
46 _SetInitialStateOp(self.cw_req, entity=self.entity) |
|
47 |
|
48 |
|
49 class PrepareStateChangeHook(Hook): |
|
50 """record previous state information""" |
|
51 __id__ = 'cwdelstate' |
|
52 __select__ = Hook.__select__ & match_rtype('in_state') |
|
53 category = 'worfklow' |
|
54 events = ('before_delete_relation',) |
|
55 |
|
56 def __call__(self): |
|
57 self.cw_req.transaction_data.setdefault('pendingrelations', []).append( |
|
58 (self.eidfrom, self.rtype, self.eidto)) |
|
59 |
|
60 |
|
61 class FireTransitionHook(PrepareStateChangeHook): |
|
62 """check the transition is allowed and record transition information""" |
|
63 __id__ = 'wffiretransition' |
|
64 events = ('before_add_relation',) |
|
65 |
|
66 def __call__(self): |
|
67 session = self.cw_req |
|
68 eidfrom = self.eidfrom |
|
69 eidto = self.eidto |
|
70 state = previous_state(session, eidfrom) |
|
71 etype = session.describe(eidfrom)[0] |
|
72 if not (session.is_super_session or 'managers' in session.user.groups): |
|
73 if not state is None: |
|
74 entity = session.entity_from_eid(eidfrom) |
|
75 # we should find at least one transition going to this state |
|
76 try: |
|
77 iter(state.transitions(entity, eidto)).next() |
|
78 except StopIteration: |
|
79 msg = session._('transition is not allowed') |
|
80 raise ValidationError(eidfrom, {'in_state': msg}) |
|
81 else: |
|
82 # not a transition |
|
83 # check state is initial state if the workflow defines one |
|
84 isrset = session.unsafe_execute('Any S WHERE ET initial_state S, ET name %(etype)s', |
|
85 {'etype': etype}) |
|
86 if isrset and not eidto == isrset[0][0]: |
|
87 msg = session._('not the initial state for this entity') |
|
88 raise ValidationError(eidfrom, {'in_state': msg}) |
|
89 eschema = session.repo.schema[etype] |
|
90 if not 'wf_info_for' in eschema.object_relations(): |
|
91 # workflow history not activated for this entity type |
|
92 return |
|
93 rql = 'INSERT TrInfo T: T wf_info_for E, T to_state DS, T comment %(comment)s' |
|
94 args = {'comment': session.get_shared_data('trcomment', None, pop=True), |
|
95 'e': eidfrom, 'ds': eidto} |
|
96 cformat = session.get_shared_data('trcommentformat', None, pop=True) |
|
97 if cformat is not None: |
|
98 args['comment_format'] = cformat |
|
99 rql += ', T comment_format %(comment_format)s' |
|
100 restriction = ['DS eid %(ds)s, E eid %(e)s'] |
|
101 if not state is None: # not a transition |
|
102 rql += ', T from_state FS' |
|
103 restriction.append('FS eid %(fs)s') |
|
104 args['fs'] = state.eid |
|
105 rql = '%s WHERE %s' % (rql, ', '.join(restriction)) |
|
106 session.unsafe_execute(rql, args, 'e') |