"""generic boxes for CubicWeb web client:* actions box* possible views boxadditional (disabled by default) boxes* schema box* startup views box:organization: Logilab:copyright: 2001-2009 LOGILAB S.A. (Paris, FRANCE), license is LGPL v2.:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr:license: GNU Lesser General Public License, v2.1 - http://www.gnu.org/licenses"""__docformat__="restructuredtext en"_=unicodefromlogilab.mtconverterimportxml_escapefromcubicweb.selectorsimportmatch_user_groups,non_final_entityfromcubicweb.viewimportEntityViewfromcubicweb.schemaimportdisplay_namefromcubicweb.web.htmlwidgetsimportBoxWidget,BoxMenu,BoxHtml,RawBoxItemfromcubicweb.webimportuicfgfromcubicweb.web.boximportBoxTemplateclassEditBox(BoxTemplate):""" box with all actions impacting the entity displayed: edit, copy, delete change state, add related entities """id='edit_box'__select__=BoxTemplate.__select__&non_final_entity()title=_('actions')order=2# class attributes below are actually stored in the uicfg module since we# don't want them to be reloadedappearsin_addmenu=uicfg.actionbox_appearsin_addmenudefcall(self,view=None,**kwargs):_=self.req._title=_(self.title)ifself.rset:etypes=self.rset.column_types(0)iflen(etypes)==1:plural=self.rset.rowcount>1and'plural'or''etypelabel=display_name(self.req,iter(etypes).next(),plural)title=u'%s - %s'%(title,etypelabel.lower())box=BoxWidget(title,self.id,_class="greyBoxFrame")# build list of actionsactions=self.vreg['actions'].possible_actions(self.req,self.rset,view=view)add_menu=BoxMenu(_('add'))# 'addrelated' categoryother_menu=BoxMenu(_('more actions'))# 'moreactions' categorysearchstate=self.req.search_state[0]forcategory,menuin(('mainactions',box),('addrelated',add_menu),('moreactions',other_menu)):foractioninactions.get(category,()):menu.append(self.box_action(action))ifself.rsetandself.rset.rowcount==1and \notself.schema[self.rset.description[0][0]].is_final()and \searchstate=='normal':entity=self.rset.get_entity(0,0)#entity.complete()ifadd_menu.items:self.info('explicit actions defined, ignoring potential rtags for %s',entity.e_schema)else:# some addrelated actions may be specified but no one is selectable# in which case we should not fallback to schema_actions. The proper# way to avoid this is to override add_related_schemas() on the# entity class to return an empty listforactioninself.schema_actions(entity):add_menu.append(action)self.workflow_actions(entity,box)ifbox.is_empty()andnotother_menu.is_empty():box.items=other_menu.itemsother_menu.items=[]self.add_submenu(box,add_menu,_('add'))self.add_submenu(box,other_menu)ifnotbox.is_empty():box.render(self.w)defadd_submenu(self,box,submenu,label_prefix=None):iflen(submenu.items)==1:boxlink=submenu.items[0]iflabel_prefix:boxlink.label=u'%s%s'%(label_prefix,boxlink.label)box.append(boxlink)elifsubmenu.items:box.append(submenu)defschema_actions(self,entity):user=self.req.useractions=[]_=self.req._eschema=entity.e_schemaforrschema,teschema,xinself.add_related_schemas(entity):ifx=='subject':label='add %s%s%s%s'%(eschema,rschema,teschema,x)url=self.linkto_url(entity,rschema,teschema,'object')else:label='add %s%s%s%s'%(teschema,rschema,eschema,x)url=self.linkto_url(entity,rschema,teschema,'subject')actions.append(self.mk_action(_(label),url))returnactionsdefadd_related_schemas(self,entity):"""this is actually used ui method to generate 'addrelated' actions from the schema. If you're using explicit 'addrelated' actions for an entity types, you should probably overrides this method to return an empty list else you may get some unexpected actions. """req=self.reqeschema=entity.e_schemaforrole,rschemasin(('subject',eschema.subject_relations()),('object',eschema.object_relations())):forrschemainrschemas:ifrschema.is_final():continue# check the relation can be added as well# XXX consider autoform_permissions_overrides?ifrole=='subject'andnotrschema.has_perm(req,'add',fromeid=entity.eid):continueifrole=='object'andnotrschema.has_perm(req,'add',toeid=entity.eid):continue# check the target types can be added as wellforteschemainrschema.targets(eschema,role):ifnotself.appearsin_addmenu.etype_get(eschema,rschema,role,teschema):continueifteschema.has_local_role('add')orteschema.has_perm(req,'add'):yieldrschema,teschema,roledefworkflow_actions(self,entity,box):if'in_state'inentity.e_schema.subject_relations()andentity.in_state:_=self.req._state=entity.in_state[0]menu_title=u'%s: %s'%(_('state'),state.view('text'))menu_items=[]fortrinstate.transitions(entity):url=entity.absolute_url(vid='statuschange',treid=tr.eid)menu_items.append(self.mk_action(_(tr.name),url))wfurl=self.build_url('cwetype/%s'%entity.e_schema,vid='workflow')menu_items.append(self.mk_action(_('view workflow'),wfurl))wfurl=entity.absolute_url(vid='wfhistory')menu_items.append(self.mk_action(_('view history'),wfurl))box.append(BoxMenu(menu_title,menu_items))returnNonedeflinkto_url(self,entity,rtype,etype,target):returnself.build_url(vid='creation',etype=etype,__linkto='%s:%s:%s'%(rtype,entity.eid,target),__redirectpath=entity.rest_path(),# should not be url quoted!__redirectvid=self.req.form.get('vid',''))classSearchBox(BoxTemplate):"""display a box with a simple search form"""id='search_box'visible=True# enabled by defaulttitle=_('search')order=0formdef=u"""<form action="%s"><table id="tsearch"><tr><td><input id="norql" type="text" accesskey="q" tabindex="%s" title="search text" value="%s" name="rql" /><input type="hidden" name="__fromsearchbox" value="1" /><input type="hidden" name="subvid" value="tsearch" /></td><td><input tabindex="%s" type="submit" id="rqlboxsubmit" class="rqlsubmit" value="" /></td></tr></table></form>"""defcall(self,view=None,**kwargs):req=self.reqifreq.form.pop('__fromsearchbox',None):rql=req.form.get('rql','')else:rql=''form=self.formdef%(req.build_url('view'),req.next_tabindex(),xml_escape(rql),req.next_tabindex())title=u"""<span onclick="javascript: toggleVisibility('rqlinput')">%s</span>"""%req._(self.title)box=BoxWidget(title,self.id,_class="searchBoxFrame",islist=False,escape=False)box.append(BoxHtml(form))box.render(self.w)# boxes disabled by default ###################################################classPossibleViewsBox(BoxTemplate):"""display a box containing links to all possible views"""id='possible_views_box'__select__=BoxTemplate.__select__&match_user_groups('users','managers')visible=Falsetitle=_('possible views')order=10defcall(self,**kwargs):box=BoxWidget(self.req._(self.title),self.id)views=[vforvinself.vreg['views'].possible_views(self.req,rset=self.rset)ifv.category!='startupview']forcategory,viewsinself.sort_actions(views):menu=BoxMenu(category)forviewinviews:menu.append(self.box_action(view))box.append(menu)ifnotbox.is_empty():box.render(self.w)classStartupViewsBox(BoxTemplate):"""display a box containing links to all startup views"""id='startup_views_box'visible=False# disabled by defaulttitle=_('startup views')order=70defcall(self,**kwargs):box=BoxWidget(self.req._(self.title),self.id)forviewinself.vreg['views'].possible_views(self.req,None):ifview.category=='startupview':box.append(self.box_action(view))ifnotbox.is_empty():box.render(self.w)# helper classes ##############################################################classSideBoxView(EntityView):"""helper view class to display some entities in a sidebox"""id='sidebox'defcall(self,boxclass='sideBox',title=u''):"""display a list of entities by calling their <item_vid> view"""iftitle:self.w(u'<div class="sideBoxTitle"><span>%s</span></div>'%title)self.w(u'<div class="%s"><div class="sideBoxBody">'%boxclass)# if not too much entities, show them all in a listmaxrelated=self.req.property_value('navigation.related-limit')ifself.rset.rowcount<=maxrelated:iflen(self.rset)==1:self.wview('incontext',self.rset,row=0)elif1<len(self.rset)<5:self.wview('csv',self.rset)else:self.wview('simplelist',self.rset)# else show links to display related entitieselse:self.rset.limit(maxrelated)rql=self.rset.printable_rql(encoded=False)self.wview('simplelist',self.rset)self.w(u'[<a href="%s">%s</a>]'%(self.build_url(rql=rql),self.req._('see them all')))self.w(u'</div>\n</div>\n')