[rql2sql] more cases fixed where something is wrongly added to GROUPBY, causing unexpected results for the query
# copyright 2003-2010 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/>."""navigation components definition for CubicWeb web client"""__docformat__="restructuredtext en"_=unicodefromrql.nodesimportVariableRef,Constantfromlogilab.mtconverterimportxml_escapefromlogilab.common.deprecationimportdeprecatedfromcubicweb.selectorsimport(paginated_rset,sorted_rset,adaptable,implements)fromcubicweb.uilibimportcutfromcubicweb.viewimportEntityAdapter,implements_adapter_compatfromcubicweb.web.componentimportEmptyComponent,EntityCtxComponent,NavigationComponentclassPageNavigation(NavigationComponent):defcall(self):"""displays a resultset by page"""params=dict(self._cw.form)self.clean_params(params)basepath=self._cw.relative_path(includeparams=False)self.w(u'<div class="pagination">')self.w(u'%s '%self.previous_link(basepath,params))self.w(u'[ %s ]'%u' | '.join(self.iter_page_links(basepath,params)))self.w(u' %s'%self.next_link(basepath,params))self.w(u'</div>')defindex_display(self,start,stop):returnu'%s - %s'%(start+1,stop+1)defiter_page_links(self,basepath,params):rset=self.cw_rsetpage_size=self.page_sizestart=0whilestart<rset.rowcount:stop=min(start+page_size-1,rset.rowcount-1)yieldself.page_link(basepath,params,start,stop,self.index_display(start,stop))start=stop+1classPageNavigationSelect(PageNavigation):"""displays a resultset by page as PageNavigationSelect but in a <select>, better when there are a lot of results. """__select__=paginated_rset(4)page_link_templ=u'<option value="%s" title="%s">%s</option>'selected_page_link_templ=u'<option value="%s" selected="selected" title="%s">%s</option>'defcall(self):params=dict(self._cw.form)self.clean_params(params)basepath=self._cw.relative_path(includeparams=False)w=self.ww(u'<div class="pagination">')w(u'%s '%self.previous_link(basepath,params))w(u'<select onchange="javascript: document.location=this.options[this.selectedIndex].value">')foroptioninself.iter_page_links(basepath,params):w(option)w(u'</select>')w(u' %s'%self.next_link(basepath,params))w(u'</div>')classSortedNavigation(NavigationComponent):"""sorted navigation apply if navigation is needed (according to page size) and if the result set is sorted """__select__=paginated_rset()&sorted_rset()# number of considered chars to build page linksnb_chars=5defdisplay_func(self,rset,col,attrname):ifattrnameisnotNone:defindex_display(row):ifnotrset[row][col]:# outer joinreturnu''entity=rset.get_entity(row,col)returnentity.printable_value(attrname,format='text/plain')elifself._cw.vreg.schema.eschema(rset.description[0][col]).final:defindex_display(row):returnunicode(rset[row][col])else:defindex_display(row):returnrset.get_entity(row,col).view('text')returnindex_displaydefcall(self):"""displays links to navigate accross pages of a result set Displayed result is done according to a variable on which the sort is done, and looks like: [ana - cro] | [cro - ghe] | ... | [tim - zou] """w=self.wrset=self.cw_rsetpage_size=self.page_sizerschema=self._cw.vreg.schema.rschema# attrname = the name of attribute according to which the sort# is done if anyforsorterminrset.syntax_tree().children[0].orderby:ifisinstance(sorterm.term,Constant):col=sorterm.term.value-1index_display=self.display_func(rset,col,None)breakvar=sorterm.term.get_nodes(VariableRef)[0].variablecol=Noneforrefinvar.references():rel=ref.relation()ifrelisNone:continueattrname=rel.r_typeifattrnamein('is','has_text'):continueifnotrschema(attrname).final:col=var.selected_index()attrname=NoneifcolisNone:# final relation or not selected non final relationifvarisrel.children[0]:relvar=rel.children[1].children[0].get_nodes(VariableRef)[0]else:relvar=rel.children[0].variablecol=relvar.selected_index()ifcolisnotNone:breakelse:# no relation but maybe usable anyway if selectedcol=var.selected_index()attrname=NoneifcolisnotNone:index_display=self.display_func(rset,col,attrname)breakelse:# nothing usable found, use the first columnindex_display=self.display_func(rset,0,None)blocklist=[]params=dict(self._cw.form)self.clean_params(params)start=0basepath=self._cw.relative_path(includeparams=False)whilestart<rset.rowcount:stop=min(start+page_size-1,rset.rowcount-1)cell=self.format_link_content(index_display(start),index_display(stop))blocklist.append(self.page_link(basepath,params,start,stop,cell))start=stop+1self.write_links(basepath,params,blocklist)defformat_link_content(self,startstr,stopstr):text=u'%s - %s'%(startstr.lower()[:self.nb_chars],stopstr.lower()[:self.nb_chars])returnxml_escape(text)defwrite_links(self,basepath,params,blocklist):self.w(u'<div class="pagination">')self.w(u'%s '%self.previous_link(basepath,params))self.w(u'[ %s ]'%u' | '.join(blocklist))self.w(u' %s'%self.next_link(basepath,params))self.w(u'</div>')fromcubicweb.interfacesimportIPrevNextclassIPrevNextAdapter(EntityAdapter):"""interface for entities which can be linked to a previous and/or next entity """__needs_bw_compat__=True__regid__='IPrevNext'__select__=implements(IPrevNext,warn=False)# XXX for bw compat, else should be abstract@implements_adapter_compat('IPrevNext')defnext_entity(self):"""return the 'next' entity"""raiseNotImplementedError@implements_adapter_compat('IPrevNext')defprevious_entity(self):"""return the 'previous' entity"""raiseNotImplementedErrorclassNextPrevNavigationComponent(EntityCtxComponent):__regid__='prevnext'# register msg not generated since no entity implements IPrevNext in cubicweb# itselfhelp=_('ctxcomponents_prevnext_description')__select__=EntityCtxComponent.__select__&adaptable('IPrevNext')context='navbottom'order=10definit_rendering(self):adapter=self.entity.cw_adapt_to('IPrevNext')self.previous=adapter.previous_entity()self.next=adapter.next_entity()ifnot(self.previousorself.next):raiseEmptyComponent()defrender_body(self,w):w(u'<div class="prevnext">')self.prevnext(w)w(u'</div>')w(u'<div class="clear"></div>')defprevnext(self,w):ifself.previous:self.prevnext_entity(w,self.previous,'prev')ifself.next:self.prevnext_entity(w,self.next,'next')defprevnext_entity(self,w,entity,type):textsize=self._cw.property_value('navigation.short-line-size')iftype=='prev':title=self._cw._('i18nprevnext_previous')icon=u'<< 'cssclass=u'previousEntity left'else:title=self._cw._('i18nprevnext_next')icon=u'>> 'cssclass=u'nextEntity right'self.prevnext_div(w,type,cssclass,entity.absolute_url(),title,icon+xml_escape(cut(entity.dc_title(),textsize)))defprevnext_div(self,w,type,cssclass,url,title,content):w(u'<div class="%s">'%cssclass)w(u'<a href="%s" title="%s">%s</a>'%(xml_escape(url),xml_escape(title),content))w(u'</div>')self._cw.html_headers.add_raw('<link rel="%s" href="%s" />'%(type,xml_escape(url)))defdo_paginate(view,rset=None,w=None,show_all_option=True,page_size=None):"""write pages index in w stream (default to view.w) and then limit the result set (default to view.rset) to the currently displayed page """req=view._cwifrsetisNone:rset=view.cw_rsetifwisNone:w=view.wnav=req.vreg['components'].select_or_none('navigation',req,rset=rset,page_size=page_size,view=view)ifnav:ifwisNone:w=view.w# get boundaries before component renderingstart,stop=nav.page_boundaries()nav.render(w=w)params=dict(req.form)nav.clean_params(params)# make a link to see them allifshow_all_option:basepath=req.relative_path(includeparams=False)params['__force_display']=1url=nav.page_url(basepath,params)w(u'<div><a href="%s">%s</a></div>\n'%(xml_escape(url),req._('show %s results')%len(rset)))rset.limit(offset=start,limit=stop-start,inplace=True)defpaginate(view,show_all_option=True,w=None,page_size=None,rset=None):"""paginate results if the view is paginable and we're not explictly told to display everything (by setting __force_display in req.form) """ifview.paginableandnotview._cw.form.get('__force_display'):do_paginate(view,rset,w,show_all_option,page_size)# monkey patch base View class to add a .paginate([...])# method to be called to write pages index in the view and then limit the result# set to the current pagefromcubicweb.viewimportViewView.do_paginate=do_paginateView.paginate=paginate#@deprecated (see below)deflimit_rset_using_paged_nav(self,req,rset,w,forcedisplay=False,show_all_option=True,page_size=None):ifnot(forcedisplayorreq.form.get('__force_display')isnotNone):do_paginate(self,rset,w,show_all_option,page_size)View.pagination=deprecated('[3.2] .pagination is deprecated, use paginate')(limit_rset_using_paged_nav)limit_rset_using_paged_nav=deprecated('[3.6] limit_rset_using_paged_nav is deprecated, use do_paginate')(limit_rset_using_paged_nav)