web/views/baseviews.py
changeset 7845 2172978be237
parent 7840 d6733436b0dd
child 7913 d0c6a7993cec
equal deleted inserted replaced
7841:287813c487b7 7845:2172978be237
     1 # copyright 2003-2010 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     1 # copyright 2003-2011 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     2 # contact http://www.logilab.fr/ -- mailto:contact@logilab.fr
     3 #
     3 #
     4 # This file is part of CubicWeb.
     4 # This file is part of CubicWeb.
     5 #
     5 #
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
     6 # CubicWeb is free software: you can redistribute it and/or modify it under the
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    13 # FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more
    14 # details.
    14 # details.
    15 #
    15 #
    16 # You should have received a copy of the GNU Lesser General Public License along
    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/>.
    17 # with CubicWeb.  If not, see <http://www.gnu.org/licenses/>.
    18 """Set of HTML generic base views:
    18 """
    19 
    19 HTML views
    20 * noresult, final
    20 ~~~~~~~~~~
    21 * primary, sidebox
    21 
    22 * oneline, incontext, outofcontext, text
    22 Special views
    23 * list
    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
    24 """
    76 """
    25 
    77 
    26 __docformat__ = "restructuredtext en"
    78 __docformat__ = "restructuredtext en"
    27 _ = unicode
    79 _ = unicode
    28 
    80 
    40 from cubicweb.uilib import cut, printable_value
    92 from cubicweb.uilib import cut, printable_value
    41 from cubicweb.web.views import calendar
    93 from cubicweb.web.views import calendar
    42 
    94 
    43 
    95 
    44 class NullView(AnyRsetView):
    96 class NullView(AnyRsetView):
    45     """default view when no result has been found"""
    97     """:__regid__: *null*
       
    98 
       
    99     This view is the default view used when nothing needs to be rendered. It is
       
   100     always applicable and is usually used as fallback view when calling
       
   101     :meth:`_cw.view` to display nothing if the result set is empty.
       
   102     """
    46     __regid__ = 'null'
   103     __regid__ = 'null'
    47     __select__ = yes()
   104     __select__ = yes()
    48     def call(self, **kwargs):
   105     def call(self, **kwargs):
    49         pass
   106         pass
    50     cell_call = call
   107     cell_call = call
    51 
   108 
    52 
   109 
    53 class NoResultView(View):
   110 class NoResultView(View):
    54     """default view when no result has been found"""
   111     """:__regid__: *noresult*
       
   112 
       
   113     This view is the default view to be used when no result has been found
       
   114     (i.e. empty result set).
       
   115 
       
   116     It's usually used as fallback view when calling :meth:`_cw.view` to display
       
   117     "no results" if the result set is empty.
       
   118     """
       
   119     __regid__ = 'noresult'
    55     __select__ = empty_rset()
   120     __select__ = empty_rset()
    56     __regid__ = 'noresult'
       
    57 
   121 
    58     def call(self, **kwargs):
   122     def call(self, **kwargs):
    59         self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n'
   123         self.w(u'<div class="searchMessage"><strong>%s</strong></div>\n'
    60                % self._cw._('No result matching query'))
   124                % self._cw._('No result matching query'))
    61 
   125 
    62 
   126 
    63 class FinalView(AnyRsetView):
   127 class FinalView(AnyRsetView):
    64     """display values without any transformation (i.e. get a number for
   128     """:__regid__: *final*
    65     entities)
   129 
       
   130     Display the value of a result set cell with minimal transformations
       
   131     (i.e. you'll get a number for entities). It is applicable on any result set,
       
   132     though usually dedicated for cells containing an attribute's value.
    66     """
   133     """
    67     __regid__ = 'final'
   134     __regid__ = 'final'
    68     # record generated i18n catalog messages
   135     # record generated i18n catalog messages
    69     _('%d&#160;years')
   136     _('%d&#160;years')
    70     _('%d&#160;months')
   137     _('%d&#160;months')
   124                 self.w(self._cw.__('%%d%sseconds' % space) % int(value.seconds))
   191                 self.w(self._cw.__('%%d%sseconds' % space) % int(value.seconds))
   125             return
   192             return
   126         self.wdata(printable_value(self._cw, etype, value, props))
   193         self.wdata(printable_value(self._cw, etype, value, props))
   127 
   194 
   128 
   195 
   129 # XXX deprecated
   196 class InContextView(EntityView):
   130 class SecondaryView(EntityView):
   197     """:__regid__: *incontext*
   131     __regid__ = 'secondary'
   198 
   132     title = _('secondary')
   199     This view is used whenthe entity should be considered as displayed in its
   133 
   200     context. By default it produces the result of `textincontext` wrapped in a
   134     def cell_call(self, row, col, **kwargs):
   201     link leading to the primary view of the entity.
   135         """the secondary view for an entity
   202     """
   136         secondary = icon + view(oneline)
   203     __regid__ = 'incontext'
   137         """
   204 
   138         entity = self.cw_rset.get_entity(row, col)
   205     def cell_call(self, row, col):
   139         self.w(u'&#160;')
   206         entity = self.cw_rset.get_entity(row, col)
   140         self.wview('oneline', self.cw_rset, row=row, col=col)
   207         desc = cut(entity.dc_description(), 50)
       
   208         self.w(u'<a href="%s" title="%s">' % (
       
   209             xml_escape(entity.absolute_url()), xml_escape(desc)))
       
   210         self.w(xml_escape(self._cw.view('textincontext', self.cw_rset,
       
   211                                         row=row, col=col)))
       
   212         self.w(u'</a>')
       
   213 
       
   214 
       
   215 class OutOfContextView(EntityView):
       
   216     """:__regid__: *outofcontext*
       
   217 
       
   218     This view is used whenthe entity should be considered as displayed out of
       
   219     its context. By default it produces the result of `textoutofcontext` wrapped
       
   220     in a link leading to the primary view of the entity.
       
   221     """
       
   222     __regid__ = 'outofcontext'
       
   223 
       
   224     def cell_call(self, row, col):
       
   225         entity = self.cw_rset.get_entity(row, col)
       
   226         desc = cut(entity.dc_description(), 50)
       
   227         self.w(u'<a href="%s" title="%s">' % (
       
   228             xml_escape(entity.absolute_url()), xml_escape(desc)))
       
   229         self.w(xml_escape(self._cw.view('textoutofcontext', self.cw_rset,
       
   230                                         row=row, col=col)))
       
   231         self.w(u'</a>')
   141 
   232 
   142 
   233 
   143 class OneLineView(EntityView):
   234 class OneLineView(EntityView):
       
   235     """:__regid__: *oneline*
       
   236 
       
   237     This view is used when we can't tell if the entity should be considered as
       
   238     displayed in or out of context. By default it produces the result of the
       
   239     `text` view in a link leading to the primary view of the entity.
       
   240     """
   144     __regid__ = 'oneline'
   241     __regid__ = 'oneline'
   145     title = _('oneline')
   242     title = _('oneline')
   146 
   243 
   147     def cell_call(self, row, col, **kwargs):
   244     def cell_call(self, row, col, **kwargs):
   148         """the one line view for an entity: linked text view
   245         """the one line view for an entity: linked text view
   151         self.w(u'<a href="%s">' % xml_escape(entity.absolute_url()))
   248         self.w(u'<a href="%s">' % xml_escape(entity.absolute_url()))
   152         self.w(xml_escape(self._cw.view('text', self.cw_rset, row=row, col=col)))
   249         self.w(xml_escape(self._cw.view('text', self.cw_rset, row=row, col=col)))
   153         self.w(u'</a>')
   250         self.w(u'</a>')
   154 
   251 
   155 
   252 
       
   253 # text views ###################################################################
       
   254 
   156 class TextView(EntityView):
   255 class TextView(EntityView):
   157     """the simplest text view for an entity"""
   256     """:__regid__: *text*
       
   257 
       
   258     This is the simplest text view for an entity. By default it returns the
       
   259     result of the entity's `dc_title()` method, which is cut to fit the
       
   260     `navigation.short-line-size` property if necessary.
       
   261     """
   158     __regid__ = 'text'
   262     __regid__ = 'text'
   159     title = _('text')
   263     title = _('text')
   160     content_type = 'text/plain'
   264     content_type = 'text/plain'
   161 
   265 
   162     def call(self, **kwargs):
   266     def call(self, **kwargs):
   163         """the view is called for an entire result set, by default loop
   267         """The view is called for an entire result set, by default loop other
   164         other rows of the result set and call the same view on the
   268         rows of the result set and call the same view on the particular row.
   165         particular row
   269 
   166 
   270         Subclasses views that are applicable on None result sets will have to
   167         Views applicable on None result sets have to override this method
   271         override this method.
   168         """
   272         """
   169         rset = self.cw_rset
   273         rset = self.cw_rset
   170         if rset is None:
   274         if rset is None:
   171             raise NotImplementedError, self
   275             raise NotImplementedError, self
   172         for i in xrange(len(rset)):
   276         for i in xrange(len(rset)):
   177     def cell_call(self, row, col=0, **kwargs):
   281     def cell_call(self, row, col=0, **kwargs):
   178         entity = self.cw_rset.get_entity(row, col)
   282         entity = self.cw_rset.get_entity(row, col)
   179         self.w(cut(entity.dc_title(),
   283         self.w(cut(entity.dc_title(),
   180                    self._cw.property_value('navigation.short-line-size')))
   284                    self._cw.property_value('navigation.short-line-size')))
   181 
   285 
       
   286 
       
   287 class InContextTextView(TextView):
       
   288     """:__regid__: *textincontext*
       
   289 
       
   290     Similar to the `text` view, but called when an entity is considered in
       
   291     context (see description of incontext HTML view for more information on
       
   292     this). By default it displays what's returned by the `dc_title()` method of
       
   293     the entity.
       
   294     """
       
   295     __regid__ = 'textincontext'
       
   296     title = None # not listed as a possible view
       
   297     def cell_call(self, row, col):
       
   298         entity = self.cw_rset.get_entity(row, col)
       
   299         self.w(entity.dc_title())
       
   300 
       
   301 
       
   302 class OutOfContextTextView(InContextTextView):
       
   303     """:__regid__: *textoutofcontext*
       
   304 
       
   305     Similar to the `text` view, but called when an entity is considered out of
       
   306     context (see description of outofcontext HTML view for more information on
       
   307     this). By default it displays what's returned by the `dc_long_title()`
       
   308     method of the entity.
       
   309     """
       
   310     __regid__ = 'textoutofcontext'
       
   311 
       
   312     def cell_call(self, row, col):
       
   313         entity = self.cw_rset.get_entity(row, col)
       
   314         self.w(entity.dc_long_title())
       
   315 
       
   316 
       
   317 # list views ##################################################################
       
   318 
       
   319 class ListView(EntityView):
       
   320     """:__regid__: *list*
       
   321 
       
   322     This view displays a list of entities by creating a HTML list (`<ul>`) and
       
   323     call the view `listitem` for each entity of the result set. The 'list' view
       
   324     will generate HTML like:
       
   325 
       
   326     .. sourcecode:: html
       
   327 
       
   328       <ul class="section">
       
   329         <li>"result of 'subvid' view for a row</li>
       
   330         ...
       
   331       </ul>
       
   332 
       
   333     If you wish to use a different view for each entity, either subclass and
       
   334     change the :attr:`item_vid` class attribute or specify a `subvid` argument
       
   335     when calling this view.
       
   336     """
       
   337     __regid__ = 'list'
       
   338     title = _('list')
       
   339     item_vid = 'listitem'
       
   340 
       
   341     def call(self, klass=None, title=None, subvid=None, listid=None, **kwargs):
       
   342         """display a list of entities by calling their <item_vid> view
       
   343 
       
   344         :param listid: the DOM id to use for the root element
       
   345         """
       
   346         # XXX much of the behaviour here should probably be outside this view
       
   347         if subvid is None and 'subvid' in self._cw.form:
       
   348             subvid = self._cw.form.pop('subvid') # consume it
       
   349         if listid:
       
   350             listid = u' id="%s"' % listid
       
   351         else:
       
   352             listid = u''
       
   353         if title:
       
   354             self.w(u'<div%s class="%s"><h4>%s</h4>\n' % (listid, klass or 'section', title))
       
   355             self.w(u'<ul>\n')
       
   356         else:
       
   357             self.w(u'<ul%s class="%s">\n' % (listid, klass or 'section'))
       
   358         for i in xrange(self.cw_rset.rowcount):
       
   359             self.cell_call(row=i, col=0, vid=subvid, **kwargs)
       
   360         self.w(u'</ul>\n')
       
   361         if title:
       
   362             self.w(u'</div>\n')
       
   363 
       
   364     def cell_call(self, row, col=0, vid=None, **kwargs):
       
   365         self.w(u'<li>')
       
   366         self.wview(self.item_vid, self.cw_rset, row=row, col=col, vid=vid, **kwargs)
       
   367         self.w(u'</li>\n')
       
   368 
       
   369 
       
   370 class ListItemView(EntityView):
       
   371     __regid__ = 'listitem'
       
   372 
       
   373     @property
       
   374     def redirect_vid(self):
       
   375         if self._cw.search_state[0] == 'normal':
       
   376             return 'outofcontext'
       
   377         return 'outofcontext-search'
       
   378 
       
   379     def cell_call(self, row, col, vid=None, **kwargs):
       
   380         if not vid:
       
   381             vid = self.redirect_vid
       
   382         try:
       
   383             self.wview(vid, self.cw_rset, row=row, col=col, **kwargs)
       
   384         except NoSelectableObject:
       
   385             if vid == self.redirect_vid:
       
   386                 raise
       
   387             self.wview(self.redirect_vid, self.cw_rset, row=row, col=col, **kwargs)
       
   388 
       
   389 
       
   390 class SimpleListView(ListItemView):
       
   391     """:__regid__: *simplelist*
       
   392 
       
   393     Similar to :class:~cubicweb.web.views.baseviews.ListView but using '<div>'
       
   394     instead of '<ul>'. It rely on '<div>' behaviour to separate items. HTML will
       
   395     look like
       
   396 
       
   397     .. sourcecode:: html
       
   398 
       
   399       <div class="section">"result of 'subvid' view for a row</div>
       
   400       ...
       
   401 
       
   402 
       
   403     It relies on base :class:`~cubicweb.view.View` class implementation of the
       
   404     :meth:`call` method to insert those <div>.
       
   405     """
       
   406     __regid__ = 'simplelist'
       
   407     redirect_vid = 'incontext'
       
   408 
       
   409     def call(self, subvid=None, **kwargs):
       
   410         """display a list of entities by calling their <item_vid> view
       
   411 
       
   412         :param listid: the DOM id to use for the root element
       
   413         """
       
   414         if subvid is None and 'vid' in kwargs:
       
   415             warn("should give a 'subvid' argument instead of 'vid'",
       
   416                  DeprecationWarning, stacklevel=2)
       
   417         else:
       
   418             kwargs['vid'] = subvid
       
   419         return super(SimpleListView, self).call(**kwargs)
       
   420 
       
   421 
       
   422 class SameETypeListView(EntityView):
       
   423     """:__regid__: *sameetypelist*
       
   424 
       
   425     This view displays a list of entities of the same type, in HTML section
       
   426     ('<div>') and call the view `sameetypelistitem` for each entity of the
       
   427     result set. It's designed to get a more adapted global list when displayed
       
   428     entities are all of the same type (for instance, display gallery if there
       
   429     are only images entities).
       
   430     """
       
   431     __regid__ = 'sameetypelist'
       
   432     __select__ = EntityView.__select__ & one_etype_rset()
       
   433     item_vid = 'sameetypelistitem'
       
   434 
       
   435     @property
       
   436     def title(self):
       
   437         etype = iter(self.cw_rset.column_types(0)).next()
       
   438         return display_name(self._cw, etype, form='plural')
       
   439 
       
   440     def call(self, **kwargs):
       
   441         """display a list of entities by calling their <item_vid> view"""
       
   442         showtitle = kwargs.pop('showtitle', not 'vtitle' in self._cw.form)
       
   443         if showtitle:
       
   444             self.w(u'<h1>%s</h1>' % self.title)
       
   445         super(SameETypeListView, self).call(**kwargs)
       
   446 
       
   447     def cell_call(self, row, col=0, **kwargs):
       
   448         self.wview(self.item_vid, self.cw_rset, row=row, col=col, **kwargs)
       
   449 
       
   450 
       
   451 class SameETypeListItemView(EntityView):
       
   452     __regid__ = 'sameetypelistitem'
       
   453 
       
   454     def cell_call(self, row, col, **kwargs):
       
   455         self.wview('listitem', self.cw_rset, row=row, col=col, **kwargs)
       
   456 
       
   457 
       
   458 class CSVView(SimpleListView):
       
   459     """:__regid__: *csv*
       
   460 
       
   461     This view displays each entity in a coma separated list. It is NOT related
       
   462     to the well-known text file format.
       
   463     """
       
   464     __regid__ = 'csv'
       
   465     redirect_vid = 'incontext'
       
   466 
       
   467     def call(self, subvid=None, **kwargs):
       
   468         if subvid is None and 'vid' in kwargs:
       
   469             warn("[3.9] should give a 'subvid' argument instead of 'vid'",
       
   470                  DeprecationWarning, stacklevel=2)
       
   471         else:
       
   472             kwargs['vid'] = subvid
       
   473         rset = self.cw_rset
       
   474         for i in xrange(len(rset)):
       
   475             self.cell_call(i, 0, **kwargs)
       
   476             if i < rset.rowcount-1:
       
   477                 self.w(u", ")
       
   478 
       
   479 
       
   480 # XXX to be documented views ###################################################
   182 
   481 
   183 class MetaDataView(EntityView):
   482 class MetaDataView(EntityView):
   184     """paragraph view of some metadata"""
   483     """paragraph view of some metadata"""
   185     __regid__ = 'metadata'
   484     __regid__ = 'metadata'
   186     show_eid = True
   485     show_eid = True
   211             self.w(u' (<span>%s</span>' % _('cw_source'))
   510             self.w(u' (<span>%s</span>' % _('cw_source'))
   212             self.w(u' <span class="value">%s</span>)' % meta['source']['uri'])
   511             self.w(u' <span class="value">%s</span>)' % meta['source']['uri'])
   213         self.w(u'</div>')
   512         self.w(u'</div>')
   214 
   513 
   215 
   514 
   216 class InContextTextView(TextView):
       
   217     __regid__ = 'textincontext'
       
   218     title = None # not listed as a possible view
       
   219     def cell_call(self, row, col):
       
   220         entity = self.cw_rset.get_entity(row, col)
       
   221         self.w(entity.dc_title())
       
   222 
       
   223 
       
   224 class OutOfContextTextView(InContextTextView):
       
   225     __regid__ = 'textoutofcontext'
       
   226 
       
   227     def cell_call(self, row, col):
       
   228         entity = self.cw_rset.get_entity(row, col)
       
   229         self.w(entity.dc_long_title())
       
   230 
       
   231 
       
   232 class InContextView(EntityView):
       
   233     __regid__ = 'incontext'
       
   234 
       
   235     def cell_call(self, row, col):
       
   236         entity = self.cw_rset.get_entity(row, col)
       
   237         desc = cut(entity.dc_description(), 50)
       
   238         self.w(u'<a href="%s" title="%s">' % (
       
   239             xml_escape(entity.absolute_url()), xml_escape(desc)))
       
   240         self.w(xml_escape(self._cw.view('textincontext', self.cw_rset,
       
   241                                         row=row, col=col)))
       
   242         self.w(u'</a>')
       
   243 
       
   244 
       
   245 class OutOfContextView(EntityView):
       
   246     __regid__ = 'outofcontext'
       
   247 
       
   248     def cell_call(self, row, col):
       
   249         entity = self.cw_rset.get_entity(row, col)
       
   250         desc = cut(entity.dc_description(), 50)
       
   251         self.w(u'<a href="%s" title="%s">' % (
       
   252             xml_escape(entity.absolute_url()), xml_escape(desc)))
       
   253         self.w(xml_escape(self._cw.view('textoutofcontext', self.cw_rset,
       
   254                                         row=row, col=col)))
       
   255         self.w(u'</a>')
       
   256 
       
   257 
       
   258 # list views ##################################################################
       
   259 
       
   260 class ListView(EntityView):
       
   261     __regid__ = 'list'
       
   262     title = _('list')
       
   263     item_vid = 'listitem'
       
   264 
       
   265     def call(self, klass=None, title=None, subvid=None, listid=None, **kwargs):
       
   266         """display a list of entities by calling their <item_vid> view
       
   267 
       
   268         :param listid: the DOM id to use for the root element
       
   269         """
       
   270         # XXX much of the behaviour here should probably be outside this view
       
   271         if subvid is None and 'subvid' in self._cw.form:
       
   272             subvid = self._cw.form.pop('subvid') # consume it
       
   273         if listid:
       
   274             listid = u' id="%s"' % listid
       
   275         else:
       
   276             listid = u''
       
   277         if title:
       
   278             self.w(u'<div%s class="%s"><h4>%s</h4>\n' % (listid, klass or 'section', title))
       
   279             self.w(u'<ul>\n')
       
   280         else:
       
   281             self.w(u'<ul%s class="%s">\n' % (listid, klass or 'section'))
       
   282         for i in xrange(self.cw_rset.rowcount):
       
   283             self.cell_call(row=i, col=0, vid=subvid, **kwargs)
       
   284         self.w(u'</ul>\n')
       
   285         if title:
       
   286             self.w(u'</div>\n')
       
   287 
       
   288     def cell_call(self, row, col=0, vid=None, **kwargs):
       
   289         self.w(u'<li>')
       
   290         self.wview(self.item_vid, self.cw_rset, row=row, col=col, vid=vid, **kwargs)
       
   291         self.w(u'</li>\n')
       
   292 
       
   293 
       
   294 class ListItemView(EntityView):
       
   295     __regid__ = 'listitem'
       
   296 
       
   297     @property
       
   298     def redirect_vid(self):
       
   299         if self._cw.search_state[0] == 'normal':
       
   300             return 'outofcontext'
       
   301         return 'outofcontext-search'
       
   302 
       
   303     def cell_call(self, row, col, vid=None, **kwargs):
       
   304         if not vid:
       
   305             vid = self.redirect_vid
       
   306         try:
       
   307             self.wview(vid, self.cw_rset, row=row, col=col, **kwargs)
       
   308         except NoSelectableObject:
       
   309             if vid == self.redirect_vid:
       
   310                 raise
       
   311             self.wview(self.redirect_vid, self.cw_rset, row=row, col=col, **kwargs)
       
   312 
       
   313 
       
   314 class SimpleListView(ListItemView):
       
   315     """list without bullets"""
       
   316     __regid__ = 'simplelist'
       
   317     redirect_vid = 'incontext'
       
   318 
       
   319     def call(self, subvid=None, **kwargs):
       
   320         """display a list of entities by calling their <item_vid> view
       
   321 
       
   322         :param listid: the DOM id to use for the root element
       
   323         """
       
   324         if subvid is None and 'vid' in kwargs:
       
   325             warn("should give a 'subvid' argument instead of 'vid'",
       
   326                  DeprecationWarning, stacklevel=2)
       
   327         else:
       
   328             kwargs['vid'] = subvid
       
   329         return super(SimpleListView, self).call(**kwargs)
       
   330 
       
   331 
       
   332 class SameETypeListView(EntityView):
       
   333     """list of entities of the same type, when asked explicitly for same etype list
       
   334     view (for instance, display gallery if only images)
       
   335     """
       
   336     __regid__ = 'sameetypelist'
       
   337     __select__ = EntityView.__select__ & one_etype_rset()
       
   338     item_vid = 'sameetypelistitem'
       
   339 
       
   340     @property
       
   341     def title(self):
       
   342         etype = iter(self.cw_rset.column_types(0)).next()
       
   343         return display_name(self._cw, etype, form='plural')
       
   344 
       
   345     def call(self, **kwargs):
       
   346         """display a list of entities by calling their <item_vid> view"""
       
   347         showtitle = kwargs.pop('showtitle', not 'vtitle' in self._cw.form)
       
   348         if showtitle:
       
   349             self.w(u'<h1>%s</h1>' % self.title)
       
   350         super(SameETypeListView, self).call(**kwargs)
       
   351 
       
   352     def cell_call(self, row, col=0, **kwargs):
       
   353         self.wview(self.item_vid, self.cw_rset, row=row, col=col, **kwargs)
       
   354 
       
   355 
       
   356 class SameETypeListItemView(EntityView):
       
   357     __regid__ = 'sameetypelistitem'
       
   358 
       
   359     def cell_call(self, row, col, **kwargs):
       
   360         self.wview('listitem', self.cw_rset, row=row, col=col, **kwargs)
       
   361 
       
   362 
       
   363 class CSVView(SimpleListView):
       
   364     __regid__ = 'csv'
       
   365     redirect_vid = 'incontext'
       
   366 
       
   367     def call(self, subvid=None, **kwargs):
       
   368         if subvid is None and 'vid' in kwargs:
       
   369             warn("[3.9] should give a 'subvid' argument instead of 'vid'",
       
   370                  DeprecationWarning, stacklevel=2)
       
   371         else:
       
   372             kwargs['vid'] = subvid
       
   373         rset = self.cw_rset
       
   374         for i in xrange(len(rset)):
       
   375             self.cell_call(i, 0, **kwargs)
       
   376             if i < rset.rowcount-1:
       
   377                 self.w(u", ")
       
   378 
       
   379 
       
   380 class TreeItemView(ListItemView):
   515 class TreeItemView(ListItemView):
   381     __regid__ = 'treeitem'
   516     __regid__ = 'treeitem'
   382 
   517 
   383     def cell_call(self, row, col):
   518     def cell_call(self, row, col):
   384         self.wview('incontext', self.cw_rset, row=row, col=col)
   519         self.wview('incontext', self.cw_rset, row=row, col=col)
       
   520 
   385 
   521 
   386 class TextSearchResultView(EntityView):
   522 class TextSearchResultView(EntityView):
   387     """this view is used to display full-text search
   523     """this view is used to display full-text search
   388 
   524 
   389     It tries to highlight part of data where the search word appears.
   525     It tries to highlight part of data where the search word appears.
   403         for attr in entity.e_schema.indexable_attributes():
   539         for attr in entity.e_schema.indexable_attributes():
   404             try:
   540             try:
   405                 value = xml_escape(entity.printable_value(attr, format='text/plain').lower())
   541                 value = xml_escape(entity.printable_value(attr, format='text/plain').lower())
   406             except TransformError, ex:
   542             except TransformError, ex:
   407                 continue
   543                 continue
   408             except:
   544             except Exception:
   409                 continue
   545                 continue
   410             if searched in value:
   546             if searched in value:
   411                 contexts = []
   547                 contexts = []
   412                 for ctx in value.split(searched):
   548                 for ctx in value.split(searched):
   413                     if len(ctx) > 30:
   549                     if len(ctx) > 30:
   423     __regid__ = 'tooltip'
   559     __regid__ = 'tooltip'
   424     def cell_call(self, row, col):
   560     def cell_call(self, row, col):
   425         self.wview('oneline', self.cw_rset, row=row, col=col)
   561         self.wview('oneline', self.cw_rset, row=row, col=col)
   426 
   562 
   427 
   563 
   428 # XXX bw compat
       
   429 
       
   430 from logilab.common.deprecation import class_moved
       
   431 
       
   432 try:
       
   433     from cubicweb.web.views.tableview import TableView
       
   434     TableView = class_moved(TableView)
       
   435 except ImportError:
       
   436     pass # gae has no tableview module (yet)
       
   437 
       
   438 from cubicweb.web.views import boxes, xmlrss, primary
       
   439 PrimaryView = class_moved(primary.PrimaryView)
       
   440 SideBoxView = class_moved(boxes.SideBoxView)
       
   441 XmlView = class_moved(xmlrss.XMLView)
       
   442 XmlItemView = class_moved(xmlrss.XMLItemView)
       
   443 XmlRsetView = class_moved(xmlrss.XMLRsetView)
       
   444 RssView = class_moved(xmlrss.RSSView)
       
   445 RssItemView = class_moved(xmlrss.RSSItemView)
       
   446 
       
   447 
       
   448 class GroupByView(EntityView):
   564 class GroupByView(EntityView):
   449     """grouped view of a result set. The `group_key` method return the group
   565     """grouped view of a result set. The `group_key` method return the group
   450     key of an entities (a string or tuple of string).
   566     key of an entities (a string or tuple of string).
   451 
   567 
   452     For each group, display a link to entities of this group by generating url
   568     For each group, display a link to entities of this group by generating url
   453     like <basepath>/<key> or <basepath>/<key item 1>/<key item 2>.
   569     like <basepath>/<key> or <basepath>/<key item 1>/<key item 2>.
   454     """
   570     """
   455     __abstrack__ = True
   571     __abstract__ = True
   456     __select__ = EntityView.__select__ & match_kwargs('basepath')
   572     __select__ = EntityView.__select__ & match_kwargs('basepath')
   457     entity_attribute = None
   573     entity_attribute = None
   458     reversed = False
   574     reversed = False
   459 
   575 
   460     def index_url(self, basepath, key, **kwargs):
   576     def index_url(self, basepath, key, **kwargs):
   548                                for etype in etypes),
   664                                for etype in etypes),
   549             'author': label}
   665             'author': label}
   550         url = self.index_url(basepath, key, vtitle=vtitle)
   666         url = self.index_url(basepath, key, vtitle=vtitle)
   551         title = self._cw._('archive for %(author)s') % {'author': key}
   667         title = self._cw._('archive for %(author)s') % {'author': key}
   552         return tags.a(label, href=url, title=title)
   668         return tags.a(label, href=url, title=title)
       
   669 
       
   670 
       
   671 # bw compat ####################################################################
       
   672 
       
   673 from logilab.common.deprecation import class_moved, class_deprecated
       
   674 
       
   675 from cubicweb.web.views import boxes, xmlrss, primary, tableview
       
   676 PrimaryView = class_moved(primary.PrimaryView)
       
   677 SideBoxView = class_moved(boxes.SideBoxView)
       
   678 XmlView = class_moved(xmlrss.XMLView)
       
   679 XmlItemView = class_moved(xmlrss.XMLItemView)
       
   680 XmlRsetView = class_moved(xmlrss.XMLRsetView)
       
   681 RssView = class_moved(xmlrss.RSSView)
       
   682 RssItemView = class_moved(xmlrss.RSSItemView)
       
   683 TableView = class_moved(tableview.TableView)
       
   684 
       
   685 
       
   686 class SecondaryView(EntityView):
       
   687     __metaclass__ = class_deprecated
       
   688     __deprecation_warning__ = '[3.9] the secondary view is deprecated, use one of oneline/incontext/outofcontext'
       
   689     __regid__ = 'secondary'
       
   690 
       
   691     def cell_call(self, row, col, **kwargs):
       
   692         entity = self.cw_rset.get_entity(row, col)
       
   693         self.w(u'&#160;')
       
   694         self.wview('oneline', self.cw_rset, row=row, col=col)