# 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/>."""Public API of the PrimaryView class````````````````````````````````````.. autoclass:: cubicweb.web.views.primary.PrimaryViewViews that may be used to display an entity's attribute or relation```````````````````````````````````````````````````````````````````Yoy may easily the display of an attribute or relation by simply configuring theview using one of `primaryview_display_ctrl` or `reledit_ctrl` to use one of theviews describled below. For instance:.. sourcecode:: python primaryview_display_ctrl.tag_attribute(('Foo', 'bar'), {'vid': 'attribute'}).. autoclass:: AttributeView.. autoclass:: URLAttributeView.. autoclass:: VerbatimAttributeView"""__docformat__="restructuredtext en"_=unicodefromwarningsimportwarnfromlogilab.common.deprecationimportdeprecatedfromlogilab.mtconverterimportxml_escapefromcubicwebimportUnauthorized,NoSelectableObjectfromcubicweb.utilsimportsupport_argsfromcubicweb.predicatesimportmatch_kwargs,match_contextfromcubicweb.viewimportEntityViewfromcubicweb.schemaimportMETA_RTYPES,VIRTUAL_RTYPES,display_namefromcubicweb.webimportuicfg,componentclassPrimaryView(EntityView):""" The basic layout of a primary view is as in the :ref:`primary_view_layout` section. This layout is actually drawn by the `render_entity` method. The methods you may want to modify while customizing a ``PrimaryView`` are: .. automethod:: cubicweb.web.views.primary.PrimaryView.render_entity_title .. automethod:: cubicweb.web.views.primary.PrimaryView.render_entity_attributes .. automethod:: cubicweb.web.views.primary.PrimaryView.render_entity_relations .. automethod:: cubicweb.web.views.primary.PrimaryView.render_side_boxes The placement of relations in the relations section or in side boxes can be controlled through the :ref:`primary_view_configuration` mechanism. .. automethod:: cubicweb.web.views.primary.PrimaryView.content_navigation_components Also, please note that by setting the following attributes in your subclass, you can already customize some of the rendering: :attr:`show_attr_label` Renders the attribute label next to the attribute value if set to `True`. Otherwise, does only display the attribute value. :attr:`show_rel_label` Renders the relation label next to the relation value if set to `True`. Otherwise, does only display the relation value. :attr:`main_related_section` Renders the relations of the entity if set to `True`. A good practice is for you to identify the content of your entity type for which the default rendering does not answer your need so that you can focus on the specific method (from the list above) that needs to be modified. We do not advise you to overwrite ``render_entity`` unless you want a completely different layout. """__regid__='primary'title=_('primary')show_attr_label=Trueshow_rel_label=Truersection=uicfg.primaryview_sectiondisplay_ctrl=uicfg.primaryview_display_ctrlmain_related_section=Truedefhtml_headers(self):"""return a list of html headers (eg something to be inserted between <head> and </head> of the returned page by default primary views are indexed """return[]defentity_call(self,entity):entity.complete()self.render_entity(entity)defrender_entity(self,entity):self.render_entity_toolbox(entity)self.render_entity_title(entity)# entity's attributes and relations, excluding meta data# if the entity isn't meta itselfifself.is_primary():boxes=self._prepare_side_boxes(entity)else:boxes=Noneifboxesorhasattr(self,'render_side_related'):self.w(u'<table width="100%"><tr><td style="width: 75%">')ifhasattr(self,'render_entity_summary'):warn('[3.10] render_entity_summary method is deprecated (%s)'%self,DeprecationWarning)self.render_entity_summary(entity)# pylint: disable=E1101summary=self.summary(entity)ifsummary:warn('[3.10] summary method is deprecated (%s)'%self,DeprecationWarning)self.w(u'<div class="summary">%s</div>'%summary)self.w(u'<div class="mainInfo">')self.content_navigation_components('navcontenttop')self.render_entity_attributes(entity)ifself.main_related_section:self.render_entity_relations(entity)self.content_navigation_components('navcontentbottom')self.w(u'</div>')# side boxesifboxesorhasattr(self,'render_side_related'):self.w(u'</td><td>')self.w(u'<div class="primaryRight">')self.render_side_boxes(boxes)self.w(u'</div>')self.w(u'</td></tr></table>')defcontent_navigation_components(self,context):"""This method is applicable only for entity type implementing the interface `IPrevNext`. This interface is for entities which can be linked to a previous and/or next entity. This method will render the navigation links between entities of this type, either at the top or at the bottom of the page given the context (navcontent{top|bottom}). """self.w(u'<div class="%s">'%context)forcompinself._cw.vreg['ctxcomponents'].poss_visible_objects(self._cw,rset=self.cw_rset,view=self,context=context):# XXX bw compat codetry:comp.render(w=self.w,row=self.cw_row,view=self)exceptTypeError:comp.render(w=self.w)self.w(u'</div>')defrender_entity_title(self,entity):"""Renders the entity title, by default using entity's :meth:`dc_title()` method. """title=xml_escape(entity.dc_title())iftitle:ifself.is_primary():self.w(u'<h1>%s</h1>'%title)else:atitle=self._cw._('follow this link for more information on this %s')%entity.dc_type()self.w(u'<h4><a href="%s" title="%s">%s</a></h4>'%(entity.absolute_url(),atitle,title))defrender_entity_toolbox(self,entity):self.content_navigation_components('ctxtoolbar')@deprecated('[3.8] render_entity_metadata method is deprecated')defrender_entity_metadata(self,entity):entity.view('metadata',w=self.w)defsummary(self,entity):"""default implementation return an empty string"""returnu''defrender_entity_attributes(self,entity):"""Renders all attributes and relations in the 'attributes' section. """display_attributes=[]forrschema,_,role,dispctrlinself._section_def(entity,'attributes'):vid=dispctrl.get('vid','reledit')ifrschema.finalorvid=='reledit'ordispctrl.get('rtypevid'):value=entity.view(vid,rtype=rschema.type,role=role,initargs={'dispctrl':dispctrl})else:rset=self._relation_rset(entity,rschema,role,dispctrl)ifrset:value=self._cw.view(vid,rset)else:value=NoneifvalueisnotNoneandvalue!='':display_attributes.append((rschema,role,dispctrl,value))ifdisplay_attributes:self.w(u'<table>')forrschema,role,dispctrl,valueindisplay_attributes:# pylint: disable=E1101ifnothasattr(self,'_render_attribute'):label=self._rel_label(entity,rschema,role,dispctrl)self.render_attribute(label,value,table=True)else:warn('[3.9] _render_attribute prototype has changed and ''renamed to render_attribute, please update %s'%self.__class__,DeprecationWarning)self._render_attribute(dispctrl,rschema,value,role=role,table=True)self.w(u'</table>')defrender_attribute(self,label,value,table=False):self.field(label,value,tr=False,table=table)defrender_entity_relations(self,entity):"""Renders all relations in the 'relations' section."""forrschema,tschemas,role,dispctrlinself._section_def(entity,'relations'):ifrschema.finalordispctrl.get('rtypevid'):vid=dispctrl.get('vid','reledit')try:rview=self._cw.vreg['views'].select(vid,self._cw,rset=entity.cw_rset,row=entity.cw_row,col=entity.cw_col,dispctrl=dispctrl,rtype=rschema,role=role)exceptNoSelectableObject:continuevalue=rview.render(row=entity.cw_row,col=entity.cw_col,rtype=rschema.type,role=role)else:rset=self._relation_rset(entity,rschema,role,dispctrl)ifnotrset:continueifhasattr(self,'_render_relation'):# pylint: disable=E1101self._render_relation(dispctrl,rset,'autolimited')warn('[3.9] _render_relation prototype has changed and has ''been renamed to render_relation, please update %s'%self.__class__,DeprecationWarning)continuevid=dispctrl.get('vid','autolimited')try:rview=self._cw.vreg['views'].select(vid,self._cw,rset=rset,dispctrl=dispctrl)exceptNoSelectableObject:continuevalue=rview.render()label=self._rel_label(entity,rschema,role,dispctrl)self.render_relation(label,value)defrender_relation(self,label,value):self.w(u'<div class="section">')iflabel:self.w(u'<h4>%s</h4>'%label)self.w(value)self.w(u'</div>')defrender_side_boxes(self,boxes):"""Renders side boxes on the right side of the content. This will generate a box for each relation in the 'sidebox' section, as well as explicit box appobjects selectable in this context. """forboxinboxes:ifisinstance(box,tuple):try:label,rset,vid,dispctrl=boxexceptValueError:label,rset,vid=boxdispctrl={}warn('[3.10] box views should now be a RsetBox instance, ''please update %s'%self.__class__.__name__,DeprecationWarning)self.w(u'<div class="sideBox">')self.wview(vid,rset,title=label,initargs={'dispctrl':dispctrl})self.w(u'</div>')else:try:box.render(w=self.w,row=self.cw_row)exceptTypeError:box.render(w=self.w)def_prepare_side_boxes(self,entity):sideboxes=[]boxesreg=self._cw.vreg['ctxcomponents']forrschema,tschemas,role,dispctrlinself._section_def(entity,'sideboxes'):rset=self._relation_rset(entity,rschema,role,dispctrl)ifnotrset:continuelabel=self._rel_label(entity,rschema,role,dispctrl)vid=dispctrl.get('vid','autolimited')box=boxesreg.select('rsetbox',self._cw,rset=rset,vid=vid,title=label,dispctrl=dispctrl,context='incontext')sideboxes.append(box)sideboxes+=boxesreg.poss_visible_objects(self._cw,rset=self.cw_rset,view=self,context='incontext')# XXX since we've two sorted list, it may be worth using bisectdefget_order(x):if'order'inx.cw_property_defs:returnx.cw_propval('order')# default to 9999 so view boxes occurs after component boxesreturnx.cw_extra_kwargs.get('dispctrl',{}).get('order',9999)returnsorted(sideboxes,key=get_order)def_section_def(self,entity,where):rdefs=[]eschema=entity.e_schemaforrschema,tschemas,roleineschema.relation_definitions(True):ifrschemainVIRTUAL_RTYPES:continuematchtschemas=[]fortschemaintschemas:section=self.rsection.etype_get(eschema,rschema,role,tschema)ifsection==where:matchtschemas.append(tschema)ifmatchtschemas:dispctrl=self.display_ctrl.etype_get(eschema,rschema,role,'*')rdefs.append((rschema,matchtschemas,role,dispctrl))returnsorted(rdefs,key=lambdax:x[-1]['order'])def_relation_rset(self,entity,rschema,role,dispctrl):try:rset=entity.related(rschema.type,role)exceptUnauthorized:returnif'filter'indispctrl:rset=dispctrl['filter'](rset)returnrsetdef_rel_label(self,entity,rschema,role,dispctrl):ifrschema.final:showlabel=dispctrl.get('showlabel',self.show_attr_label)else:showlabel=dispctrl.get('showlabel',self.show_rel_label)ifshowlabel:ifdispctrl.get('label'):label=self._cw._(dispctrl['label'])else:label=display_name(self._cw,rschema.type,role,context=entity.__regid__)returnlabelreturnu''classRelatedView(EntityView):"""Display a rset, usually containing entities linked to another entity being displayed. It will try to display nicely according to the number of items in the result set. XXX include me in the doc """__regid__='autolimited'defcall(self,**kwargs):if'dispctrl'inself.cw_extra_kwargs:if'limit'inself.cw_extra_kwargs['dispctrl']:limit=self.cw_extra_kwargs['dispctrl']['limit']else:limit=self._cw.property_value('navigation.related-limit')list_limit=self.cw_extra_kwargs['dispctrl'].get('use_list_limit',5)subvid=self.cw_extra_kwargs['dispctrl'].get('subvid','incontext')else:limit=list_limit=Nonesubvid='incontext'iflimitisNoneorself.cw_rset.rowcount<=limit:ifself.cw_rset.rowcount==1:self.wview(subvid,self.cw_rset,row=0)eliflist_limitisNoneor1<self.cw_rset.rowcount<=list_limit:self.wview('csv',self.cw_rset,subvid=subvid)else:self.w(u'<div>')self.wview('simplelist',self.cw_rset,subvid=subvid)self.w(u'</div>')# else show links to display related entitieselse:rql=self.cw_rset.printable_rql()rset=self.cw_rset.limit(limit)# remove extra entityiflist_limitisNone:self.wview('csv',rset,subvid=subvid)self.w(u'[<a href="%s">%s</a>]'%(xml_escape(self._cw.build_url(rql=rql,vid=subvid)),self._cw._('see them all')))else:self.w(u'<div>')self.wview('simplelist',rset,subvid=subvid)self.w(u'[<a href="%s">%s</a>]'%(xml_escape(self._cw.build_url(rql=rql,vid=subvid)),self._cw._('see them all')))self.w(u'</div>')classAttributeView(EntityView):""":__regid__: *attribute* This view is generally used to disable the *reledit* feature. It works on both relations and attributes. """__regid__='attribute'__select__=EntityView.__select__&match_kwargs('rtype')defentity_call(self,entity,rtype,role='subject',**kwargs):ifself._cw.vreg.schema.rschema(rtype).final:self.w(entity.printable_value(rtype))else:dispctrl=uicfg.primaryview_display_ctrl.etype_get(entity.e_schema,rtype,role,'*')rset=entity.related(rtype,role)ifrset:self.wview('autolimited',rset,initargs={'dispctrl':dispctrl})classURLAttributeView(EntityView):""":__regid__: *urlattr* This view will wrap an attribute value (hence expect a string) into an '<a>' HTML tag to display a clickable link. """__regid__='urlattr'__select__=EntityView.__select__&match_kwargs('rtype')defentity_call(self,entity,rtype,**kwargs):url=entity.printable_value(rtype)ifurl:self.w(u'<a href="%s">%s</a>'%(url,url))classVerbatimAttributeView(EntityView):""":__regid__: *verbatimattr* This view will wrap an attribute value into an '<pre>' HTML tag to display arbitrary text where EOL will be respected. It usually make sense for attributes whose value is a multi-lines string where new lines matters. """__regid__='verbatimattr'__select__=EntityView.__select__&match_kwargs('rtype')defentity_call(self,entity,rtype,**kwargs):value=entity.printable_value(rtype)ifvalue:self.w(u'<pre>%s</pre>'%value)classToolbarLayout(component.Layout):# XXX include me in the doc__select__=match_context('ctxtoolbar')defrender(self,w):ifself.init_rendering():self.cw_extra_kwargs['view'].render_body(w)## default primary ui configuration ###########################################_pvs=uicfg.primaryview_sectionforrtypeinMETA_RTYPES:_pvs.tag_subject_of(('*',rtype,'*'),'hidden')_pvs.tag_object_of(('*',rtype,'*'),'hidden')