# 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/>."""Generic boxes for CubicWeb web client:* actions box* search boxAdditional boxes (disabled by default):* schema box* possible views box* startup views box"""__docformat__="restructuredtext en"_=unicodefromwarningsimportwarnfromlogilab.mtconverterimportxml_escapefromlogilab.common.deprecationimportclass_deprecatedfromcubicwebimportUnauthorizedfromcubicweb.predicatesimport(match_user_groups,match_kwargs,non_final_entity,nonempty_rset,match_context,contextual)fromcubicweb.utilsimportwrap_on_writefromcubicweb.viewimportEntityViewfromcubicweb.schemaimportdisplay_namefromcubicweb.webimportcomponent,box,htmlwidgets# XXX bw compat, some cubes import this class from hereBoxTemplate=box.BoxTemplateBoxHtml=htmlwidgets.BoxHtmlclassEditBox(component.CtxComponent):""" box with all actions impacting the entity displayed: edit, copy, delete change state, add related entities... """__regid__='edit_box'title=_('actions')order=2contextual=True__select__=component.CtxComponent.__select__&non_final_entity()definit_rendering(self):super(EditBox,self).init_rendering()_=self._cw._self._menus_in_order=[]self._menus_by_id={}# build list of actionsactions=self._cw.vreg['actions'].possible_actions(self._cw,self.cw_rset,**self.cw_extra_kwargs)other_menu=self._get_menu('moreactions',_('more actions'))forcategory,defaultmenuin(('mainactions',self),('moreactions',other_menu),('addrelated',None)):foractioninactions.get(category,()):ifaction.submenu:menu=self._get_menu(action.submenu)else:menu=defaultmenuaction.fill_menu(self,menu)# if we've nothing but actions in the other_menu, add them directly into the boxifnotself.itemsandlen(self._menus_by_id)==1andnotother_menu.is_empty():self.items=other_menu.itemselse:# ensure 'more actions' menu appears lastself._menus_in_order.remove(other_menu)self._menus_in_order.append(other_menu)forsubmenuinself._menus_in_order:self.add_submenu(self,submenu)ifnotself.items:raisecomponent.EmptyComponent()defrender_title(self,w):title=self._cw._(self.title)ifself.cw_rset:etypes=self.cw_rset.column_types(0)iflen(etypes)==1:plural=self.cw_rset.rowcount>1and'plural'or''etypelabel=display_name(self._cw,iter(etypes).next(),plural)title=u'%s - %s'%(title,etypelabel.lower())w(title)defrender_body(self,w):self.render_items(w)def_get_menu(self,id,title=None,label_prefix=None):try:returnself._menus_by_id[id]exceptKeyError:iftitleisNone:title=self._cw._(id)self._menus_by_id[id]=menu=htmlwidgets.BoxMenu(title)menu.label_prefix=label_prefixself._menus_in_order.append(menu)returnmenudefadd_submenu(self,box,submenu,label_prefix=None):appendanyway=getattr(submenu,'append_anyway',False)iflen(submenu.items)==1andnotappendanyway:boxlink=submenu.items[0]ifsubmenu.label_prefix:# XXX iirkifhasattr(boxlink,'label'):boxlink.label=u'%s%s'%(submenu.label_prefix,boxlink.label)else:boxlink=u'%s%s'%(submenu.label_prefix,boxlink)box.append(boxlink)elifsubmenu.items:box.append(submenu)elifappendanyway:box.append(xml_escape(submenu.label))classSearchBox(component.CtxComponent):"""display a box with a simple search form"""__regid__='search_box'title=_('search')order=0formdef=u"""<form action="%(action)s"><table id="%(id)s"><tr><td><input class="norql" type="text" accesskey="q" tabindex="%(tabindex1)s" title="search text" value="%(value)s" name="rql" /><input type="hidden" name="__fromsearchbox" value="1" /><input type="hidden" name="subvid" value="tsearch" /></td><td><input tabindex="%(tabindex2)s" type="submit" class="rqlsubmit" value="" /> </td></tr></table> </form>"""defrender_title(self,w):w(u"""<span onclick="javascript: toggleVisibility('rqlinput')">%s</span>"""%self._cw._(self.title))defrender_body(self,w):ifself._cw.form.pop('__fromsearchbox',None):rql=self._cw.form.get('rql','')else:rql=''tabidx1=self._cw.next_tabindex()tabidx2=self._cw.next_tabindex()w(self.formdef%{'action':self._cw.build_url('view'),'value':xml_escape(rql),'id':self.cw_extra_kwargs.get('domid','tsearch'),'tabindex1':tabidx1,'tabindex2':tabidx2})# boxes disabled by default ###################################################classPossibleViewsBox(component.CtxComponent):"""display a box containing links to all possible views"""__regid__='possible_views_box'contextual=Truetitle=_('possible views')order=10visible=False# disabled by defaultdefinit_rendering(self):self.views=[vforvinself._cw.vreg['views'].possible_views(self._cw,rset=self.cw_rset)ifv.category!='startupview']ifnotself.views:raisecomponent.EmptyComponent()self.items=[]defrender_body(self,w):forcategory,viewsinbox.sort_by_category(self.views):menu=htmlwidgets.BoxMenu(self._cw._(category),ident=category)forviewinviews:menu.append(self.action_link(view))self.append(menu)self.render_items(w)classStartupViewsBox(PossibleViewsBox):"""display a box containing links to all startup views"""__regid__='startup_views_box'contextual=Falsetitle=_('startup views')order=70visible=False# disabled by defaultdefinit_rendering(self):self.views=[vforvinself._cw.vreg['views'].possible_views(self._cw)ifv.category=='startupview']ifnotself.views:raisecomponent.EmptyComponent()self.items=[]classRsetBox(component.CtxComponent):"""helper view class to display an rset in a sidebox"""__select__=nonempty_rset()&match_kwargs('title','vid')__regid__='rsetbox'cw_property_defs={}context='incontext'@propertydefdomid(self):returnsuper(RsetBox,self).domid+unicode(abs(id(self)))+unicode(abs(id(self.cw_rset)))defrender_title(self,w):w(self.cw_extra_kwargs['title'])defrender_body(self,w):if'dispctrl'inself.cw_extra_kwargs:# XXX do not modify dispctrl!self.cw_extra_kwargs['dispctrl'].setdefault('subvid','outofcontext')self.cw_extra_kwargs['dispctrl'].setdefault('use_list_limit',1)self._cw.view(self.cw_extra_kwargs['vid'],self.cw_rset,w=w,initargs=self.cw_extra_kwargs)# helper classes ##############################################################classSideBoxView(EntityView):"""helper view class to display some entities in a sidebox"""__metaclass__=class_deprecated__deprecation_warning__='[3.10] SideBoxView is deprecated, use RsetBox instead (%(cls)s)'__regid__='sidebox'defcall(self,title=u'',**kwargs):"""display a list of entities by calling their <item_vid> view"""if'dispctrl'inself.cw_extra_kwargs:# XXX do not modify dispctrl!self.cw_extra_kwargs['dispctrl'].setdefault('subvid','outofcontext')self.cw_extra_kwargs['dispctrl'].setdefault('use_list_limit',1)iftitle:self.cw_extra_kwargs['title']=titleself.cw_extra_kwargs.setdefault('context','incontext')box=self._cw.vreg['ctxcomponents'].select('rsetbox',self._cw,rset=self.cw_rset,vid='autolimited',**self.cw_extra_kwargs)box.render(self.w)classContextualBoxLayout(component.Layout):__select__=match_context('incontext','left','right')&contextual()# predefined class in cubicweb.css: contextualBox | contextFreeBoxcssclass='contextualBox'defrender(self,w):ifself.init_rendering():view=self.cw_extra_kwargs['view']w(u'<div class="%s%s" id="%s">'%(self.cssclass,view.cssclass,view.domid))withwrap_on_write(w,u'<div class="boxTitle"><span>',u'</span></div>')aswow:view.render_title(wow)w(u'<div class="boxBody">')view.render_body(w)# boxFooter div is a CSS place holder (for shadow for example)w(u'</div><div class="boxFooter"></div></div>\n')classContextFreeBoxLayout(ContextualBoxLayout):__select__=match_context('incontext','left','right')&~contextual()cssclass='contextFreeBox'