restore possibility to have default value methods on forms, this may still be useful according to how the form is defined
"""some hooks and views to handle supervising of any data changes:organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), all rights reserved.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr"""__docformat__="restructuredtext en"fromcubicwebimportUnknownEidfromcubicweb.selectorsimportnone_rsetfromcubicweb.viewimportComponentfromcubicweb.common.mailimportformat_mailfromcubicweb.server.hooksmanagerimportHookfromcubicweb.server.hookhelperimportSendMailOpclassSomethingChangedHook(Hook):events=('before_add_relation','before_delete_relation','after_add_entity','before_update_entity')accepts=('Any',)defcall(self,session,*args):dest=self.config['supervising-addrs']ifnotdest:# no supervisors, don't do this for nothing...returnself.session=sessionifself._call(*args):SupervisionMailOp(session)def_call(self,*args):ifself._event()=='update_entity'andargs[0].e_schema=='CWUser':updated=set(args[0].iterkeys())ifnot(updated-frozenset(('eid','modification_date','last_login_time'))):# don't record last_login_time update which are done# automatically at login timereturnFalseself.session.add_query_data('pendingchanges',(self._event(),args))returnTruedef_event(self):returnself.event.split('_',1)[1]classEntityDeleteHook(SomethingChangedHook):events=('before_delete_entity',)def_call(self,eid):entity=self.session.entity(eid)try:title=entity.dc_title()except:# may raise an error during deletion process, for instance due to# missing required relationtitle='#%s'%eidself.session.add_query_data('pendingchanges',('delete_entity',(eid,str(entity.e_schema),title)))returnTruedeffilter_changes(changes):""" * when an entity has been deleted: * don't show deletion of its relations * don't show related TrInfo deletion if any * when an entity has been added don't show owned_by relation addition * don't show new TrInfo entities if any """# first build an index of changesindex={}added,deleted=set(),set()forchangeinchanges[:]:event,changedescr=changeifevent=='add_entity':entity=changedescr[0]added.add(entity.eid)ifentity.e_schema=='TrInfo':changes.remove(change)ifentity.from_state:try:changes.remove(('delete_relation',(entity.wf_info_for[0].eid,'in_state',entity.from_state[0].eid)))exceptValueError:passtry:changes.remove(('add_relation',(entity.wf_info_for[0].eid,'in_state',entity.to_state[0].eid)))exceptValueError:passevent='change_state'change=(event,(entity.wf_info_for[0],entity.from_state[0],entity.to_state[0]))changes.append(change)elifevent=='delete_entity':deleted.add(changedescr[0])index.setdefault(event,set()).add(change)# filter changesforeidinadded:try:forchangeinindex['add_relation'].copy():changedescr=change[1]# skip meta-relations which are set automatically# XXX generate list below using rtags (category = 'generated')ifchangedescr[1]in('created_by','owned_by','is','is_instance_of','from_state','to_state','wf_info_for',) \andchangedescr[0]==eid:index['add_relation'].remove(change)# skip in_state relation if the entity is being created# XXX this may be automatized by skipping all mandatory relation# at entity creation timeelifchangedescr[1]=='in_state'andchangedescr[0]inadded:index['add_relation'].remove(change)exceptKeyError:breakforeidindeleted:try:forchangeinindex['delete_relation'].copy():fromeid,rtype,toeid=change[1]iffromeid==eid:index['delete_relation'].remove(change)eliftoeid==eid:index['delete_relation'].remove(change)ifrtype=='wf_info_for':forchangeinindex['delete_entity'].copy():ifchange[1][0]==fromeid:index['delete_entity'].remove(change)exceptKeyError:breakforchangeinchanges:event,changedescr=changeifchangeinindex[event]:yieldchangeclassSupervisionEmailView(Component):"""view implementing the email API for data changes supervision notification """__select__=none_rset()id='supervision_notif'defrecipients(self):returnself.config['supervising-addrs']defsubject(self):returnself.req._('[%s supervision] changes summary')%self.config.appiddefcall(self,changes):user=self.req.actual_session().userself.w(self.req._('user %s has made the following change(s):\n\n')%user.login)forevent,changedescrinfilter_changes(changes):self.w(u'* ')getattr(self,event)(*changedescr)self.w(u'\n\n')def_entity_context(self,entity):return{'eid':entity.eid,'etype':entity.dc_type().lower(),'title':entity.dc_title()}defadd_entity(self,entity):msg=self.req._('added %(etype)s #%(eid)s (%(title)s)')self.w(u'%s\n'%(msg%self._entity_context(entity)))self.w(u' %s'%entity.absolute_url())defupdate_entity(self,entity):msg=self.req._('updated %(etype)s #%(eid)s (%(title)s)')self.w(u'%s\n'%(msg%self._entity_context(entity)))# XXX print changesself.w(u' %s'%entity.absolute_url())defdelete_entity(self,eid,etype,title):msg=self.req._('deleted %(etype)s #%(eid)s (%(title)s)')etype=display_name(self.req,etype).lower()self.w(msg%locals())defchange_state(self,entity,fromstate,tostate):msg=self.req._('changed state of %(etype)s #%(eid)s (%(title)s)')self.w(u'%s\n'%(msg%self._entity_context(entity)))self.w(_(' from state %(fromstate)s to state %(tostate)s\n'%{'fromstate':_(fromstate.name),'tostate':_(tostate.name)}))self.w(u' %s'%entity.absolute_url())def_relation_context(self,fromeid,rtype,toeid):_=self.req._session=self.req.actual_session()defdescribe(eid):try:return_(session.describe(eid)[0]).lower()exceptUnknownEid:# may occurs when an entity has been deleted from an external# source and we're cleaning its relationreturn_('unknown external entity')return{'rtype':_(rtype),'fromeid':fromeid,'frometype':describe(fromeid),'toeid':toeid,'toetype':describe(toeid)}defadd_relation(self,fromeid,rtype,toeid):msg=self.req._('added relation %(rtype)s from %(frometype)s #%(fromeid)s to %(toetype)s #%(toeid)s')self.w(msg%self._relation_context(fromeid,rtype,toeid))defdelete_relation(self,fromeid,rtype,toeid):msg=self.req._('deleted relation %(rtype)s from %(frometype)s #%(fromeid)s to %(toetype)s #%(toeid)s')self.w(msg%self._relation_context(fromeid,rtype,toeid))classSupervisionMailOp(SendMailOp):"""special send email operation which should be done only once for a bunch of changes """def_get_view(self):returnself.session.vreg.select_component('supervision_notif',self.session,None)def_prepare_email(self):session=self.sessionconfig=session.vreg.configuinfo={'email':config['sender-addr'],'name':config['sender-name']}view=self._get_view()content=view.render(changes=session.query_data('pendingchanges'))recipients=view.recipients()msg=format_mail(uinfo,recipients,content,view.subject(),config=config)self.to_send=[(msg,recipients)]defcommit_event(self):self._prepare_email()SendMailOp.commit_event(self)