[R hooks] use DONT_CHECK_RTYPES_ON_[ADD|DEL] constant, don't check wf related internal relations
"""abstract controler classe for CubicWeb web client:organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"importdatetimefromcubicwebimporttyped_eidfromcubicweb.selectorsimportyes,require_group_compatfromcubicweb.appobjectimportAppObjectfromcubicweb.webimportLOGGER,Redirect,RequestErrorNAVIGATION_PARAMETERS=(('vid','__redirectvid'),('rql','__redirectrql'),('__redirectpath','__redirectpath'),('__redirectparams','__redirectparams'),)NAV_FORM_PARAMETERS=tuple(fpforap,fpinNAVIGATION_PARAMETERS)defredirect_params(form):"""transform redirection parameters into navigation parameters """params={}# extract navigation parameters from redirection parametersfornavparam,redirectparaminNAVIGATION_PARAMETERS:ifnavparam==redirectparam:continueifredirectparaminform:params[navparam]=form[redirectparam]returnparamsdefparse_relations_descr(rdescr):"""parse a string describing some relations, in the form subjeids:rtype:objeids where subjeids and objeids are eids separeted by a underscore return an iterator on (subject eid, relation type, object eid) found """forrstrinrdescr:subjs,rtype,objs=rstr.split(':')forsubjinsubjs.split('_'):forobjinobjs.split('_'):yieldtyped_eid(subj),rtype,typed_eid(obj)defappend_url_params(url,params):"""append raw parameters to the url. Given parameters, if any, are expected to be already url-quoted. """ifparams:ifnot'?'inurl:url+='?'else:url+='&'url+=paramsreturnurlclassController(AppObject):"""a controller is responsible to make necessary stuff to publish a request. There is usually at least one standard "view" controller and another linked by forms to edit objects ("edit"). """__registry__='controllers'__select__=yes()registered=require_group_compat(AppObject.registered)def__init__(self,*args,**kwargs):super(Controller,self).__init__(*args,**kwargs)# attributes use to control after edition redirectionself._after_deletion_path=Noneself._edited_entity=Nonedefpublish(self,rset=None):"""publish the current request, with an option input rql string (already processed if necessary) """raiseNotImplementedError# generic methods useful for concret implementations ######################defprocess_rql(self,rql):"""execute rql if specified"""# XXX assigning to self really necessary?self.rset=Noneifrql:self.ensure_ro_rql(rql)ifnotisinstance(rql,unicode):rql=unicode(rql,self.req.encoding)pp=self.vreg.select_object('components','magicsearch',self.req)ifppisnotNone:self.rset=pp.process_query(rql,self.req)returnself.rsetdefcheck_expected_params(self,params):"""check that the given list of parameters are specified in the form dictionary """missing=[]forparaminparams:ifnotself.req.form.get(param):missing.append(param)ifmissing:raiseRequestError('missing required parameter(s): %s'%','.join(missing))defnotify_edited(self,entity):"""called by edit_entity() to notify which entity is edited"""# NOTE: we can't use entity.rest_path() at this point because# rest_path() could rely on schema constraints (such as a required# relation) that might not be satisfied yet (in case of creations)ifnotself._edited_entity:self._edited_entity=entitydefdelete_entities(self,eidtypes):"""delete entities from the repository"""redirect_info=set()eidtypes=tuple(eidtypes)foreid,etypeineidtypes:entity=self.req.eid_rset(eid,etype).get_entity(0,0)path,params=entity.after_deletion_path()redirect_info.add((path,tuple(params.iteritems())))entity.delete()iflen(redirect_info)>1:# In the face of ambiguity, refuse the temptation to guess.self._after_deletion_path='view',()else:self._after_deletion_path=iter(redirect_info).next()iflen(eidtypes)>1:self.req.set_message(self.req._('entities deleted'))else:self.req.set_message(self.req._('entity deleted'))defdelete_relations(self,rdefs):"""delete relations from the repository"""# FIXME convert to using the syntax subject:relation:eidsexecute=self.req.executeforsubj,rtype,objinrdefs:rql='DELETE X %s Y where X eid %%(x)s, Y eid %%(y)s'%rtypeexecute(rql,{'x':subj,'y':obj},('x','y'))self.req.set_message(self.req._('relations deleted'))definsert_relations(self,rdefs):"""insert relations into the repository"""execute=self.req.executeforsubj,rtype,objinrdefs:rql='SET X %s Y where X eid %%(x)s, Y eid %%(y)s'%rtypeexecute(rql,{'x':subj,'y':obj},('x','y'))defreset(self):"""reset form parameters and redirect to a view determinated by given parameters """newparams={}# sets message if neededifself.req.message:newparams['__message']=self.req.messageifself.req.form.has_key('__action_apply'):self._return_to_edition_view(newparams)ifself.req.form.has_key('__action_cancel'):self._return_to_lastpage(newparams)else:self._return_to_original_view(newparams)def_return_to_original_view(self,newparams):"""validate-button case"""# transforms __redirect[*] parameters into regular form parametersnewparams.update(redirect_params(self.req.form))# find out if we have some explicit `rql` needsrql=newparams.pop('rql',None)# if rql is needed (explicit __redirectrql or multiple deletions for# instance), we have to use the old `view?rql=...` formifrql:path='view'newparams['rql']=rqlelif'__redirectpath'inself.req.form:# if redirect path was explicitly specified in the form, use itpath=self.req.form['__redirectpath']ifself._edited_entityandpath!=self._edited_entity.rest_path():# XXX may be here on modification? if yes the message should be# modified where __createdpath is detected (cw.web.request)newparams['__createdpath']=self._edited_entity.rest_path()elifself._after_deletion_path:# else it should have been set during form processingpath,params=self._after_deletion_pathparams=dict(params)# params given as tupleparams.update(newparams)newparams=paramselifself._edited_entity:path=self._edited_entity.rest_path()else:path='view'url=self.build_url(path,**newparams)url=append_url_params(url,self.req.form.get('__redirectparams'))raiseRedirect(url)def_return_to_edition_view(self,newparams):"""apply-button case"""form=self.req.formifself._edited_entity:path=self._edited_entity.rest_path()newparams.pop('rql',None)# else, fallback on the old `view?rql=...` url formelif'rql'inself.req.form:path='view'newparams['rql']=form['rql']else:self.warning("the edited data seems inconsistent")path='view'# pick up the correction edition viewifform.get('__form_id'):newparams['vid']=form['__form_id']# re-insert copy redirection parametersforredirectparaminNAV_FORM_PARAMETERS:ifredirectparaminform:newparams[redirectparam]=form[redirectparam]raiseRedirect(self.build_url(path,**newparams))def_return_to_lastpage(self,newparams):"""cancel-button case: in this case we are always expecting to go back where we came from, and this is not easy. Currently we suppose that __redirectpath is specifying that place if found, else we look in the request breadcrumbs for the last visited page. """if'__redirectpath'inself.req.form:# if redirect path was explicitly specified in the form, use itpath=self.req.form['__redirectpath']url=self.build_url(path,**newparams)url=append_url_params(url,self.req.form.get('__redirectparams'))else:url=self.req.last_visited_page()raiseRedirect(url)fromcubicwebimportset_log_methodsset_log_methods(Controller,LOGGER)