[rqlrewrite] if inlined relation has to be moved to a subquery, take care of the object of the relation (closes #1945725)
As the object of the relation is moved to the subquery, all variables/relations representing
some of its attributes or inlined relations should be moved there as well.
This avoid error such as "BadRQLQuery: variable AF should be selected by the subquery"
on security insertion.
# copyright 2003-2011 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/>."""Associate url's path to view identifier / rql queries.It currently handles url path with the forms:* <publishing_method>* minimal REST publishing: * <eid> * <etype>[/<attribute name>/<attribute value>]** folder navigationYou can actually control URL (more exactly path) resolution using anURL path evaluator... note:: Actionpath and Folderpath execute a query whose results is lost because of redirecting instead of direct traversal."""__docformat__="restructuredtext en"fromrqlimportTypeResolverExceptionfromcubicwebimportRegistryException,typed_eidfromcubicweb.webimportNotFound,Redirect,componentclassPathDontMatch(Exception):"""exception used by url evaluators to notify they can't evaluate a path """classURLPublisherComponent(component.Component):"""Associate url path to view identifier / rql queries, by applying a chain of urlpathevaluator components. An evaluator is a URLPathEvaluator subclass with an .evaluate_path method taking the request object and the path to publish as argument. It will either return a publishing method identifier and an rql query on success or raise a `PathDontMatch` exception on failure. URL evaluators are called according to their `priority` attribute, with 0 as the greatest priority and greater values as lower priority. The first evaluator returning a result or raising something else than `PathDontMatch` will stop the handlers chain. """__regid__='urlpublisher'vreg=None# XXX necessary until property for deprecation warning is on appobjectdef__init__(self,vreg,default_method='view'):super(URLPublisherComponent,self).__init__()self.vreg=vregself.default_method=default_methodevaluators=[]forevaluatorclsinvreg['components']['urlpathevaluator']:# instantiation neededevaluator=evaluatorcls(self)evaluators.append(evaluator)self.evaluators=sorted(evaluators,key=lambdax:x.priority)defprocess(self,req,path):"""Given an url (essentialy caracterized by a path on the server, but additional information may be found in the request object), return a publishing method identifier (e.g. controller) and an optional result set. :type req: `cubicweb.web.request.CubicWebRequestBase` :param req: the request object :type path: str :param path: the path of the resource to publish :rtype: tuple(str, `cubicweb.rset.ResultSet` or None) :return: the publishing method identifier and an optional result set :raise NotFound: if no handler is able to decode the given path """parts=[partforpartinpath.split('/')ifpart!='']or(self.default_method,)ifreq.form.get('rql'):ifparts[0]inself.vreg['controllers']:returnparts[0],Nonereturn'view',Noneforevaluatorinself.evaluators:try:pmid,rset=evaluator.evaluate_path(req,parts[:])breakexceptPathDontMatch:continueelse:raiseNotFound(path)ifpmidisNone:pmid=self.default_methodreturnpmid,rsetclassURLPathEvaluator(component.Component):__abstract__=True__regid__='urlpathevaluator'vreg=None# XXX necessary until property for deprecation warning is on appobjectdef__init__(self,urlpublisher):self.urlpublisher=urlpublisherself.vreg=urlpublisher.vregclassRawPathEvaluator(URLPathEvaluator):"""handle path of the form:: <publishing_method>?parameters... """priority=0defevaluate_path(self,req,parts):iflen(parts)==1andparts[0]inself.vreg['controllers']:returnparts[0],NoneraisePathDontMatch()classEidPathEvaluator(URLPathEvaluator):"""handle path with the form:: <eid> """priority=1defevaluate_path(self,req,parts):iflen(parts)!=1:raisePathDontMatch()try:rset=req.execute('Any X WHERE X eid %(x)s',{'x':typed_eid(parts[0])})exceptValueError:raisePathDontMatch()ifrset.rowcount==0:raiseNotFound()returnNone,rsetclassRestPathEvaluator(URLPathEvaluator):"""handle path with the form:: <etype>[[/<attribute name>]/<attribute value>]* """priority=3defevaluate_path(self,req,parts):ifnot(0<len(parts)<4):raisePathDontMatch()try:etype=self.vreg.case_insensitive_etypes[parts.pop(0).lower()]exceptKeyError:raisePathDontMatch()cls=self.vreg['etypes'].etype_class(etype)ifparts:iflen(parts)==2:attrname=parts.pop(0).lower()try:cls.e_schema.subjrels[attrname]exceptKeyError:raisePathDontMatch()else:attrname=cls._rest_attr_info()[0]value=req.url_unquote(parts.pop(0))returnself.handle_etype_attr(req,cls,attrname,value)returnself.handle_etype(req,cls)defset_vid_for_rset(self,req,cls,rset):# cls is there to ease overridingifrset.rowcount==0:raiseNotFound()# we've to set a default vid here, since vid_from_rset may try to use a# table view if fetch_rql include some non final relationifrset.rowcount==1:req.form.setdefault('vid','primary')else:# rset.rowcount >= 1req.form.setdefault('vid','sameetypelist')defhandle_etype(self,req,cls):rset=req.execute(cls.fetch_rql(req.user))self.set_vid_for_rset(req,cls,rset)returnNone,rsetdefhandle_etype_attr(self,req,cls,attrname,value):rql=cls.fetch_rql(req.user,['X %s%%(x)s'%(attrname)],mainvar='X',ordermethod=None)ifattrname=='eid':try:rset=req.execute(rql,{'x':typed_eid(value)})except(ValueError,TypeResolverException):# conflicting eid/typeraisePathDontMatch()else:rset=req.execute(rql,{'x':value})self.set_vid_for_rset(req,cls,rset)returnNone,rsetclassURLRewriteEvaluator(URLPathEvaluator):"""tries to find a rewrite rule to apply URL rewrite rule definitions are stored in URLRewriter objects """priority=2defevaluate_path(self,req,parts):# uri <=> req._twreq.path or req._twreq.uriuri=req.url_unquote('/'+'/'.join(parts))evaluators=sorted(self.vreg['urlrewriting'].all_objects(),key=lambdax:x.priority,reverse=True)forrewriterclsinevaluators:rewriter=rewritercls(req)try:# XXX we might want to chain url rewritesreturnrewriter.rewrite(req,uri)exceptKeyError:continueraisePathDontMatch()classActionPathEvaluator(URLPathEvaluator):"""handle path with the form:: <any evaluator path>/<action> """priority=4defevaluate_path(self,req,parts):iflen(parts)<2:raisePathDontMatch()# remove last part and see if this is something like an actions# if so, call# XXX bad smell: refactor to simpler codetry:actionsreg=self.vreg['actions']requested=parts.pop(-1)actions=actionsreg[requested]exceptRegistryException:raisePathDontMatch()forevaluatorinself.urlpublisher.evaluators:ifevaluatorisselforevaluator.priority==0:continuetry:pmid,rset=evaluator.evaluate_path(req,parts[:])exceptPathDontMatch:continueelse:try:action=actionsreg._select_best(actions,req,rset=rset)ifactionisnotNone:raiseRedirect(action.url())exceptRegistryException:pass# continue searchingraisePathDontMatch()