[server] fix NameError (missing VIRTUAL_RTYPES import) in checkintegrity.py
# 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/>."""abstract component class and base components definition for CubicWeb webclient"""__docformat__="restructuredtext en"_=unicodefromwarningsimportwarnfromlogilab.common.deprecationimportclass_deprecated,class_renamed,deprecatedfromlogilab.mtconverterimportxml_escapefromcubicwebimportUnauthorized,role,target,tagsfromcubicweb.schemaimportdisplay_namefromcubicweb.uilibimportjs,domidfromcubicweb.utilsimportjson_dumpsfromcubicweb.viewimportReloadableMixIn,Componentfromcubicweb.selectorsimport(no_cnx,paginated_rset,one_line_rset,non_final_entity,partial_relation_possible,partial_has_related_entities)fromcubicweb.appobjectimportAppObjectfromcubicweb.webimportINTERNAL_FIELD_VALUE,stdmsgs# abstract base class for navigation components ################################classNavigationComponent(Component):"""abstract base class for navigation components"""__regid__='navigation'__select__=paginated_rset()cw_property_defs={_('visible'):dict(type='Boolean',default=True,help=_('display the component or not')),}page_size_property='navigation.page-size'start_param='__start'stop_param='__stop'page_link_templ=u'<span class="slice"><a href="%s" title="%s">%s</a></span>'selected_page_link_templ=u'<span class="selectedSlice"><a href="%s" title="%s">%s</a></span>'previous_page_link_templ=next_page_link_templ=page_link_templno_previous_page_link=u'<<'no_next_page_link=u'>>'def__init__(self,req,rset,**kwargs):super(NavigationComponent,self).__init__(req,rset=rset,**kwargs)self.starting_from=0self.total=rset.rowcountdefget_page_size(self):try:returnself._page_sizeexceptAttributeError:page_size=self.cw_extra_kwargs.get('page_size')ifpage_sizeisNone:if'page_size'inself._cw.form:page_size=int(self._cw.form['page_size'])else:page_size=self._cw.property_value(self.page_size_property)self._page_size=page_sizereturnpage_sizedefset_page_size(self,page_size):self._page_size=page_sizepage_size=property(get_page_size,set_page_size)defpage_boundaries(self):try:stop=int(self._cw.form[self.stop_param])+1start=int(self._cw.form[self.start_param])exceptKeyError:start,stop=0,self.page_sizeifstart>=len(self.cw_rset):start,stop=0,self.page_sizeself.starting_from=startreturnstart,stopdefclean_params(self,params):ifself.start_paraminparams:delparams[self.start_param]ifself.stop_paraminparams:delparams[self.stop_param]defpage_url(self,path,params,start=None,stop=None):params=dict(params)ifstartisnotNone:params[self.start_param]=startifstopisnotNone:params[self.stop_param]=stopview=self.cw_extra_kwargs.get('view')ifviewisnotNoneandhasattr(view,'page_navigation_url'):url=view.page_navigation_url(self,path,params)elifpath=='json':url=self.ajax_page_url(**params)else:url=self._cw.build_url(path,**params)# XXX hack to avoid opening a new page containing the evaluation of the# js expression on ajax callifurl.startswith('javascript:'):url+='; $.noop();'returnurldefajax_page_url(self,**params):divid=params.setdefault('divid','pageContent')params['rql']=self.cw_rset.printable_rql()return"javascript: $(%s).loadxhtml('json', %s, 'get', 'swap')"%(json_dumps('#'+divid),js.ajaxFuncArgs('view',params))defpage_link(self,path,params,start,stop,content):url=xml_escape(self.page_url(path,params,start,stop))ifstart==self.starting_from:returnself.selected_page_link_templ%(url,content,content)returnself.page_link_templ%(url,content,content)defprevious_link(self,path,params,content='<<',title=_('previous_results')):start=self.starting_fromifnotstart:returnself.no_previous_page_linkstart=max(0,start-self.page_size)stop=start+self.page_size-1url=xml_escape(self.page_url(path,params,start,stop))returnself.previous_page_link_templ%(url,title,content)defnext_link(self,path,params,content='>>',title=_('next_results')):start=self.starting_from+self.page_sizeifstart>=self.total:returnself.no_next_page_linkstop=start+self.page_size-1url=xml_escape(self.page_url(path,params,start,stop))returnself.next_page_link_templ%(url,title,content)# new contextual components system #############################################defoverride_ctx(cls,**kwargs):cwpdefs=cls.cw_property_defs.copy()cwpdefs['context']=cwpdefs['context'].copy()cwpdefs['context'].update(kwargs)returncwpdefsclassEmptyComponent(Exception):"""some selectable component has actually no content and should not be rendered """classLink(object):"""a link to a view or action in the ui. Use this rather than `cw.web.htmlwidgets.BoxLink`. Note this class could probably be avoided with a proper DOM on the server side. """newstyle=Truedef__init__(self,href,label,**attrs):self.href=hrefself.label=labelself.attrs=attrsdef__unicode__(self):returntags.a(self.label,href=self.href,**self.attrs)defrender(self,w):w(tags.a(self.label,href=self.href,**self.attrs))classSeparator(object):"""a menu separator. Use this rather than `cw.web.htmlwidgets.BoxSeparator`. """newstyle=Truedefrender(self,w):w(u'<hr class="boxSeparator"/>')def_bwcompatible_render_item(w,item):ifhasattr(item,'render'):ifgetattr(item,'newstyle',False):ifisinstance(item,Separator):w(u'</ul>')item.render(w)w(u'<ul>')else:w(u'<li>')item.render(w)w(u'</li>')else:item.render(w)# XXX displays <li> by itselfelse:w(u'<li>%s</li>'%item)classLayout(Component):__regid__='layout'__abstract__=Truedefinit_rendering(self):"""init view for rendering. Return true if we should go on, false if we should stop now. """view=self.cw_extra_kwargs['view']try:view.init_rendering()exceptUnauthorized,ex:self.warning("can't render %s: %s",view,ex)returnFalseexceptEmptyComponent:returnFalsereturnTrueclassCtxComponent(AppObject):"""base class for contextual components. The following contexts are predefined: * boxes: 'left', 'incontext', 'right' * section: 'navcontenttop', 'navcontentbottom', 'navtop', 'navbottom' * other: 'ctxtoolbar' The 'incontext', 'navcontenttop', 'navcontentbottom' and 'ctxtoolbar' contexts are handled by the default primary view, others by the default main template. All subclasses may not support all those contexts (for instance if it can't be displayed as box, or as a toolbar icon). You may restrict allowed context as follows: .. sourcecode:: python class MyComponent(CtxComponent): cw_property_defs = override_ctx(CtxComponent, vocabulary=[list of contexts]) context = 'my default context' You can configure a component's default context by simply giving an appropriate value to the `context` class attribute, as seen above. """__registry__='ctxcomponents'__select__=~no_cnx()categories_in_order=()cw_property_defs={_('visible'):dict(type='Boolean',default=True,help=_('display the box or not')),_('order'):dict(type='Int',default=99,help=_('display order of the box')),_('context'):dict(type='String',default='left',vocabulary=(_('left'),_('incontext'),_('right'),_('navtop'),_('navbottom'),_('navcontenttop'),_('navcontentbottom'),_('ctxtoolbar')),help=_('context where this component should be displayed')),}visible=Trueorder=0context='left'contextual=Falsetitle=None# XXX support kwargs for compat with old boxes which gets the view as# argumentdefrender(self,w,**kwargs):ifhasattr(self,'call'):warn('[3.10] should not anymore implement call on %s, see new CtxComponent api'%self.__class__,DeprecationWarning)self.w=wdefwview(__vid,rset=None,__fallback_vid=None,**kwargs):self._cw.view(__vid,rset,__fallback_vid,w=self.w,**kwargs)self.wview=wviewself.call(**kwargs)returngetlayout=self._cw.vreg['components'].selectlayout=getlayout('layout',self._cw,**self.layout_select_args())layout.render(w)deflayout_select_args(self):try:# XXX ensure context is given when the component is reloaded through# ajaxcontext=self.cw_extra_kwargs['context']exceptKeyError:context=self.cw_propval('context')returndict(rset=self.cw_rset,row=self.cw_row,col=self.cw_col,view=self,context=context)definit_rendering(self):"""init rendering callback: that's the good time to check your component has some content to display. If not, you can still raise :exc:`EmptyComponent` to inform it should be skipped. Also, :exc:`Unauthorized` will be catched, logged, then the component will be skipped. """self.items=[]@propertydefdomid(self):"""return the HTML DOM identifier for this component"""returndomid(self.__regid__)@propertydefcssclass(self):"""return the CSS class name for this component"""returndomid(self.__regid__)defrender_title(self,w):"""return the title for this component"""ifself.title:w(self._cw._(self.title))defrender_body(self,w):"""return the body (content) for this component"""raiseNotImplementedError()defrender_items(self,w,items=None,klass=u'boxListing'):ifitemsisNone:items=self.itemsassertitemsw(u'<ul class="%s">'%klass)foriteminitems:_bwcompatible_render_item(w,item)w(u'</ul>')defappend(self,item):self.items.append(item)defaction_link(self,action):returnself.link(self._cw._(action.title),action.url())deflink(self,title,url,**kwargs):ifself._cw.selected(url):try:kwargs['klass']+=' selected'exceptKeyError:kwargs['klass']='selected'returnLink(url,title,**kwargs)defseparator(self):returnSeparator()@deprecated('[3.10] use action_link() / link()')defbox_action(self,action):# XXX action_linkreturnself.build_link(self._cw._(action.title),action.url())@deprecated('[3.10] use action_link() / link()')defbuild_link(self,title,url,**kwargs):ifself._cw.selected(url):try:kwargs['klass']+=' selected'exceptKeyError:kwargs['klass']='selected'returntags.a(title,href=url,**kwargs)classEntityCtxComponent(CtxComponent):"""base class for boxes related to a single entity"""__select__=CtxComponent.__select__&non_final_entity()&one_line_rset()context='incontext'contextual=Truedef__init__(self,*args,**kwargs):super(EntityCtxComponent,self).__init__(*args,**kwargs)try:entity=kwargs['entity']exceptKeyError:entity=self.cw_rset.get_entity(self.cw_rowor0,self.cw_color0)self.entity=entitydeflayout_select_args(self):args=super(EntityCtxComponent,self).layout_select_args()args['entity']=self.entityreturnargs@propertydefdomid(self):returndomid(self.__regid__)+unicode(self.entity.eid)deflazy_view_holder(self,w,entity,oid,registry='views'):"""add a holder and return an url that may be used to replace this holder by the html generate by the view specified by registry and identifier. Registry defaults to 'views'. """holderid='%sHolder'%self.domidw(u'<div id="%s"></div>'%holderid)params=self.cw_extra_kwargs.copy()params.pop('view',None)params.pop('entity',None)form=params.pop('formparams',{})form['pageid']=self._cw.pageidifentity.has_eid():eid=entity.eidelse:eid=Noneform['etype']=entity.__regid__form['tempEid']=entity.eidargs=[json_dumps(x)forxin(registry,oid,eid,params)]returnself._cw.ajax_replace_url(holderid,fname='render',arg=args,**form)# high level abstract classes ##################################################classRQLCtxComponent(CtxComponent):"""abstract box for boxes displaying the content of a rql query not related to the current result set. Notice that this class's init_rendering implemention is overwriting context result set (eg `cw_rset`) with the result set returned by execution of `to_display_rql()`. """rql=Nonedefto_display_rql(self):"""return arguments to give to self._cw.execute, as a tuple, to build the result set to be displayed by this box. """assertself.rqlisnotNone,self.__regid__return(self.rql,)definit_rendering(self):super(RQLCtxComponent,self).init_rendering()self.cw_rset=self._cw.execute(*self.to_display_rql())ifnotself.cw_rset:raiseEmptyComponent()defrender_body(self,w):rset=self.cw_rsetiflen(rset[0])==2:items=[]fori,(eid,label)inenumerate(rset):entity=rset.get_entity(i,0)items.append(self.link(label,entity.absolute_url()))else:items=[self.link(e.dc_title(),e.absolute_url())foreinrset.entities()]self.render_items(w,items)classEditRelationMixIn(ReloadableMixIn):defbox_item(self,entity,etarget,rql,label):"""builds HTML link to edit relation between `entity` and `etarget`"""args={role(self)[0]:entity.eid,target(self)[0]:etarget.eid}url=self._cw.user_rql_callback((rql,args))# for each target, provide a link to edit the relationreturnu'[<a href="%s" class="action">%s</a>] %s'%(xml_escape(url),label,etarget.view('incontext'))defrelated_boxitems(self,entity):rql='DELETE S %s O WHERE S eid %%(s)s, O eid %%(o)s'%self.rtypereturn[self.box_item(entity,etarget,rql,u'-')foretargetinself.related_entities(entity)]defrelated_entities(self,entity):returnentity.related(self.rtype,role(self),entities=True)defunrelated_boxitems(self,entity):rql='SET S %s O WHERE S eid %%(s)s, O eid %%(o)s'%self.rtypereturn[self.box_item(entity,etarget,rql,u'+')foretargetinself.unrelated_entities(entity)]defunrelated_entities(self,entity):"""returns the list of unrelated entities, using the entity's appropriate vocabulary function """skip=set(unicode(e.eid)foreinentity.related(self.rtype,role(self),entities=True))skip.add(None)skip.add(INTERNAL_FIELD_VALUE)filteretype=getattr(self,'etype',None)entities=[]form=self._cw.vreg['forms'].select('edition',self._cw,rset=self.cw_rset,row=self.cw_rowor0)field=form.field_by_name(self.rtype,role(self),entity.e_schema)for_,eidinfield.vocabulary(form):ifeidnotinskip:entity=self._cw.entity_from_eid(eid)iffilteretypeisNoneorentity.__regid__==filteretype:entities.append(entity)returnentities# XXX should be a view usable using uicfgclassEditRelationCtxComponent(EditRelationMixIn,EntityCtxComponent):"""base class for boxes which let add or remove entities linked by a given relation subclasses should define at least id, rtype and target class attributes. """defrender_title(self,w):w(display_name(self._cw,self.rtype,role(self),context=self.entity.__regid__))defrender_body(self,w):self._cw.add_js('cubicweb.ajax.js')related=self.related_boxitems(self.entity)unrelated=self.unrelated_boxitems(self.entity)self.items.extend(related)ifrelatedandunrelated:self.items.append(u'<hr class="boxSeparator"/>')self.items.extend(unrelated)self.render_items(w)classAjaxEditRelationCtxComponent(EntityCtxComponent):__select__=EntityCtxComponent.__select__&(partial_relation_possible(action='add')|partial_has_related_entities())# view used to display related enttiesitem_vid='incontext'# values separator when multiple values are allowedseparator=','# msgid of the message to display when some new relation has been added/removedadded_msg=Noneremoved_msg=None# class attributes below *must* be set in concret classes (additionaly to# rtype / role [/ target_etype]. They should correspond to js_* methods on# the json controller# function(eid)# -> expected to return a list of values to display as input selector# vocabularyfname_vocabulary=None# function(eid, value)# -> handle the selector's input (eg create necessary entities and/or# relations). If the relation is multiple, you'll get a list of value, else# a single string value.fname_validate=None# function(eid, linked entity eid)# -> remove the relationfname_remove=Nonedef__init__(self,*args,**kwargs):super(AjaxEditRelationCtxComponent,self).__init__(*args,**kwargs)self.rdef=self.entity.e_schema.rdef(self.rtype,self.role,self.target_etype)defrender_title(self,w):w(self.rdef.rtype.display_name(self._cw,self.role,context=self.entity.__regid__))defrender_body(self,w):req=self._cwentity=self.entityrelated=entity.related(self.rtype,self.role)ifself.role=='subject':mayadd=self.rdef.has_perm(req,'add',fromeid=entity.eid)maydel=self.rdef.has_perm(req,'delete',fromeid=entity.eid)else:mayadd=self.rdef.has_perm(req,'add',toeid=entity.eid)maydel=self.rdef.has_perm(req,'delete',toeid=entity.eid)ifmayaddormaydel:req.add_js(('jquery.ui.js','cubicweb.widgets.js'))req.add_js(('cubicweb.ajax.js','cubicweb.ajax.box.js'))req.add_css('jquery.ui.css')_=req._ifrelated:w(u'<table class="ajaxEditRelationTable">')forrentityinrelated.entities():# for each related entity, provide a link to remove the relationsubview=rentity.view(self.item_vid)ifmaydel:jscall=unicode(js.ajaxBoxRemoveLinkedEntity(self.__regid__,entity.eid,rentity.eid,self.fname_remove,self.removed_msgand_(self.removed_msg)))w(u'<tr><td class="dellink">[<a href="javascript: %s">-</a>]</td>''<td class="entity"> %s</td></tr>'%(xml_escape(jscall),subview))else:w(u'<tr><td class="entity">%s</td></tr>'%(subview))w(u'</table>')else:w(_('no related entity'))ifmayadd:multiple=self.rdef.role_cardinality(self.role)in'*+'w(u'<table><tr><td>')jscall=unicode(js.ajaxBoxShowSelector(self.__regid__,entity.eid,self.fname_vocabulary,self.fname_validate,self.added_msgand_(self.added_msg),_(stdmsgs.BUTTON_OK[0]),_(stdmsgs.BUTTON_CANCEL[0]),multipleandself.separator))w('<a class="button sglink" href="javascript: %s">%s</a>'%(xml_escape(jscall),multipleand_('add_relation')or_('update_relation')))w(u'</td><td>')w(u'<div id="%sHolder"></div>'%self.domid)w(u'</td></tr></table>')classRelatedObjectsCtxComponent(EntityCtxComponent):"""a contextual component to display entities related to another"""__select__=EntityCtxComponent.__select__&partial_has_related_entities()context='navcontentbottom'rtype=Nonerole='subject'vid='list'defrender_body(self,w):rset=self.entity.related(self.rtype,role(self))self._cw.view(self.vid,rset,w=w)# old contextual components, deprecated ########################################classEntityVComponent(Component):"""abstract base class for additinal components displayed in content headers and footer according to: * the displayed entity's type * a context (currently 'header' or 'footer') it should be configured using .accepts, .etype, .rtype, .target and .context class attributes """__metaclass__=class_deprecated__deprecation_warning__='[3.10] *VComponent classes are deprecated, use *CtxComponent instead (%(cls)s)'__registry__='ctxcomponents'__select__=one_line_rset()cw_property_defs={_('visible'):dict(type='Boolean',default=True,help=_('display the component or not')),_('order'):dict(type='Int',default=99,help=_('display order of the component')),_('context'):dict(type='String',default='navtop',vocabulary=(_('navtop'),_('navbottom'),_('navcontenttop'),_('navcontentbottom'),_('ctxtoolbar')),help=_('context where this component should be displayed')),}context='navcontentbottom'defcall(self,view=None):ifself.cw_rsetisNone:self.entity_call(self.cw_extra_kwargs.pop('entity'))else:self.cell_call(0,0,view=view)defcell_call(self,row,col,view=None):self.entity_call(self.cw_rset.get_entity(row,col),view=view)defentity_call(self,entity,view=None):raiseNotImplementedError()classRelatedObjectsVComponent(EntityVComponent):"""a section to display some related entities"""__select__=EntityVComponent.__select__&partial_has_related_entities()vid='list'defrql(self):"""override this method if you want to use a custom rql query"""returnNonedefcell_call(self,row,col,view=None):rql=self.rql()ifrqlisNone:entity=self.cw_rset.get_entity(row,col)rset=entity.related(self.rtype,role(self))else:eid=self.cw_rset[row][col]rset=self._cw.execute(self.rql(),{'x':eid})ifnotrset.rowcount:returnself.w(u'<div class="%s">'%self.cssclass)self.w(u'<h4>%s</h4>\n'%self._cw._(self.title).capitalize())self.wview(self.vid,rset)self.w(u'</div>')VComponent=class_renamed('VComponent',Component,'[3.2] VComponent is deprecated, use Component')SingletonVComponent=class_renamed('SingletonVComponent',Component,'[3.2] SingletonVComponent is deprecated, use ''Component and explicit registration control')