[migration] test addition of a cube providing a custom final type
This does not work yet.
Related to #7569998
# 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/>."""the facets box and some basic facets"""__docformat__="restructuredtext en"fromcubicwebimport_fromwarningsimportwarnfromlogilab.mtconverterimportxml_escapefromlogilab.common.decoratorsimportcachedpropertyfromlogilab.common.registryimportobjectify_predicate,yesfromcubicwebimporttagsfromcubicweb.predicatesimport(non_final_entity,multi_lines_rset,match_context_prop,relation_possible)fromcubicweb.utilsimportjson_dumpsfromcubicweb.uilibimportcss_em_num_valuefromcubicweb.viewimportAnyRsetViewfromcubicweb.webimportcomponent,facetasfacetbasefromcubicweb.web.views.ajaxcontrollerimportajaxfuncdeffacets(req,rset,context,mainvar=None,**kwargs):"""return the base rql and a list of widgets for facets applying to the given rset/context (cached version of :func:`_facet`) :param req: A :class:`~cubicweb.req.RequestSessionBase` object :param rset: A :class:`~cubicweb.rset.ResultSet` :param context: A string that match the ``__regid__`` of a ``FacetFilter`` :param mainvar: A string that match a select var from the rset """try:cache=req.__rset_facetsexceptAttributeError:cache=req.__rset_facets={}try:returncache[(rset,context,mainvar)]exceptKeyError:facets=_facets(req,rset,context,mainvar,**kwargs)cache[(rset,context,mainvar)]=facetsreturnfacetsdef_facets(req,rset,context,mainvar,**kwargs):"""return the base rql and a list of widgets for facets applying to the given rset/context :param req: A :class:`~cubicweb.req.RequestSessionBase` object :param rset: A :class:`~cubicweb.rset.ResultSet` :param context: A string that match the ``__regid__`` of a ``FacetFilter`` :param mainvar: A string that match a select var from the rset """### initialisation# XXX done by selectors, though maybe necessary when rset has been hijacked# (e.g. contextview_selector matched)origqlst=rset.syntax_tree()# union not yet supportediflen(origqlst.children)!=1:req.debug('facette disabled on union request %s',origqlst)returnNone,()rqlst=origqlst.copy()select=rqlst.children[0]filtered_variable,baserql=facetbase.init_facets(rset,select,mainvar)### Selectionpossible_facets=req.vreg['facets'].poss_visible_objects(req,rset=rset,rqlst=origqlst,select=select,context=context,filtered_variable=filtered_variable,**kwargs)wdgs=[(facet,facet.get_widget())forfacetinpossible_facets]returnbaserql,[wdgforfacet,wdginwdgsifwdgisnotNone]@objectify_predicatedefcontextview_selector(cls,req,rset=None,row=None,col=None,view=None,**kwargs):ifview:try:getcontext=getattr(view,'filter_box_context_info')exceptAttributeError:return0rset=getcontext()[0]ifrsetisNoneorrset.rowcount<2:return0wdgs=facets(req,rset,cls.__regid__,view=view)[1]returnlen(wdgs)return0@objectify_predicatedefhas_facets(cls,req,rset=None,**kwargs):ifrsetisNoneorrset.rowcount<2:return0wdgs=facets(req,rset,cls.__regid__,**kwargs)[1]returnlen(wdgs)deffilter_hiddens(w,baserql,wdgs,**kwargs):kwargs['facets']=','.join(wdg.facet.__regid__forwdginwdgs)kwargs['baserql']=baserqlforkey,valinkwargs.items():w(u'<input type="hidden" name="%s" value="%s" />'%(key,xml_escape(val)))classFacetFilterMixIn(object):"""Mixin Class to generate Facet Filter Form To generate the form, you need to explicitly call the following method: .. automethod:: generate_form The most useful function to override is: .. automethod:: layout_widgets """needs_js=['cubicweb.ajax.js','cubicweb.facets.js']needs_css=['cubicweb.facets.css']defgenerate_form(self,w,rset,divid,vid,vidargs=None,mainvar=None,paginate=False,cssclass='',hiddens=None,**kwargs):"""display a form to filter some view's content :param w: Write function :param rset: ResultSet to be filtered :param divid: Dom ID of the div where the rendering of the view is done. :type divid: string :param vid: ID of the view display in the div :type vid: string :param paginate: Is the view paginated? :type paginate: boolean :param cssclass: Additional css classes to put on the form. :type cssclass: string :param hiddens: other hidden parametters to include in the forms. :type hiddens: dict from extra keyword argument """# XXX Facet.context property hijacks an otherwise well-behaved# vocabulary with its own notions# Hence we whack here to avoid a clashkwargs.pop('context',None)baserql,wdgs=facets(self._cw,rset,context=self.__regid__,mainvar=mainvar,**kwargs)assertwdgsself._cw.add_js(self.needs_js)self._cw.add_css(self.needs_css)self._cw.html_headers.define_var('facetLoadingMsg',self._cw._('facet-loading-msg'))ifvidargsisnotNone:warn("[3.14] vidargs is deprecated. Maybe you're using some TableView?",DeprecationWarning,stacklevel=2)else:vidargs={}vidargs=dict((k,v)fork,vinvidargs.items()ifv)facetargs=xml_escape(json_dumps([divid,vid,paginate,vidargs]))w(u'<form id="%sForm" class="%s" method="post" action="" ''cubicweb:facetargs="%s" >'%(divid,cssclass,facetargs))w(u'<fieldset>')ifhiddensisNone:hiddens={}ifmainvar:hiddens['mainvar']=mainvarfilter_hiddens(w,baserql,wdgs,**hiddens)self.layout_widgets(w,self.sorted_widgets(wdgs))# <Enter> is supposed to submit the form only if there is a single# input:text field. However most browsers will submit the form# on <Enter> anyway if there is an input:submit field.## see: http://www.w3.org/MarkUp/html-spec/html-spec_8.html#SEC8.2## Firefox 7.0.1 does not submit form on <Enter> if there is more than a# input:text field and not input:submit but does it if there is an# input:submit.## IE 6 or Firefox 2 behave the same way.w(u'<input type="submit" class="hidden" />')#w(u'</fieldset>\n')w(u'</form>\n')defsorted_widgets(self,wdgs):"""sort widgets: by default sort by widget height, then according to widget.order (the original widgets order) """returnsorted(wdgs,key=lambdax:99*(notx.facet.start_unfolded)orx.height)deflayout_widgets(self,w,wdgs):"""layout widgets: by default simply render each of them (i.e. succession of <div>) """forwdginwdgs:wdg.render(w=w)classFilterBox(FacetFilterMixIn,component.CtxComponent):"""filter results of a query"""__regid__='facet.filterbox'__select__=((non_final_entity()&has_facets())|contextview_selector())# can't use has_facets because of# contextview mecanismcontext='left'# XXX doesn't support 'incontext', only 'left' or 'right'title=_('facet.filters')visible=True# functionality provided by the search box by defaultorder=1bk_linkbox_template=u'<div class="facetTitle">%s</div>'defrender_body(self,w,**kwargs):req=self._cwrset,vid,divid,paginate=self._get_context()assertlen(rset)>1ifvidisNone:vid=req.form.get('vid')ifself.bk_linkbox_templateandreq.vreg.schema['Bookmark'].has_perm(req,'add'):w(self.bookmark_link(rset))w(self.focus_link(rset))hiddens={}forparamin('subvid','vtitle'):ifparaminreq.form:hiddens[param]=req.form[param]self.generate_form(w,rset,divid,vid,paginate=paginate,hiddens=hiddens,**self.cw_extra_kwargs)def_get_context(self):view=self.cw_extra_kwargs.get('view')context=getattr(view,'filter_box_context_info',lambda:None)()ifcontext:rset,vid,divid,paginate=contextelse:rset=self.cw_rsetvid,divid=None,'pageContent'paginate=viewandview.paginablereturnrset,vid,divid,paginatedefbookmark_link(self,rset):req=self._cwbk_path=u'rql=%s'%req.url_quote(rset.printable_rql())ifreq.form.get('vid'):bk_path+=u'&vid=%s'%req.url_quote(req.form['vid'])bk_path=u'view?'+bk_pathbk_title=req._('my custom search')linkto=u'bookmarked_by:%s:subject'%req.user.eidbkcls=req.vreg['etypes'].etype_class('Bookmark')bk_add_url=bkcls.cw_create_url(req,path=bk_path,title=bk_title,__linkto=linkto)bk_base_url=bkcls.cw_create_url(req,title=bk_title,__linkto=linkto)bk_link=u'<a cubicweb:target="%s" id="facetBkLink" href="%s">%s</a>'%(xml_escape(bk_base_url),xml_escape(bk_add_url),req._('bookmark this search'))returnself.bk_linkbox_template%bk_linkdeffocus_link(self,rset):returnself.bk_linkbox_template%tags.a(self._cw._('focus on this selection'),href=self._cw.url(),id='focusLink')classFilterTable(FacetFilterMixIn,AnyRsetView):__regid__='facet.filtertable'__select__=has_facets()average_perfacet_uncomputable_overhead=.3defcall(self,vid,divid,vidargs=None,cssclass=''):hiddens=self.cw_extra_kwargs.setdefault('hiddens',{})hiddens['fromformfilter']='1'self.generate_form(self.w,self.cw_rset,divid,vid,vidargs=vidargs,cssclass=cssclass,**self.cw_extra_kwargs)@cachedpropertydefper_facet_height_overhead(self):return(css_em_num_value(self._cw.vreg,'facet_MarginBottom',.2)+css_em_num_value(self._cw.vreg,'facet_Padding',.2)+self.average_perfacet_uncomputable_overhead)deflayout_widgets(self,w,wdgs):"""layout widgets: put them in a table where each column should have sum(wdg.height) < wdg_stack_size. """w(u'<div class="filter">\n')widget_queue=[]queue_height=0wdg_stack_size=facetbase._DEFAULT_FACET_GROUP_HEIGHTforwdginwdgs:height=wdg.height+self.per_facet_height_overheadifqueue_height+height<=wdg_stack_size:widget_queue.append(wdg)queue_height+=heightcontinuew(u'<div class="facetGroup">')forqueuedinwidget_queue:queued.render(w=w)w(u'</div>')widget_queue=[wdg]queue_height=heightifwidget_queue:w(u'<div class="facetGroup">')forqueuedinwidget_queue:queued.render(w=w)w(u'</div>')w(u'</div>\n')# python-ajax remote functions used by facet widgets #########################@ajaxfunc(output_type='json')deffilter_build_rql(self,names,values):form=self._rebuild_posted_form(names,values)self._cw.form=formbuilder=facetbase.FilterRQLBuilder(self._cw)returnbuilder.build_rql()@ajaxfunc(output_type='json')deffilter_select_content(self,facetids,rql,mainvar):# Union unsupported yetselect=self._cw.vreg.parse(self._cw,rql).children[0]filtered_variable=facetbase.get_filtered_variable(select,mainvar)facetbase.prepare_select(select,filtered_variable)update_map={}forfidinfacetids:fobj=facetbase.get_facet(self._cw,fid,select,filtered_variable)update_map[fid]=fobj.possible_values()returnupdate_map# facets ######################################################################classCWSourceFacet(facetbase.RelationFacet):__regid__='cw_source-facet'rtype='cw_source'target_attr='name'classCreatedByFacet(facetbase.RelationFacet):__regid__='created_by-facet'rtype='created_by'target_attr='login'classInGroupFacet(facetbase.RelationFacet):__regid__='in_group-facet'rtype='in_group'target_attr='name'classInStateFacet(facetbase.RelationAttributeFacet):__regid__='in_state-facet'rtype='in_state'target_attr='name'# inherit from RelationFacet to benefit from its possible_values implementationclassETypeFacet(facetbase.RelationFacet):__regid__='etype-facet'__select__=yes()order=1rtype='is'target_attr='name'@propertydeftitle(self):returnself._cw._('entity type')defvocabulary(self):"""return vocabulary for this facet, eg a list of 2-uple (label, value) """etypes=self.cw_rset.column_types(0)returnsorted((self._cw._(etype),etype)foretypeinetypes)defadd_rql_restrictions(self):"""add restriction for this facet into the rql syntax tree"""value=self._cw.form.get(self.__regid__)ifnotvalue:returnself.select.add_type_restriction(self.filtered_variable,value)defpossible_values(self):"""return a list of possible values (as string since it's used to compare to a form value in javascript) for this facet """select=self.selectselect.save_state()try:facetbase.cleanup_select(select,self.filtered_variable)etype_var=facetbase.prepare_vocabulary_select(select,self.filtered_variable,self.rtype,self.role)attrvar=select.make_variable()select.add_selected(attrvar)select.add_relation(etype_var,'name',attrvar)return[etypefor_,etypeinself.rqlexec(select.as_string())]finally:select.recover()classHasTextFacet(facetbase.AbstractFacet):__select__=relation_possible('has_text','subject')&match_context_prop()__regid__='has_text-facet'rtype='has_text'role='subject'order=0@propertydefwdgclass(self):returnfacetbase.FacetStringWidget@propertydeftitle(self):returnself._cw._('has_text')defget_widget(self):"""return the widget instance to use to display this facet default implentation expects a .vocabulary method on the facet and return a combobox displaying this vocabulary """returnself.wdgclass(self)defadd_rql_restrictions(self):"""add restriction for this facet into the rql syntax tree"""value=self._cw.form.get(self.__regid__)ifnotvalue:returnself.select.add_constant_restriction(self.filtered_variable,'has_text',value,'String')