sobjects/supervising.py
changeset 2841 107ba1c45227
parent 2812 b260ed87a650
child 2968 0e3460341023
equal deleted inserted replaced
2840:06daf13195d4 2841:107ba1c45227
    11 from cubicweb import UnknownEid
    11 from cubicweb import UnknownEid
    12 from cubicweb.selectors import none_rset
    12 from cubicweb.selectors import none_rset
    13 from cubicweb.schema import display_name
    13 from cubicweb.schema import display_name
    14 from cubicweb.view import Component
    14 from cubicweb.view import Component
    15 from cubicweb.common.mail import format_mail
    15 from cubicweb.common.mail import format_mail
    16 from cubicweb.server.hooksmanager import Hook
       
    17 from cubicweb.server.hookhelper import SendMailOp
    16 from cubicweb.server.hookhelper import SendMailOp
    18 
       
    19 
       
    20 class SomethingChangedHook(Hook):
       
    21     events = ('before_add_relation', 'before_delete_relation',
       
    22               'after_add_entity', 'before_update_entity')
       
    23     accepts = ('Any',)
       
    24 
       
    25     def call(self, session, *args):
       
    26         dest = session.vreg.config['supervising-addrs']
       
    27         if not dest: # no supervisors, don't do this for nothing...
       
    28             return
       
    29         self.session = session
       
    30         if self._call(*args):
       
    31             SupervisionMailOp(session)
       
    32 
       
    33     def _call(self, *args):
       
    34         if self._event() == 'update_entity':
       
    35             if args[0].eid in self.session.transaction_data.get('neweids', ()):
       
    36                 return False
       
    37             if args[0].e_schema == 'CWUser':
       
    38                 updated = set(args[0].iterkeys())
       
    39                 if not (updated - frozenset(('eid', 'modification_date',
       
    40                                              'last_login_time'))):
       
    41                     # don't record last_login_time update which are done
       
    42                     # automatically at login time
       
    43                     return False
       
    44         self.session.transaction_data.setdefault('pendingchanges', []).append(
       
    45             (self._event(), args))
       
    46         return True
       
    47 
       
    48     def _event(self):
       
    49         return self.event.split('_', 1)[1]
       
    50 
       
    51 
       
    52 class EntityDeleteHook(SomethingChangedHook):
       
    53     events = ('before_delete_entity',)
       
    54 
       
    55     def _call(self, eid):
       
    56         entity = self.session.entity_from_eid(eid)
       
    57         try:
       
    58             title = entity.dc_title()
       
    59         except:
       
    60             # may raise an error during deletion process, for instance due to
       
    61             # missing required relation
       
    62             title = '#%s' % eid
       
    63         self.session.transaction_data.setdefault('pendingchanges', []).append(
       
    64             ('delete_entity', (eid, str(entity.e_schema), title)))
       
    65         return True
       
    66 
    17 
    67 
    18 
    68 def filter_changes(changes):
    19 def filter_changes(changes):
    69     """
    20     """
    70     * when an entity has been deleted:
    21     * when an entity has been deleted:
    77     index = {}
    28     index = {}
    78     added, deleted = set(), set()
    29     added, deleted = set(), set()
    79     for change in changes[:]:
    30     for change in changes[:]:
    80         event, changedescr = change
    31         event, changedescr = change
    81         if event == 'add_entity':
    32         if event == 'add_entity':
    82             entity = changedescr[0]
    33             entity = changedescr.entity
    83             added.add(entity.eid)
    34             added.add(entity.eid)
    84             if entity.e_schema == 'TrInfo':
    35             if entity.e_schema == 'TrInfo':
    85                 changes.remove(change)
    36                 changes.remove(change)
    86                 if entity.from_state:
    37                 if entity.from_state:
    87                     try:
    38                     try:
   109         try:
    60         try:
   110             for change in index['add_relation'].copy():
    61             for change in index['add_relation'].copy():
   111                 changedescr = change[1]
    62                 changedescr = change[1]
   112                 # skip meta-relations which are set automatically
    63                 # skip meta-relations which are set automatically
   113                 # XXX generate list below using rtags (category = 'generated')
    64                 # XXX generate list below using rtags (category = 'generated')
   114                 if changedescr[1] in ('created_by', 'owned_by', 'is', 'is_instance_of',
    65                 if changedescr.rtype in ('created_by', 'owned_by', 'is', 'is_instance_of',
   115                                       'from_state', 'to_state', 'wf_info_for',) \
    66                                       'from_state', 'to_state', 'wf_info_for',) \
   116                        and changedescr[0] == eid:
    67                        and changedescr.eidfrom == eid:
   117                     index['add_relation'].remove(change)
    68                     index['add_relation'].remove(change)
   118                 # skip in_state relation if the entity is being created
    69                 # skip in_state relation if the entity is being created
   119                 # XXX this may be automatized by skipping all mandatory relation
    70                 # XXX this may be automatized by skipping all mandatory relation
   120                 #     at entity creation time
    71                 #     at entity creation time
   121                 elif changedescr[1] == 'in_state' and changedescr[0] in added:
    72                 elif changedescr.rtype == 'in_state' and changedescr.eidfrom in added:
   122                     index['add_relation'].remove(change)
    73                     index['add_relation'].remove(change)
   123 
    74 
   124         except KeyError:
    75         except KeyError:
   125             break
    76             break
   126     for eid in deleted:
    77     for eid in deleted:
   127         try:
    78         try:
   128             for change in index['delete_relation'].copy():
    79             for change in index['delete_relation'].copy():
   129                 fromeid, rtype, toeid = change[1]
    80                 if change.eidfrom == eid:
   130                 if fromeid == eid:
       
   131                     index['delete_relation'].remove(change)
    81                     index['delete_relation'].remove(change)
   132                 elif toeid == eid:
    82                 elif change.eidto == eid:
   133                     index['delete_relation'].remove(change)
    83                     index['delete_relation'].remove(change)
   134                     if rtype == 'wf_info_for':
    84                     if change.rtype == 'wf_info_for':
   135                         for change in index['delete_entity'].copy():
    85                         for change_ in index['delete_entity'].copy():
   136                             if change[1][0] == fromeid:
    86                             if change_[1].eidfrom == change.eidfrom:
   137                                 index['delete_entity'].remove(change)
    87                                 index['delete_entity'].remove(change_)
   138         except KeyError:
    88         except KeyError:
   139             break
    89             break
   140     for change in changes:
    90     for change in changes:
   141         event, changedescr = change
    91         event, changedescr = change
   142         if change in index[event]:
    92         if change in index[event]:
   159         user = self.req.actual_session().user
   109         user = self.req.actual_session().user
   160         self.w(self.req._('user %s has made the following change(s):\n\n')
   110         self.w(self.req._('user %s has made the following change(s):\n\n')
   161                % user.login)
   111                % user.login)
   162         for event, changedescr in filter_changes(changes):
   112         for event, changedescr in filter_changes(changes):
   163             self.w(u'* ')
   113             self.w(u'* ')
   164             getattr(self, event)(*changedescr)
   114             getattr(self, event)(changedescr)
   165             self.w(u'\n\n')
   115             self.w(u'\n\n')
   166 
   116 
   167     def _entity_context(self, entity):
   117     def _entity_context(self, entity):
   168         return {'eid': entity.eid,
   118         return {'eid': entity.eid,
   169                 'etype': entity.dc_type().lower(),
   119                 'etype': entity.dc_type().lower(),
   170                 'title': entity.dc_title()}
   120                 'title': entity.dc_title()}
   171 
   121 
   172     def add_entity(self, entity):
   122     def add_entity(self, changedescr):
   173         msg = self.req._('added %(etype)s #%(eid)s (%(title)s)')
   123         msg = self.req._('added %(etype)s #%(eid)s (%(title)s)')
   174         self.w(u'%s\n' % (msg % self._entity_context(entity)))
   124         self.w(u'%s\n' % (msg % self._entity_context(changedescr.entity)))
   175         self.w(u'  %s' % entity.absolute_url())
   125         self.w(u'  %s' % changedescr.entity.absolute_url())
   176 
   126 
   177     def update_entity(self, entity):
   127     def update_entity(self, changedescr):
   178         msg = self.req._('updated %(etype)s #%(eid)s (%(title)s)')
   128         msg = self.req._('updated %(etype)s #%(eid)s (%(title)s)')
   179         self.w(u'%s\n' % (msg % self._entity_context(entity)))
   129         self.w(u'%s\n' % (msg % self._entity_context(changedescr.entity)))
   180         # XXX print changes
   130         # XXX print changes
   181         self.w(u'  %s' % entity.absolute_url())
   131         self.w(u'  %s' % changedescr.entity.absolute_url())
   182 
   132 
   183     def delete_entity(self, eid, etype, title):
   133     def delete_entity(self, (eid, etype, title)):
   184         msg = self.req._('deleted %(etype)s #%(eid)s (%(title)s)')
   134         msg = self.req._('deleted %(etype)s #%(eid)s (%(title)s)')
   185         etype = display_name(self.req, etype).lower()
   135         etype = display_name(self.req, etype).lower()
   186         self.w(msg % locals())
   136         self.w(msg % locals())
   187 
   137 
   188     def change_state(self, entity, fromstate, tostate):
   138     def change_state(self, (entity, fromstate, tostate)):
   189         msg = self.req._('changed state of %(etype)s #%(eid)s (%(title)s)')
   139         msg = self.req._('changed state of %(etype)s #%(eid)s (%(title)s)')
   190         self.w(u'%s\n' % (msg % self._entity_context(entity)))
   140         self.w(u'%s\n' % (msg % self._entity_context(entity)))
   191         self.w(_('  from state %(fromstate)s to state %(tostate)s\n' %
   141         self.w(_('  from state %(fromstate)s to state %(tostate)s\n' %
   192                  {'fromstate': _(fromstate.name), 'tostate': _(tostate.name)}))
   142                  {'fromstate': _(fromstate.name), 'tostate': _(tostate.name)}))
   193         self.w(u'  %s' % entity.absolute_url())
   143         self.w(u'  %s' % entity.absolute_url())
   194 
   144 
   195     def _relation_context(self, fromeid, rtype, toeid):
   145     def _relation_context(self, changedescr):
   196         _ = self.req._
   146         _ = self.req._
   197         session = self.req.actual_session()
   147         session = self.req.actual_session()
   198         def describe(eid):
   148         def describe(eid):
   199             try:
   149             try:
   200                 return _(session.describe(eid)[0]).lower()
   150                 return _(session.describe(eid)[0]).lower()
   201             except UnknownEid:
   151             except UnknownEid:
   202                 # may occurs when an entity has been deleted from an external
   152                 # may occurs when an entity has been deleted from an external
   203                 # source and we're cleaning its relation
   153                 # source and we're cleaning its relation
   204                 return _('unknown external entity')
   154                 return _('unknown external entity')
       
   155         eidfrom, rtype, eidto = changedescr.eidfrom, changedescr.rtype, changedescr.eidto
   205         return {'rtype': _(rtype),
   156         return {'rtype': _(rtype),
   206                 'fromeid': fromeid,
   157                 'eidfrom': eidfrom,
   207                 'frometype': describe(fromeid),
   158                 'frometype': describe(eidfrom),
   208                 'toeid': toeid,
   159                 'eidto': eidto,
   209                 'toetype': describe(toeid)}
   160                 'toetype': describe(eidto)}
   210 
   161 
   211     def add_relation(self, fromeid, rtype, toeid):
   162     def add_relation(self, changedescr):
   212         msg = self.req._('added relation %(rtype)s from %(frometype)s #%(fromeid)s to %(toetype)s #%(toeid)s')
   163         msg = self.req._('added relation %(rtype)s from %(frometype)s #%(eidfrom)s to %(toetype)s #%(eidto)s')
   213         self.w(msg % self._relation_context(fromeid, rtype, toeid))
   164         self.w(msg % self._relation_context(changedescr))
   214 
   165 
   215     def delete_relation(self, fromeid, rtype, toeid):
   166     def delete_relation(self, eidfrom, rtype, eidto):
   216         msg = self.req._('deleted relation %(rtype)s from %(frometype)s #%(fromeid)s to %(toetype)s #%(toeid)s')
   167         msg = self.req._('deleted relation %(rtype)s from %(frometype)s #%(eidfrom)s to %(toetype)s #%(eidto)s')
   217         self.w(msg % self._relation_context(fromeid, rtype, toeid))
   168         self.w(msg % self._relation_context(changedescr))
   218 
   169 
   219 
   170 
   220 class SupervisionMailOp(SendMailOp):
   171 class SupervisionMailOp(SendMailOp):
   221     """special send email operation which should be done only once for a bunch
   172     """special send email operation which should be done only once for a bunch
   222     of changes
   173     of changes