web/views/navigation.py
changeset 0 b97547f5f1fa
child 237 3df2e0ae2eba
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """navigation components definition for CubicWeb web client
       
     2 
       
     3 :organization: Logilab
       
     4 :copyright: 2001-2008 LOGILAB S.A. (Paris, FRANCE), all rights reserved.
       
     5 :contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
       
     6 """
       
     7 __docformat__ = "restructuredtext en"
       
     8 
       
     9 from rql.nodes import VariableRef, Constant
       
    10 
       
    11 from logilab.mtconverter import html_escape
       
    12 
       
    13 from cubicweb.interfaces import IPrevNext
       
    14 from cubicweb.common.selectors import (largerset_selector, sortedrset_selector,
       
    15                                     primaryview_selector, contextprop_selector,
       
    16                                     onelinerset_selector, interface_selector)
       
    17 from cubicweb.common.uilib import cut
       
    18 from cubicweb.web.component import EntityVComponent, NavigationComponent
       
    19 
       
    20 _ = unicode
       
    21 
       
    22 
       
    23 class PageNavigation(NavigationComponent):
       
    24 
       
    25     def call(self):
       
    26         """displays a resultset by page"""
       
    27         w = self.w
       
    28         req = self.req
       
    29         rset = self.rset
       
    30         page_size = self.page_size
       
    31         start = 0
       
    32         blocklist = []
       
    33         params = dict(req.form)
       
    34         self.clean_params(params)
       
    35         basepath = req.relative_path(includeparams=False)
       
    36         while start < rset.rowcount:
       
    37             stop = min(start + page_size - 1, rset.rowcount - 1)
       
    38             blocklist.append(self.page_link(basepath, params, start, stop,
       
    39                                             u'%s - %s' % (start+1, stop+1)))
       
    40             start = stop + 1
       
    41         w(u'<div class="pagination">')
       
    42         w(u'%s&nbsp;' % self.previous_link(params))
       
    43         w(u'[&nbsp;%s&nbsp;]' % u'&nbsp;| '.join(blocklist))
       
    44         w(u'&nbsp;%s' % self.next_link(params))
       
    45         w(u'</div>')
       
    46 
       
    47     
       
    48 class SortedNavigation(NavigationComponent):
       
    49     """sorted navigation apply if navigation is needed (according to page size)
       
    50     and if the result set is sorted
       
    51     """
       
    52     __selectors__ = (largerset_selector, sortedrset_selector)
       
    53     
       
    54     # number of considered chars to build page links
       
    55     nb_chars = 5
       
    56     
       
    57     def display_func(self, rset, col, attrname):
       
    58         req = self.req
       
    59         if attrname is not None:
       
    60             def index_display(row):
       
    61                 entity = rset.get_entity(row, col)
       
    62                 return entity.printable_value(attrname, format='text/plain')
       
    63         elif self.schema.eschema(rset.description[0][col]).is_final():
       
    64             def index_display(row):
       
    65                 return unicode(rset[row][col])
       
    66         else:
       
    67             def index_display(row):
       
    68                 return rset.get_entity(row, col).view('text')
       
    69         return index_display
       
    70     
       
    71     def call(self):
       
    72         """displays links to navigate accross pages of a result set
       
    73 
       
    74         Displayed result is done according to a variable on which the sort
       
    75         is done, and looks like:
       
    76         [ana - cro] | [cro - ghe] | ... | [tim - zou]
       
    77         """
       
    78         w = self.w
       
    79         rset = self.rset
       
    80         page_size = self.page_size
       
    81         rschema = self.schema.rschema
       
    82         # attrname = the name of attribute according to which the sort
       
    83         # is done if any
       
    84         for sorterm in rset.syntax_tree().children[0].orderby:
       
    85             if isinstance(sorterm.term, Constant):
       
    86                 col = sorterm.term.value - 1
       
    87                 index_display = self.display_func(rset, col, None)
       
    88                 break
       
    89             var = sorterm.term.get_nodes(VariableRef)[0].variable
       
    90             col = None
       
    91             for ref in var.references():
       
    92                 rel = ref.relation()
       
    93                 if rel is None:
       
    94                     continue
       
    95                 attrname = rel.r_type
       
    96                 if attrname == 'is':
       
    97                     continue
       
    98                 if not rschema(attrname).is_final():
       
    99                     col = var.selected_index()
       
   100                     attrname = None
       
   101                 if col is None:
       
   102                     # final relation or not selected non final relation
       
   103                     if var is rel.children[0]:
       
   104                         relvar = rel.children[1].children[0].get_nodes(VariableRef)[0]
       
   105                     else:
       
   106                         relvar = rel.children[0].variable
       
   107                     col = relvar.selected_index()
       
   108                 if col is not None:
       
   109                     break
       
   110             else:
       
   111                 # no relation but maybe usable anyway if selected
       
   112                 col = var.selected_index()
       
   113                 attrname = None
       
   114             if col is not None:
       
   115                 index_display = self.display_func(rset, col, attrname)
       
   116                 break
       
   117         else:
       
   118             # nothing usable found, use the first column
       
   119             index_display = self.display_func(rset, 0, None)
       
   120         blocklist = []
       
   121         params = dict(self.req.form)
       
   122         self.clean_params(params)
       
   123         start = 0
       
   124         basepath = self.req.relative_path(includeparams=False)
       
   125         while start < rset.rowcount:
       
   126             stop = min(start + page_size - 1, rset.rowcount - 1)
       
   127             cell = self.format_link_content(index_display(start), index_display(stop))
       
   128             blocklist.append(self.page_link(basepath, params, start, stop, cell))
       
   129             start = stop + 1
       
   130         self.write_links(params, blocklist)
       
   131 
       
   132     def format_link_content(self, startstr, stopstr):
       
   133         text = u'%s - %s' % (startstr.lower()[:self.nb_chars],
       
   134                              stopstr.lower()[:self.nb_chars])
       
   135         return html_escape(text)
       
   136 
       
   137     def write_links(self, params, blocklist):
       
   138         self.w(u'<div class="pagination">')
       
   139         self.w(u'%s&nbsp;' % self.previous_link(params))
       
   140         self.w(u'[&nbsp;%s&nbsp;]' % u'&nbsp;| '.join(blocklist))
       
   141         self.w(u'&nbsp;%s' % self.next_link(params))
       
   142         self.w(u'</div>')
       
   143 
       
   144 
       
   145 def limit_rset_using_paged_nav(self, req, rset, w, forcedisplay=False, show_all_option=True):
       
   146     showall = forcedisplay or req.form.get('__force_display') is not None
       
   147     nav = not showall and self.vreg.select_component('navigation', req, rset)
       
   148     if nav:
       
   149         # get boundaries before component rendering
       
   150         start, stop = nav.page_boundaries()
       
   151         nav.dispatch(w=w)
       
   152         params = dict(req.form)
       
   153         nav.clean_params(params)
       
   154         # make a link to see them all
       
   155         if show_all_option:
       
   156             url = html_escape(self.build_url(__force_display=1, **params))
       
   157             w(u'<p><a href="%s">%s</a></p>\n'
       
   158               % (url, req._('show %s results') % len(rset)))
       
   159         rset.limit(offset=start, limit=stop-start, inplace=True)
       
   160 
       
   161 
       
   162 # monkey patch base View class to add a .pagination(req, rset, w, forcedisplay)
       
   163 # method to be called on view's result set and printing pages index in the view
       
   164 from cubicweb.common.view import View
       
   165 # XXX deprecated, use paginate
       
   166 View.pagination = limit_rset_using_paged_nav
       
   167 
       
   168 def paginate(view, show_all_option=True, w=None):
       
   169     limit_rset_using_paged_nav(view, view.req, view.rset, w or view.w,
       
   170                                not view.need_navigation, show_all_option)
       
   171 View.paginate = paginate
       
   172 
       
   173 class NextPrevNavigationComponent(EntityVComponent):
       
   174     id = 'prevnext'
       
   175     # register msg not generated since no entity implements IPrevNext in cubicweb
       
   176     # itself
       
   177     title = _('contentnavigation_prevnext')
       
   178     help = _('contentnavigation_prevnext_description')
       
   179     __selectors__ = (onelinerset_selector, primaryview_selector,
       
   180                      contextprop_selector, interface_selector)
       
   181     accepts_interfaces = (IPrevNext,)
       
   182     context = 'navbottom'
       
   183     order = 10
       
   184     def call(self, view=None):
       
   185         entity = self.entity(0)
       
   186         previous = entity.previous_entity()
       
   187         next = entity.next_entity()
       
   188         if previous or next:
       
   189             textsize = self.req.property_value('navigation.short-line-size')
       
   190             self.w(u'<div class="prevnext">')
       
   191             if previous:
       
   192                 self.w(u'<div class="previousEntity left">')
       
   193                 self.w(self.previous_link(previous, textsize))
       
   194                 self.w(u'</div>')
       
   195                 self.req.html_headers.add_raw('<link rel="prev" href="%s" />'
       
   196                                               % html_escape(previous.absolute_url()))
       
   197             if next:
       
   198                 self.w(u'<div class="nextEntity right">')
       
   199                 self.w(self.next_link(next, textsize))
       
   200                 self.w(u'</div>')
       
   201                 self.req.html_headers.add_raw('<link rel="next" href="%s" />'
       
   202                                               % html_escape(next.absolute_url()))
       
   203             self.w(u'</div>')
       
   204             self.w(u'<div class="clear"></div>')
       
   205 
       
   206     def previous_link(self, previous, textsize):
       
   207         return u'<a href="%s" title="%s">&lt;&lt; %s</a>' % (
       
   208             html_escape(previous.absolute_url()),
       
   209             self.req._('i18nprevnext_previous'),
       
   210             html_escape(cut(previous.dc_title(), textsize)))
       
   211     
       
   212     def next_link(self, next, textsize):
       
   213         return u'<a href="%s" title="%s">%s &gt;&gt;</a>' % (
       
   214             html_escape(next.absolute_url()),
       
   215             self.req._('i18nprevnext_next'),
       
   216             html_escape(cut(next.dc_title(), textsize)))