web/views/primary.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
--- a/web/views/primary.py	Mon Jan 04 18:40:30 2016 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,444 +0,0 @@
-# copyright 2003-2014 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.PrimaryView
-
-Views 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 the
-view using one of `primaryview_display_ctrl` or `reledit_ctrl` to use one of the
-views describled below. For instance:
-
-.. sourcecode:: python
-
-    primaryview_display_ctrl.tag_attribute(('Foo', 'bar'), {'vid': 'attribute'})
-
-
-.. autoclass:: AttributeView
-.. autoclass:: URLAttributeView
-.. autoclass:: VerbatimAttributeView
-"""
-
-__docformat__ = "restructuredtext en"
-from cubicweb import _
-
-from warnings import warn
-
-from logilab.common.deprecation import deprecated
-from logilab.mtconverter import xml_escape
-
-from cubicweb import Unauthorized, NoSelectableObject
-from cubicweb.utils import support_args
-from cubicweb.predicates import match_kwargs, match_context
-from cubicweb.view import EntityView
-from cubicweb.schema import META_RTYPES, VIRTUAL_RTYPES, display_name
-from cubicweb.web import component
-from cubicweb.web.views import uicfg
-
-
-class PrimaryView(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 = True
-    show_rel_label = True
-    rsection = None
-    display_ctrl = None
-    main_related_section = True
-
-    def html_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 []
-
-    def entity_call(self, entity, **kwargs):
-        entity.complete()
-        uicfg_reg = self._cw.vreg['uicfg']
-        if self.rsection is None:
-            self.rsection = uicfg_reg.select('primaryview_section',
-                                             self._cw, entity=entity)
-        if self.display_ctrl is None:
-            self.display_ctrl = uicfg_reg.select('primaryview_display_ctrl',
-                                                 self._cw, entity=entity)
-        self.render_entity(entity)
-
-    def render_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 itself
-        if self.is_primary():
-            boxes = self._prepare_side_boxes(entity)
-        else:
-            boxes = None
-        if boxes or hasattr(self, 'render_side_related'):
-            self.w(u'<table width="100%"><tr><td style="width: 75%">')
-
-        self.w(u'<div class="mainInfo">')
-        self.content_navigation_components('navcontenttop')
-        self.render_entity_attributes(entity)
-        if self.main_related_section:
-            self.render_entity_relations(entity)
-        self.content_navigation_components('navcontentbottom')
-        self.w(u'</div>')
-        # side boxes
-        if boxes or hasattr(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>')
-
-    def content_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)
-        for comp in self._cw.vreg['ctxcomponents'].poss_visible_objects(
-            self._cw, rset=self.cw_rset, view=self, context=context):
-            # XXX bw compat code
-            try:
-                comp.render(w=self.w, row=self.cw_row, view=self)
-            except TypeError:
-                comp.render(w=self.w)
-        self.w(u'</div>')
-
-    def render_entity_title(self, entity):
-        """Renders the entity title, by default using entity's
-        :meth:`dc_title()` method.
-        """
-        title = xml_escape(entity.dc_title())
-        if title:
-            if self.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))
-
-    def render_entity_toolbox(self, entity):
-        self.content_navigation_components('ctxtoolbar')
-
-    def render_entity_attributes(self, entity):
-        """Renders all attributes and relations in the 'attributes' section. 
-        """
-        display_attributes = []
-        for rschema, _, role, dispctrl in self._section_def(entity, 'attributes'):
-            vid = dispctrl.get('vid', 'reledit')
-            if rschema.final or vid == 'reledit' or dispctrl.get('rtypevid'):
-                value = entity.view(vid, rtype=rschema.type, role=role,
-                                    initargs={'dispctrl': dispctrl})
-            else:
-                rset = self._relation_rset(entity, rschema, role, dispctrl)
-                if rset:
-                    value = self._cw.view(vid, rset)
-                else:
-                    value = None
-            if value is not None and value != '':
-                display_attributes.append( (rschema, role, dispctrl, value) )
-        if display_attributes:
-            self.w(u'<table>')
-            for rschema, role, dispctrl, value in display_attributes:
-                label = self._rel_label(entity, rschema, role, dispctrl)
-                self.render_attribute(label, value, table=True)
-            self.w(u'</table>')
-
-    def render_attribute(self, label, value, table=False):
-        self.field(label, value, tr=False, table=table)
-
-    def render_entity_relations(self, entity):
-        """Renders all relations in the 'relations' section."""
-        defaultlimit = self._cw.property_value('navigation.related-limit')
-        for rschema, tschemas, role, dispctrl in self._section_def(entity, 'relations'):
-            if rschema.final or dispctrl.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)
-                except NoSelectableObject:
-                    continue
-                value = rview.render(row=entity.cw_row, col=entity.cw_col,
-                                     rtype=rschema.type, role=role)
-            else:
-                vid = dispctrl.get('vid', 'autolimited')
-                limit = dispctrl.get('limit', defaultlimit) if vid == 'autolimited' else None
-                if limit is not None:
-                    limit += 1 # need one more so the view can check if there is more than the limit
-                rset = self._relation_rset(entity, rschema, role, dispctrl, limit=limit)
-                if not rset:
-                    continue
-                try:
-                    rview = self._cw.vreg['views'].select(
-                        vid, self._cw, rset=rset, dispctrl=dispctrl)
-                except NoSelectableObject:
-                    continue
-                value = rview.render()
-            label = self._rel_label(entity, rschema, role, dispctrl)
-            self.render_relation(label, value)
-
-    def render_relation(self, label, value):
-        self.w(u'<div class="section">')
-        if label:
-            self.w(u'<h4>%s</h4>' % label)
-        self.w(value)
-        self.w(u'</div>')
-
-    def render_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.
-        """
-        for box in boxes:
-            try:
-                box.render(w=self.w, row=self.cw_row)
-            except TypeError:
-                box.render(w=self.w)
-
-    def _prepare_side_boxes(self, entity):
-        sideboxes = []
-        boxesreg = self._cw.vreg['ctxcomponents']
-        defaultlimit = self._cw.property_value('navigation.related-limit')
-        for rschema, tschemas, role, dispctrl in self._section_def(entity, 'sideboxes'):
-            vid = dispctrl.get('vid', 'autolimited')
-            limit = defaultlimit if vid == 'autolimited' else None
-            rset = self._relation_rset(entity, rschema, role, dispctrl, limit=limit)
-            if not rset:
-                continue
-            label = self._rel_label(entity, rschema, role, dispctrl)
-            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 bisect
-        def get_order(x):
-            if 'order' in x.cw_property_defs:
-                return x.cw_propval('order')
-            # default to 9999 so view boxes occurs after component boxes
-            return x.cw_extra_kwargs.get('dispctrl', {}).get('order', 9999)
-        return sorted(sideboxes, key=get_order)
-
-    def _section_def(self, entity, where):
-        rdefs = []
-        eschema = entity.e_schema
-        for rschema, tschemas, role in eschema.relation_definitions(True):
-            if rschema in VIRTUAL_RTYPES:
-                continue
-            matchtschemas = []
-            for tschema in tschemas:
-                section = self.rsection.etype_get(eschema, rschema, role,
-                                                  tschema)
-                if section == where:
-                    matchtschemas.append(tschema)
-            if matchtschemas:
-                dispctrl = self.display_ctrl.etype_get(eschema, rschema, role, '*')
-                rdefs.append( (rschema, matchtschemas, role, dispctrl) )
-        return sorted(rdefs, key=lambda x: x[-1]['order'])
-
-    def _relation_rset(self, entity, rschema, role, dispctrl, limit=None):
-        try:
-            rset = entity.related(rschema.type, role, limit=limit)
-        except Unauthorized:
-            return
-        if 'filter' in dispctrl:
-            rset = dispctrl['filter'](rset)
-        return rset
-
-    def _rel_label(self, entity, rschema, role, dispctrl):
-        if rschema.final:
-            showlabel = dispctrl.get('showlabel', self.show_attr_label)
-        else:
-            showlabel = dispctrl.get('showlabel', self.show_rel_label)
-        if showlabel:
-            if dispctrl.get('label'):
-                label = self._cw._(dispctrl['label'])
-            else:
-                label = display_name(self._cw, rschema.type, role,
-                                     context=entity.cw_etype)
-            return label
-        return u''
-
-
-class RelatedView(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'
-
-    def call(self, **kwargs):
-        if 'dispctrl' in self.cw_extra_kwargs:
-            if 'limit' in self.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 = None
-            subvid = 'incontext'
-        if limit is None or self.cw_rset.rowcount <= limit:
-            if self.cw_rset.rowcount == 1:
-                self.wview(subvid, self.cw_rset, row=0)
-            elif list_limit is None or 1 < 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 entities
-        else:
-            rql = self.cw_rset.printable_rql()
-            rset = self.cw_rset.limit(limit) # remove extra entity
-            if list_limit is None:
-                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>')
-
-
-class AttributeView(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')
-
-    def entity_call(self, entity, rtype, role='subject', **kwargs):
-        if self._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)
-            if rset:
-                self.wview('autolimited', rset, initargs={'dispctrl': dispctrl})
-
-
-class URLAttributeView(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')
-
-    def entity_call(self, entity, rtype, **kwargs):
-        url = entity.printable_value(rtype)
-        if url:
-            self.w(u'<a href="%s">%s</a>' % (url, url))
-
-
-class VerbatimAttributeView(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')
-
-    def entity_call(self, entity, rtype, **kwargs):
-        value = entity.printable_value(rtype)
-        if value:
-            self.w(u'<pre>%s</pre>' % value)
-
-
-
-
-
-class ToolbarLayout(component.Layout):
-    # XXX include me in the doc
-    __select__ = match_context('ctxtoolbar')
-
-    def render(self, w):
-        if self.init_rendering():
-            self.cw_extra_kwargs['view'].render_body(w)
-
-
-## default primary ui configuration ###########################################
-
-_pvs = uicfg.primaryview_section
-for rtype in META_RTYPES:
-    _pvs.tag_subject_of(('*', rtype, '*'), 'hidden')
-    _pvs.tag_object_of(('*', rtype, '*'), 'hidden')