web/views/ibreadcrumbs.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
     1 # copyright 2003-2014 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     3 #
       
     4 # This file is part of CubicWeb.
       
     5 #
       
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
       
     7 # terms of the GNU Lesser General Public License as published by the Free
       
     8 # Software Foundation, either version 2.1 of the License, or (at your option)
       
     9 # any later version.
       
    10 #
       
    11 # CubicWeb is distributed in the hope that it will be useful, but WITHOUT
       
    12 # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
       
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
       
    14 # details.
       
    15 #
       
    16 # You should have received a copy of the GNU Lesser General Public License along
       
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
       
    18 """breadcrumbs components definition for CubicWeb web client"""
       
    19 
       
    20 __docformat__ = "restructuredtext en"
       
    21 from cubicweb import _
       
    22 
       
    23 from warnings import warn
       
    24 
       
    25 from six import text_type
       
    26 
       
    27 from logilab.mtconverter import xml_escape
       
    28 
       
    29 from cubicweb import tags, uilib
       
    30 from cubicweb.entity import Entity
       
    31 from cubicweb.predicates import (is_instance, one_line_rset, adaptable,
       
    32                                 one_etype_rset, multi_lines_rset, any_rset,
       
    33                                 match_form_params)
       
    34 from cubicweb.view import EntityView, EntityAdapter
       
    35 from cubicweb.web.views import basecomponents
       
    36 # don't use AnyEntity since this may cause bug with isinstance() due to reloading
       
    37 
       
    38 
       
    39 
       
    40 class IBreadCrumbsAdapter(EntityAdapter):
       
    41     """adapters for entities which can be"located" on some path to display in
       
    42     the web ui
       
    43     """
       
    44     __regid__ = 'IBreadCrumbs'
       
    45     __select__ = is_instance('Any', accept_none=False)
       
    46 
       
    47     def parent_entity(self):
       
    48         itree = self.entity.cw_adapt_to('ITree')
       
    49         if itree is not None:
       
    50             return itree.parent()
       
    51         return None
       
    52 
       
    53     def breadcrumbs(self, view=None, recurs=None):
       
    54         """return a list containing some:
       
    55 
       
    56         * tuple (url, label)
       
    57         * entity
       
    58         * simple label string
       
    59 
       
    60         defining path from a root to the current view
       
    61 
       
    62         the main view is given as argument so breadcrumbs may vary according to
       
    63         displayed view (may be None). When recursing on a parent entity, the
       
    64         `recurs` argument should be a set of already traversed nodes (infinite
       
    65         loop safety belt).
       
    66         """
       
    67         parent = self.parent_entity()
       
    68         if parent is not None:
       
    69             if recurs:
       
    70                 _recurs = recurs
       
    71             else:
       
    72                 _recurs = set()
       
    73             if _recurs and parent.eid in _recurs:
       
    74                 self.error('cycle in breadcrumbs for entity %s' % self.entity)
       
    75                 return []
       
    76             _recurs.add(parent.eid)
       
    77             adapter = parent.cw_adapt_to('IBreadCrumbs')
       
    78             path = adapter.breadcrumbs(view, _recurs) + [self.entity]
       
    79         else:
       
    80             path = [self.entity]
       
    81         if not recurs:
       
    82             if view is None:
       
    83                 if 'vtitle' in self._cw.form:
       
    84                     # embeding for instance
       
    85                     path.append( self._cw.form['vtitle'] )
       
    86             elif view.__regid__ != 'primary' and hasattr(view, 'title'):
       
    87                 path.append( self._cw._(view.title) )
       
    88         return path
       
    89 
       
    90 
       
    91 class BreadCrumbEntityVComponent(basecomponents.HeaderComponent):
       
    92     __regid__ = 'breadcrumbs'
       
    93     __select__ = (basecomponents.HeaderComponent.__select__
       
    94                   & one_line_rset() & adaptable('IBreadCrumbs'))
       
    95     order = basecomponents.ApplicationName.order + 1
       
    96     context = basecomponents.ApplicationName.context
       
    97     separator = u'&#160;&gt;&#160;'
       
    98     link_template = u'<a href="%s">%s</a>'
       
    99     first_separator = True
       
   100 
       
   101     # XXX support kwargs for compat with other components which gets the view as
       
   102     # argument
       
   103     def render(self, w, **kwargs):
       
   104         try:
       
   105             entity = self.cw_extra_kwargs['entity']
       
   106         except KeyError:
       
   107             entity = self.cw_rset.get_entity(0, 0)
       
   108         adapter = entity.cw_adapt_to('IBreadCrumbs')
       
   109         view = self.cw_extra_kwargs.get('view')
       
   110         path = adapter.breadcrumbs(view)
       
   111         if path:
       
   112             self.open_breadcrumbs(w)
       
   113             self.render_breadcrumbs(w, entity, path)
       
   114             self.close_breadcrumbs(w)
       
   115 
       
   116     def open_breadcrumbs(self, w):
       
   117         w(u'<span id="breadcrumbs" class="pathbar">')
       
   118         if self.first_separator:
       
   119             w(self.separator)
       
   120 
       
   121     def close_breadcrumbs(self, w):
       
   122         w(u'</span>')
       
   123 
       
   124     def render_breadcrumbs(self, w, contextentity, path):
       
   125         root = path.pop(0)
       
   126         if isinstance(root, Entity):
       
   127             w(self.link_template % (self._cw.build_url(root.__regid__),
       
   128                                          root.dc_type('plural')))
       
   129             w(self.separator)
       
   130         self.wpath_part(w, root, contextentity, not path)
       
   131         for i, parent in enumerate(path):
       
   132             w(self.separator)
       
   133             w(u"\n")
       
   134             self.wpath_part(w, parent, contextentity, i == len(path) - 1)
       
   135 
       
   136     def wpath_part(self, w, part, contextentity, last=False): # XXX deprecates last argument?
       
   137         if isinstance(part, Entity):
       
   138             w(part.view('breadcrumbs'))
       
   139         elif isinstance(part, tuple):
       
   140             url, title = part
       
   141             textsize = self._cw.property_value('navigation.short-line-size')
       
   142             w(self.link_template % (
       
   143                 xml_escape(url), xml_escape(uilib.cut(title, textsize))))
       
   144         else:
       
   145             textsize = self._cw.property_value('navigation.short-line-size')
       
   146             w(xml_escape(uilib.cut(text_type(part), textsize)))
       
   147 
       
   148 
       
   149 class BreadCrumbETypeVComponent(BreadCrumbEntityVComponent):
       
   150     __select__ = (basecomponents.HeaderComponent.__select__
       
   151                   & multi_lines_rset() & one_etype_rset()
       
   152                   & adaptable('IBreadCrumbs'))
       
   153 
       
   154     def render_breadcrumbs(self, w, contextentity, path):
       
   155         # XXX hack: only display etype name or first non entity path part
       
   156         root = path.pop(0)
       
   157         if isinstance(root, Entity):
       
   158             w(u'<a href="%s">%s</a>' % (self._cw.build_url(root.__regid__),
       
   159                                         root.dc_type('plural')))
       
   160         else:
       
   161             self.wpath_part(w, root, contextentity, not path)
       
   162 
       
   163 
       
   164 class BreadCrumbAnyRSetVComponent(BreadCrumbEntityVComponent):
       
   165     __select__ = basecomponents.HeaderComponent.__select__ & any_rset()
       
   166 
       
   167     # XXX support kwargs for compat with other components which gets the view as
       
   168     # argument
       
   169     def render(self, w, **kwargs):
       
   170         self.open_breadcrumbs(w)
       
   171         w(self._cw._('search'))
       
   172         self.close_breadcrumbs(w)
       
   173 
       
   174 
       
   175 class BreadCrumbLinkToVComponent(BreadCrumbEntityVComponent):
       
   176     __select__ = basecomponents.HeaderComponent.__select__ & match_form_params('__linkto')
       
   177 
       
   178     def render(self, w, **kwargs):
       
   179         eid = self._cw.list_form_param('__linkto')[0].split(':')[1]
       
   180         entity = self._cw.entity_from_eid(eid)
       
   181         ecmp = self._cw.vreg[self.__registry__].select(
       
   182             self.__regid__, self._cw, entity=entity, **kwargs)
       
   183         ecmp.render(w, **kwargs)
       
   184 
       
   185 
       
   186 class BreadCrumbView(EntityView):
       
   187     __regid__ = 'breadcrumbs'
       
   188 
       
   189     def cell_call(self, row, col, **kwargs):
       
   190         entity = self.cw_rset.get_entity(row, col)
       
   191         desc = uilib.cut(entity.dc_description(), 50)
       
   192         # NOTE remember camember: tags.a autoescapes
       
   193         self.w(tags.a(entity.view('breadcrumbtext'),
       
   194                       href=entity.absolute_url(), title=desc))
       
   195 
       
   196 
       
   197 class BreadCrumbTextView(EntityView):
       
   198     __regid__ = 'breadcrumbtext'
       
   199 
       
   200     def cell_call(self, row, col, **kwargs):
       
   201         entity = self.cw_rset.get_entity(row, col)
       
   202         textsize = self._cw.property_value('navigation.short-line-size')
       
   203         self.w(uilib.cut(entity.dc_title(), textsize))