web/views/baseviews.py
changeset 11057 0b59724cb3f2
parent 11052 058bb3dc685f
child 11058 23eb30449fe5
equal deleted inserted replaced
11052:058bb3dc685f 11057:0b59724cb3f2
     1 # copyright 2003-2012 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 """
       
    19 HTML views
       
    20 ~~~~~~~~~~
       
    21 
       
    22 Special views
       
    23 `````````````
       
    24 
       
    25 .. autoclass:: NullView
       
    26 .. autoclass:: NoResultView
       
    27 .. autoclass:: FinalView
       
    28 
       
    29 
       
    30 Base entity views
       
    31 `````````````````
       
    32 
       
    33 .. autoclass:: InContextView
       
    34 .. autoclass:: OutOfContextView
       
    35 .. autoclass:: OneLineView
       
    36 
       
    37 Those are used to display a link to an entity, whose label depends on the entity
       
    38 having to be displayed in or out of context (of another entity): some entities
       
    39 make sense in the context of another entity. For instance, the `Version` of a
       
    40 `Project` in forge. So one may expect that 'incontext' will be called when
       
    41 display a version from within the context of a project, while 'outofcontext"'
       
    42 will be called in other cases. In our example, the 'incontext' view of the
       
    43 version would be something like '0.1.2', while the 'outofcontext' view would
       
    44 include the project name, e.g. 'baz 0.1.2' (since only a version number without
       
    45 the associated project doesn't make sense if you don't know yet that you're
       
    46 talking about the famous 'baz' project. |cubicweb| tries to make guess and call
       
    47 'incontext'/'outofcontext' nicely. When it can't know, the 'oneline' view should
       
    48 be used.
       
    49 
       
    50 
       
    51 List entity views
       
    52 `````````````````
       
    53 
       
    54 .. autoclass:: ListView
       
    55 .. autoclass:: SimpleListView
       
    56 .. autoclass:: SameETypeListView
       
    57 .. autoclass:: CSVView
       
    58 
       
    59 Those list views can be given a 'subvid' arguments, telling the view to use of
       
    60 each item in the list. When not specified, the value of the 'redirect_vid'
       
    61 attribute of :class:`ListItemView` (for 'listview') or of
       
    62 :class:`SimpleListView` will be used. This default to 'outofcontext' for 'list'
       
    63 / 'incontext' for 'simplelist'
       
    64 
       
    65 
       
    66 Text entity views
       
    67 ~~~~~~~~~~~~~~~~~
       
    68 
       
    69 Basic HTML view have some variants to be used when generating raw text, not HTML
       
    70 (for notifications for instance). Also, as explained above, some of the HTML
       
    71 views use those text views as a basis.
       
    72 
       
    73 .. autoclass:: TextView
       
    74 .. autoclass:: InContextTextView
       
    75 .. autoclass:: OutOfContextView
       
    76 """
       
    77 
       
    78 __docformat__ = "restructuredtext en"
       
    79 from cubicweb import _
       
    80 
       
    81 from datetime import timedelta
       
    82 from warnings import warn
       
    83 
       
    84 from six.moves import range
       
    85 
       
    86 from rql import nodes
       
    87 
       
    88 from logilab.mtconverter import TransformError, xml_escape
       
    89 from logilab.common.registry import yes
       
    90 
       
    91 from cubicweb import NoSelectableObject, tags
       
    92 from cubicweb.predicates import empty_rset, one_etype_rset, match_kwargs
       
    93 from cubicweb.schema import display_name
       
    94 from cubicweb.view import EntityView, AnyRsetView, View
       
    95 from cubicweb.uilib import cut
       
    96 from cubicweb.web.views import calendar
       
    97 
       
    98 
       
    99 class NullView(AnyRsetView):
       
   100     """:__regid__: *null*
       
   101 
       
   102     This view is the default view used when nothing needs to be rendered. It is
       
   103     always applicable and is usually used as fallback view when calling
       
   104     :meth:`_cw.view` to display nothing if the result set is empty.
       
   105     """
       
   106     __regid__ = 'null'
       
   107     __select__ = yes()
       
   108     def call(self, **kwargs):
       
   109         pass
       
   110     cell_call = call
       
   111 
       
   112 
       
   113 class NoResultView(View):
       
   114     """:__regid__: *noresult*
       
   115 
       
   116     This view is the default view to be used when no result has been found
       
   117     (i.e. empty result set).
       
   118 
       
   119     It's usually used as fallback view when calling :meth:`_cw.view` to display
       
   120     "no results" if the result set is empty.
       
   121     """
       
   122     __regid__ = 'noresult'
       
   123     __select__ = empty_rset()
       
   124 
       
   125     def call(self, **kwargs):
       
   126         self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n'
       
   127                % self._cw._('No result matching query'))
       
   128 
       
   129 
       
   130 class FinalView(AnyRsetView):
       
   131     """:__regid__: *final*
       
   132 
       
   133     Display the value of a result set cell with minimal transformations
       
   134     (i.e. you'll get a number for entities). It is applicable on any result set,
       
   135     though usually dedicated for cells containing an attribute's value.
       
   136     """
       
   137     __regid__ = 'final'
       
   138 
       
   139     def cell_call(self, row, col, props=None, format='text/html'):
       
   140         value = self.cw_rset.rows[row][col]
       
   141         if value is None:
       
   142             self.w(u'')
       
   143             return
       
   144         etype = self.cw_rset.description[row][col]
       
   145         if etype == 'String':
       
   146             entity, rtype = self.cw_rset.related_entity(row, col)
       
   147             if entity is not None:
       
   148                 # call entity's printable_value which may have more information
       
   149                 # about string format & all
       
   150                 self.w(entity.printable_value(rtype, value, format=format))
       
   151                 return
       
   152         value = self._cw.printable_value(etype, value, props)
       
   153         if etype in ('Time', 'Interval'):
       
   154             self.w(value.replace(' ', '&#160;'))
       
   155         else:
       
   156             self.wdata(value)
       
   157 
       
   158 
       
   159 class InContextView(EntityView):
       
   160     """:__regid__: *incontext*
       
   161 
       
   162     This view is used when the entity should be considered as displayed in its
       
   163     context. By default it produces the result of ``entity.dc_title()`` wrapped in a
       
   164     link leading to the primary view of the entity.
       
   165     """
       
   166     __regid__ = 'incontext'
       
   167 
       
   168     def cell_call(self, row, col):
       
   169         entity = self.cw_rset.get_entity(row, col)
       
   170         desc = cut(entity.dc_description(), 50)
       
   171         self.w(u'<a href="%s" title="%s">%s</a>' % (
       
   172             xml_escape(entity.absolute_url()), xml_escape(desc),
       
   173             xml_escape(entity.dc_title())))
       
   174 
       
   175 class OutOfContextView(EntityView):
       
   176     """:__regid__: *outofcontext*
       
   177 
       
   178     This view is used when the entity should be considered as displayed out of
       
   179     its context. By default it produces the result of ``entity.dc_long_title()``
       
   180     wrapped in a link leading to the primary view of the entity.
       
   181     """
       
   182     __regid__ = 'outofcontext'
       
   183 
       
   184     def cell_call(self, row, col):
       
   185         entity = self.cw_rset.get_entity(row, col)
       
   186         desc = cut(entity.dc_description(), 50)
       
   187         self.w(u'<a href="%s" title="%s">%s</a>' % (
       
   188             xml_escape(entity.absolute_url()), xml_escape(desc),
       
   189             xml_escape(entity.dc_long_title())))
       
   190 
       
   191 
       
   192 class OneLineView(EntityView):
       
   193     """:__regid__: *oneline*
       
   194 
       
   195     This view is used when we can't tell if the entity should be considered as
       
   196     displayed in or out of context. By default it produces the result of the
       
   197     `text` view in a link leading to the primary view of the entity.
       
   198     """
       
   199     __regid__ = 'oneline'
       
   200     title = _('oneline')
       
   201 
       
   202     def cell_call(self, row, col, **kwargs):
       
   203         """the one line view for an entity: linked text view
       
   204         """
       
   205         entity = self.cw_rset.get_entity(row, col)
       
   206         desc = cut(entity.dc_description(), 50)
       
   207         title = cut(entity.dc_title(),
       
   208                     self._cw.property_value('navigation.short-line-size'))
       
   209         self.w(u'<a href="%s" title="%s">%s</a>' % (
       
   210                 xml_escape(entity.absolute_url()), xml_escape(desc),
       
   211                 xml_escape(title)))
       
   212 
       
   213 
       
   214 # text views ###################################################################
       
   215 
       
   216 class TextView(EntityView):
       
   217     """:__regid__: *text*
       
   218 
       
   219     This is the simplest text view for an entity. By default it returns the
       
   220     result of the entity's `dc_title()` method, which is cut to fit the
       
   221     `navigation.short-line-size` property if necessary.
       
   222     """
       
   223     __regid__ = 'text'
       
   224     title = _('text')
       
   225     content_type = 'text/plain'
       
   226 
       
   227     def call(self, **kwargs):
       
   228         """The view is called for an entire result set, by default loop other
       
   229         rows of the result set and call the same view on the particular row.
       
   230 
       
   231         Subclasses views that are applicable on None result sets will have to
       
   232         override this method.
       
   233         """
       
   234         rset = self.cw_rset
       
   235         if rset is None:
       
   236             raise NotImplementedError(self)
       
   237         for i in range(len(rset)):
       
   238             self.wview(self.__regid__, rset, row=i, **kwargs)
       
   239             if len(rset) > 1:
       
   240                 self.w(u"\n")
       
   241 
       
   242     def cell_call(self, row, col=0, **kwargs):
       
   243         entity = self.cw_rset.get_entity(row, col)
       
   244         self.w(cut(entity.dc_title(),
       
   245                    self._cw.property_value('navigation.short-line-size')))
       
   246 
       
   247 
       
   248 class InContextTextView(TextView):
       
   249     """:__regid__: *textincontext*
       
   250 
       
   251     Similar to the `text` view, but called when an entity is considered in
       
   252     context (see description of incontext HTML view for more information on
       
   253     this). By default it displays what's returned by the `dc_title()` method of
       
   254     the entity.
       
   255     """
       
   256     __regid__ = 'textincontext'
       
   257     title = None # not listed as a possible view
       
   258     def cell_call(self, row, col):
       
   259         entity = self.cw_rset.get_entity(row, col)
       
   260         self.w(entity.dc_title())
       
   261 
       
   262 
       
   263 class OutOfContextTextView(InContextTextView):
       
   264     """:__regid__: *textoutofcontext*
       
   265 
       
   266     Similar to the `text` view, but called when an entity is considered out of
       
   267     context (see description of outofcontext HTML view for more information on
       
   268     this). By default it displays what's returned by the `dc_long_title()`
       
   269     method of the entity.
       
   270     """
       
   271     __regid__ = 'textoutofcontext'
       
   272 
       
   273     def cell_call(self, row, col):
       
   274         entity = self.cw_rset.get_entity(row, col)
       
   275         self.w(entity.dc_long_title())
       
   276 
       
   277 
       
   278 # list views ##################################################################
       
   279 
       
   280 class ListView(EntityView):
       
   281     """:__regid__: *list*
       
   282 
       
   283     This view displays a list of entities by creating a HTML list (`<ul>`) and
       
   284     call the view `listitem` for each entity of the result set. The 'list' view
       
   285     will generate HTML like:
       
   286 
       
   287     .. sourcecode:: html
       
   288 
       
   289       <ul class="section">
       
   290         <li>"result of 'subvid' view for a row</li>
       
   291         ...
       
   292       </ul>
       
   293 
       
   294     If you wish to use a different view for each entity, either subclass and
       
   295     change the :attr:`item_vid` class attribute or specify a `subvid` argument
       
   296     when calling this view.
       
   297     """
       
   298     __regid__ = 'list'
       
   299     title = _('list')
       
   300     item_vid = 'listitem'
       
   301 
       
   302     def call(self, klass=None, title=None, subvid=None, listid=None, **kwargs):
       
   303         """display a list of entities by calling their <item_vid> view
       
   304 
       
   305         :param listid: the DOM id to use for the root element
       
   306         """
       
   307         # XXX much of the behaviour here should probably be outside this view
       
   308         if subvid is None and 'subvid' in self._cw.form:
       
   309             subvid = self._cw.form.pop('subvid') # consume it
       
   310         if listid:
       
   311             listid = u' id="%s"' % listid
       
   312         else:
       
   313             listid = u''
       
   314         if title:
       
   315             self.w(u'<div%s class="%s"><h4>%s</h4>\n' % (listid, klass or 'section', title))
       
   316             self.w(u'<ul>\n')
       
   317         else:
       
   318             self.w(u'<ul%s class="%s">\n' % (listid, klass or 'section'))
       
   319         for i in range(self.cw_rset.rowcount):
       
   320             self.cell_call(row=i, col=0, vid=subvid, klass=klass, **kwargs)
       
   321         self.w(u'</ul>\n')
       
   322         if title:
       
   323             self.w(u'</div>\n')
       
   324 
       
   325     def cell_call(self, row, col=0, vid=None, klass=None, **kwargs):
       
   326         self.w(u'<li>')
       
   327         self.wview(self.item_vid, self.cw_rset, row=row, col=col, vid=vid, **kwargs)
       
   328         self.w(u'</li>\n')
       
   329 
       
   330 
       
   331 class ListItemView(EntityView):
       
   332     __regid__ = 'listitem'
       
   333 
       
   334     @property
       
   335     def redirect_vid(self):
       
   336         if self._cw.search_state[0] == 'normal':
       
   337             return 'outofcontext'
       
   338         return 'outofcontext-search'
       
   339 
       
   340     def cell_call(self, row, col, vid=None, **kwargs):
       
   341         if not vid:
       
   342             vid = self.redirect_vid
       
   343         try:
       
   344             self.wview(vid, self.cw_rset, row=row, col=col, **kwargs)
       
   345         except NoSelectableObject:
       
   346             if vid == self.redirect_vid:
       
   347                 raise
       
   348             self.wview(self.redirect_vid, self.cw_rset, row=row, col=col, **kwargs)
       
   349 
       
   350 
       
   351 class SimpleListView(ListItemView):
       
   352     """:__regid__: *simplelist*
       
   353 
       
   354     Similar to :class:~cubicweb.web.views.baseviews.ListView but using '<div>'
       
   355     instead of '<ul>'. It rely on '<div>' behaviour to separate items. HTML will
       
   356     look like
       
   357 
       
   358     .. sourcecode:: html
       
   359 
       
   360       <div class="section">"result of 'subvid' view for a row</div>
       
   361       ...
       
   362 
       
   363 
       
   364     It relies on base :class:`~cubicweb.view.View` class implementation of the
       
   365     :meth:`call` method to insert those <div>.
       
   366     """
       
   367     __regid__ = 'simplelist'
       
   368     redirect_vid = 'incontext'
       
   369 
       
   370     def call(self, subvid=None, **kwargs):
       
   371         """display a list of entities by calling their <item_vid> view
       
   372 
       
   373         :param listid: the DOM id to use for the root element
       
   374         """
       
   375         if subvid is None and 'vid' in kwargs:
       
   376             warn("should give a 'subvid' argument instead of 'vid'",
       
   377                  DeprecationWarning, stacklevel=2)
       
   378         else:
       
   379             kwargs['vid'] = subvid
       
   380         return super(SimpleListView, self).call(**kwargs)
       
   381 
       
   382 
       
   383 class SameETypeListView(EntityView):
       
   384     """:__regid__: *sameetypelist*
       
   385 
       
   386     This view displays a list of entities of the same type, in HTML section
       
   387     ('<div>') and call the view `sameetypelistitem` for each entity of the
       
   388     result set. It's designed to get a more adapted global list when displayed
       
   389     entities are all of the same type (for instance, display gallery if there
       
   390     are only images entities).
       
   391     """
       
   392     __regid__ = 'sameetypelist'
       
   393     __select__ = EntityView.__select__ & one_etype_rset()
       
   394     item_vid = 'sameetypelistitem'
       
   395 
       
   396     @property
       
   397     def title(self):
       
   398         etype = next(iter(self.cw_rset.column_types(0)))
       
   399         return display_name(self._cw, etype, form='plural')
       
   400 
       
   401     def call(self, **kwargs):
       
   402         """display a list of entities by calling their <item_vid> view"""
       
   403         showtitle = kwargs.pop('showtitle', not 'vtitle' in self._cw.form)
       
   404         if showtitle:
       
   405             self.w(u'<h1>%s</h1>' % self.title)
       
   406         super(SameETypeListView, self).call(**kwargs)
       
   407 
       
   408     def cell_call(self, row, col=0, **kwargs):
       
   409         self.wview(self.item_vid, self.cw_rset, row=row, col=col, **kwargs)
       
   410 
       
   411 
       
   412 class SameETypeListItemView(EntityView):
       
   413     __regid__ = 'sameetypelistitem'
       
   414 
       
   415     def cell_call(self, row, col, **kwargs):
       
   416         self.wview('listitem', self.cw_rset, row=row, col=col, **kwargs)
       
   417 
       
   418 
       
   419 class CSVView(SimpleListView):
       
   420     """:__regid__: *csv*
       
   421 
       
   422     This view displays each entity in a coma separated list. It is NOT related
       
   423     to the well-known text file format.
       
   424     """
       
   425     __regid__ = 'csv'
       
   426     redirect_vid = 'incontext'
       
   427     separator = u', '
       
   428 
       
   429     def call(self, subvid=None, **kwargs):
       
   430         kwargs['vid'] = subvid
       
   431         rset = self.cw_rset
       
   432         for i in range(len(rset)):
       
   433             self.cell_call(i, 0, **kwargs)
       
   434             if i < rset.rowcount-1:
       
   435                 self.w(self.separator)
       
   436 
       
   437 
       
   438 # XXX to be documented views ###################################################
       
   439 
       
   440 class MetaDataView(EntityView):
       
   441     """paragraph view of some metadata"""
       
   442     __regid__ = 'metadata'
       
   443     show_eid = True
       
   444 
       
   445     def cell_call(self, row, col):
       
   446         _ = self._cw._
       
   447         entity = self.cw_rset.get_entity(row, col)
       
   448         self.w(u'<div>')
       
   449         if self.show_eid:
       
   450             self.w(u'%s #%s - ' % (entity.dc_type(), entity.eid))
       
   451         if entity.modification_date != entity.creation_date:
       
   452             self.w(u'<span>%s</span> ' % _('latest update on'))
       
   453             self.w(u'<span class="value">%s</span>, '
       
   454                    % self._cw.format_date(entity.modification_date))
       
   455         # entities from external source may not have a creation date (eg ldap)
       
   456         if entity.creation_date:
       
   457             self.w(u'<span>%s</span> ' % _('created on'))
       
   458             self.w(u'<span class="value">%s</span>'
       
   459                    % self._cw.format_date(entity.creation_date))
       
   460         if entity.creator:
       
   461             if entity.creation_date:
       
   462                 self.w(u' <span>%s</span> ' % _('by'))
       
   463             else:
       
   464                 self.w(u' <span>%s</span> ' % _('created_by'))
       
   465             self.w(u'<span class="value">%s</span>' % entity.creator.name())
       
   466         meta = entity.cw_metainformation()
       
   467         if meta['source']['uri'] != 'system':
       
   468             self.w(u' (<span>%s</span>' % _('cw_source'))
       
   469             self.w(u' <span class="value">%s</span>)' % meta['source']['uri'])
       
   470         self.w(u'</div>')
       
   471 
       
   472 
       
   473 class TreeItemView(ListItemView):
       
   474     __regid__ = 'treeitem'
       
   475 
       
   476     def cell_call(self, row, col):
       
   477         self.wview('incontext', self.cw_rset, row=row, col=col)
       
   478 
       
   479 
       
   480 class TextSearchResultView(EntityView):
       
   481     """this view is used to display full-text search
       
   482 
       
   483     It tries to highlight part of data where the search word appears.
       
   484 
       
   485     XXX: finish me (fixed line width, fixed number of lines, CSS, etc.)
       
   486     """
       
   487     __regid__ = 'tsearch'
       
   488 
       
   489     def cell_call(self, row, col, **kwargs):
       
   490         entity = self.cw_rset.complete_entity(row, col)
       
   491         self.w(entity.view('incontext'))
       
   492         searched = self.cw_rset.searched_text()
       
   493         if searched is None:
       
   494             return
       
   495         searched = searched.lower()
       
   496         highlighted = '<b>%s</b>' % searched
       
   497         for attr in entity.e_schema.indexable_attributes():
       
   498             try:
       
   499                 value = xml_escape(entity.printable_value(attr, format='text/plain').lower())
       
   500             except TransformError as ex:
       
   501                 continue
       
   502             except Exception:
       
   503                 continue
       
   504             if searched in value:
       
   505                 contexts = []
       
   506                 for ctx in value.split(searched):
       
   507                     if len(ctx) > 30:
       
   508                         contexts.append(u'...' + ctx[-30:])
       
   509                     else:
       
   510                         contexts.append(ctx)
       
   511                 value = u'\n' + highlighted.join(contexts)
       
   512                 self.w(value.replace('\n', '<br/>'))
       
   513 
       
   514 
       
   515 class TooltipView(EntityView):
       
   516     """A entity view used in a tooltip"""
       
   517     __regid__ = 'tooltip'
       
   518     def cell_call(self, row, col):
       
   519         self.wview('oneline', self.cw_rset, row=row, col=col)
       
   520 
       
   521 
       
   522 class GroupByView(EntityView):
       
   523     """grouped view of a result set. The `group_key` method return the group
       
   524     key of an entities (a string or tuple of string).
       
   525 
       
   526     For each group, display a link to entities of this group by generating url
       
   527     like <basepath>/<key> or <basepath>/<key item 1>/<key item 2>.
       
   528     """
       
   529     __abstract__ = True
       
   530     __select__ = EntityView.__select__ & match_kwargs('basepath')
       
   531     entity_attribute = None
       
   532     reversed = False
       
   533 
       
   534     def index_url(self, basepath, key, **kwargs):
       
   535         if isinstance(key, (list, tuple)):
       
   536             key = '/'.join(key)
       
   537         return self._cw.build_url('%s/%s' % (basepath, key),
       
   538                                   **kwargs)
       
   539 
       
   540     def index_link(self, basepath, key, items):
       
   541         url = self.index_url(basepath, key)
       
   542         if isinstance(key, (list, tuple)):
       
   543             key = ' '.join(key)
       
   544         return tags.a(key, href=url)
       
   545 
       
   546     def group_key(self, entity, **kwargs):
       
   547         value = getattr(entity, self.entity_attribute)
       
   548         if callable(value):
       
   549             value = value()
       
   550         return value
       
   551 
       
   552     def call(self, basepath, maxentries=None, **kwargs):
       
   553         index = {}
       
   554         for entity in self.cw_rset.entities():
       
   555             index.setdefault(self.group_key(entity, **kwargs), []).append(entity)
       
   556         displayed = sorted(index)
       
   557         if self.reversed:
       
   558             displayed = reversed(displayed)
       
   559         if maxentries is None:
       
   560             needmore = False
       
   561         else:
       
   562             needmore = len(index) > maxentries
       
   563             displayed = tuple(displayed)[:maxentries]
       
   564         w = self.w
       
   565         w(u'<ul class="boxListing">')
       
   566         for key in displayed:
       
   567             if key:
       
   568                 w(u'<li>%s</li>\n' %
       
   569                   self.index_link(basepath, key, index[key]))
       
   570         if needmore:
       
   571             url = self._cw.build_url('view', vid=self.__regid__,
       
   572                                      rql=self.cw_rset.printable_rql())
       
   573             w( u'<li>%s</li>\n' % tags.a(u'[%s]' % self._cw._('see more'),
       
   574                                          href=url))
       
   575         w(u'</ul>\n')
       
   576 
       
   577 
       
   578 class ArchiveView(GroupByView):
       
   579     """archive view of a result set. Links to months are built using a basepath
       
   580     parameters, eg using url like <basepath>/<year>/<month>
       
   581     """
       
   582     __regid__ = 'cw.archive.by_date'
       
   583     entity_attribute = 'creation_date'
       
   584     reversed = True
       
   585 
       
   586     def group_key(self, entity, **kwargs):
       
   587         value = super(ArchiveView, self).group_key(entity, **kwargs)
       
   588         return '%04d' % value.year, '%02d' % value.month
       
   589 
       
   590     def index_link(self, basepath, key, items):
       
   591         """represent a single month entry"""
       
   592         year, month = key
       
   593         label = u'%s %s [%s]' % (self._cw._(calendar.MONTHNAMES[int(month)-1]),
       
   594                                  year, len(items))
       
   595         etypes = set(entity.cw_etype for entity in items)
       
   596         vtitle = '%s %s' % (', '.join(display_name(self._cw, etype, 'plural')
       
   597                                       for etype in etypes),
       
   598                             label)
       
   599         title = self._cw._('archive for %(month)s/%(year)s') % {
       
   600             'month': month, 'year': year}
       
   601         url = self.index_url(basepath, key, vtitle=vtitle)
       
   602         return tags.a(label, href=url, title=title)
       
   603 
       
   604 
       
   605 class AuthorView(GroupByView):
       
   606     """author view of a result set. Links to month are built using a basepath
       
   607     parameters, eg using url like <basepath>/<author>
       
   608     """
       
   609     __regid__ = 'cw.archive.by_author'
       
   610     entity_attribute = 'creator'
       
   611 
       
   612     def group_key(self, entity, **kwargs):
       
   613         value = super(AuthorView, self).group_key(entity, **kwargs)
       
   614         if value:
       
   615             return (value.name(), value.login)
       
   616         return (None, None)
       
   617 
       
   618     def index_link(self, basepath, key, items):
       
   619         if key[0] is None:
       
   620             return
       
   621         label = u'%s [%s]' % (key[0], len(items))
       
   622         etypes = set(entity.cw_etype for entity in items)
       
   623         vtitle = self._cw._('%(etype)s by %(author)s') % {
       
   624             'etype': ', '.join(display_name(self._cw, etype, 'plural')
       
   625                                for etype in etypes),
       
   626             'author': label}
       
   627         url = self.index_url(basepath, key[1], vtitle=vtitle)
       
   628         title = self._cw._('archive for %(author)s') % {'author': key[0]}
       
   629         return tags.a(label, href=url, title=title)
       
   630 
       
   631 
       
   632 # bw compat ####################################################################
       
   633 
       
   634 from logilab.common.deprecation import class_moved, class_deprecated
       
   635 
       
   636 from cubicweb.web.views import boxes, xmlrss, primary, tableview
       
   637 PrimaryView = class_moved(primary.PrimaryView)
       
   638 SideBoxView = class_moved(boxes.SideBoxView)
       
   639 XmlView = class_moved(xmlrss.XMLView)
       
   640 XmlItemView = class_moved(xmlrss.XMLItemView)
       
   641 XmlRsetView = class_moved(xmlrss.XMLRsetView)
       
   642 RssView = class_moved(xmlrss.RSSView)
       
   643 RssItemView = class_moved(xmlrss.RSSItemView)
       
   644 TableView = class_moved(tableview.TableView)