[server] always monkeypatch QuerierHelper to handle dates and times on SQLite
The patch prevents problems due to a SQLite bug causing some values to
be returned as strings instead of properly typed objects.
This should probably be improved to use SQLite connection hooks. But
the major improvement is that the monkeypatch is now done all the time,
and not just for tests.
# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved.# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr## This file is part of CubicWeb.## CubicWeb is free software: you can redistribute it and/or modify it under the# terms of the GNU Lesser General Public License as published by the Free# Software Foundation, either version 2.1 of the License, or (at your option)# any later version.## CubicWeb is distributed in the hope that it will be useful, but WITHOUT# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more# details.## You should have received a copy of the GNU Lesser General Public License along# with CubicWeb. If not, see <http://www.gnu.org/licenses/>."""Core hooks: synchronize living session on persistent data changes"""__docformat__="restructuredtext en"fromcubicwebimport_fromcubicwebimportUnknownProperty,BadConnectionId,validation_errorfromcubicweb.predicatesimportis_instancefromcubicweb.serverimporthookdefget_user_sessions(repo,ueid):forsessioninrepo._sessions.values():ifueid==session.user.eid:yieldsessionclassSyncSessionHook(hook.Hook):__abstract__=Truecategory='syncsession'# user/groups synchronisation #################################################class_GroupOperation(hook.Operation):"""base class for group operation"""cnxuser=None# make pylint happydef__init__(self,cnx,*args,**kwargs):"""override to get the group name before actual groups manipulation: we may temporarily loose right access during a commit event, so no query should be emitted while comitting """rql='Any N WHERE G eid %(x)s, G name N'result=cnx.execute(rql,{'x':kwargs['geid']},build_descr=False)hook.Operation.__init__(self,cnx,*args,**kwargs)self.group=result[0][0]class_DeleteGroupOp(_GroupOperation):"""synchronize user when a in_group relation has been deleted"""defpostcommit_event(self):"""the observed connections set has been commited"""groups=self.cnxuser.groupstry:groups.remove(self.group)exceptKeyError:self.error('user %s not in group %s',self.cnxuser,self.group)class_AddGroupOp(_GroupOperation):"""synchronize user when a in_group relation has been added"""defpostcommit_event(self):"""the observed connections set has been commited"""groups=self.cnxuser.groupsifself.groupingroups:self.warning('user %s already in group %s',self.cnxuser,self.group)else:groups.add(self.group)classSyncInGroupHook(SyncSessionHook):__regid__='syncingroup'__select__=SyncSessionHook.__select__&hook.match_rtype('in_group')events=('after_delete_relation','after_add_relation')def__call__(self):ifself.event=='after_delete_relation':opcls=_DeleteGroupOpelse:opcls=_AddGroupOpforsessioninget_user_sessions(self._cw.repo,self.eidfrom):opcls(self._cw,cnxuser=session.user,geid=self.eidto)class_DelUserOp(hook.Operation):"""close associated user's session when it is deleted"""def__init__(self,cnx,sessionid):self.sessionid=sessionidhook.Operation.__init__(self,cnx)defpostcommit_event(self):"""the observed connections set has been commited"""try:self.cnx.repo.close(self.sessionid)exceptBadConnectionId:pass# already closedclassCloseDeletedUserSessionsHook(SyncSessionHook):__regid__='closession'__select__=SyncSessionHook.__select__&is_instance('CWUser')events=('after_delete_entity',)def__call__(self):"""modify user permission, need to update users"""forsessioninget_user_sessions(self._cw.repo,self.entity.eid):_DelUserOp(self._cw,session.sessionid)# CWProperty hooks #############################################################class_DelCWPropertyOp(hook.Operation):"""a user's custom properties has been deleted"""cwpropdict=key=None# make pylint happydefpostcommit_event(self):"""the observed connections set has been commited"""try:delself.cwpropdict[self.key]exceptKeyError:self.error('%s has no associated value',self.key)class_ChangeCWPropertyOp(hook.Operation):"""a user's custom properties has been added/changed"""cwpropdict=key=value=None# make pylint happydefpostcommit_event(self):"""the observed connections set has been commited"""self.cwpropdict[self.key]=self.valueclass_AddCWPropertyOp(hook.Operation):"""a user's custom properties has been added/changed"""cwprop=None# make pylint happydefpostcommit_event(self):"""the observed connections set has been commited"""cwprop=self.cwpropifnotcwprop.for_user:self.cnx.vreg['propertyvalues'][cwprop.pkey]=cwprop.value# if for_user is set, update is handled by a ChangeCWPropertyOp operationclassAddCWPropertyHook(SyncSessionHook):__regid__='addcwprop'__select__=SyncSessionHook.__select__&is_instance('CWProperty')events=('after_add_entity',)def__call__(self):key,value=self.entity.pkey,self.entity.valueifkey.startswith('sources.'):returncnx=self._cwtry:value=cnx.vreg.typed_value(key,value)exceptUnknownProperty:msg=_('unknown property key %s')raisevalidation_error(self.entity,{('pkey','subject'):msg},(key,))exceptValueErrorasex:raisevalidation_error(self.entity,{('value','subject'):str(ex)})ifnotcnx.user.matching_groups('managers'):cnx.add_relation(self.entity.eid,'for_user',cnx.user.eid)else:_AddCWPropertyOp(cnx,cwprop=self.entity)classUpdateCWPropertyHook(AddCWPropertyHook):__regid__='updatecwprop'events=('after_update_entity',)def__call__(self):entity=self.entityifnot('pkey'inentity.cw_editedor'value'inentity.cw_edited):returnkey,value=entity.pkey,entity.valueifkey.startswith('sources.'):returncnx=self._cwtry:value=cnx.vreg.typed_value(key,value)exceptUnknownProperty:returnexceptValueErrorasex:raisevalidation_error(entity,{('value','subject'):str(ex)})ifentity.for_user:forsessioninget_user_sessions(cnx.repo,entity.for_user[0].eid):_ChangeCWPropertyOp(cnx,cwpropdict=session.user.properties,key=key,value=value)else:# site wide properties_ChangeCWPropertyOp(cnx,cwpropdict=cnx.vreg['propertyvalues'],key=key,value=value)classDeleteCWPropertyHook(AddCWPropertyHook):__regid__='delcwprop'events=('before_delete_entity',)def__call__(self):eid=self.entity.eidcnx=self._cwforeidfrom,rtype,eidtoincnx.transaction_data.get('pendingrelations',()):ifrtype=='for_user'andeidfrom==self.entity.eid:# if for_user was set, delete has already been handledbreakelse:_DelCWPropertyOp(cnx,cwpropdict=cnx.vreg['propertyvalues'],key=self.entity.pkey)classAddForUserRelationHook(SyncSessionHook):__regid__='addcwpropforuser'__select__=SyncSessionHook.__select__&hook.match_rtype('for_user')events=('after_add_relation',)def__call__(self):cnx=self._cweidfrom=self.eidfromifnotcnx.entity_metas(eidfrom)['type']=='CWProperty':returnkey,value=cnx.execute('Any K,V WHERE P eid %(x)s,P pkey K,P value V',{'x':eidfrom})[0]ifcnx.vreg.property_info(key)['sitewide']:msg=_("site-wide property can't be set for user")raisevalidation_error(eidfrom,{('for_user','subject'):msg})forsessioninget_user_sessions(cnx.repo,self.eidto):_ChangeCWPropertyOp(cnx,cwpropdict=session.user.properties,key=key,value=value)classDelForUserRelationHook(AddForUserRelationHook):__regid__='delcwpropforuser'events=('after_delete_relation',)def__call__(self):cnx=self._cwkey=cnx.execute('Any K WHERE P eid %(x)s, P pkey K',{'x':self.eidfrom})[0][0]cnx.transaction_data.setdefault('pendingrelations',[]).append((self.eidfrom,self.rtype,self.eidto))forsessioninget_user_sessions(cnx.repo,self.eidto):_DelCWPropertyOp(cnx,cwpropdict=session.user.properties,key=key)