web/component.py
changeset 0 b97547f5f1fa
child 237 3df2e0ae2eba
equal deleted inserted replaced
-1:000000000000 0:b97547f5f1fa
       
     1 """abstract component class and base 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 cubicweb.common.appobject import Component, SingletonComponent
       
    10 from cubicweb.common.utils import merge_dicts
       
    11 from cubicweb.common.view import VComponent, SingletonVComponent
       
    12 from cubicweb.common.registerers import action_registerer
       
    13 from cubicweb.common.selectors import (largerset_selector, onelinerset_selector, 
       
    14                                     etype_rtype_selector, rqlcondition_selector,
       
    15                                     accept_selector, contextprop_selector,
       
    16                                     primaryview_selector, accept_rtype_selector)
       
    17 from cubicweb.common.uilib import html_escape
       
    18 
       
    19 _ = unicode
       
    20 
       
    21 
       
    22 class EntityVComponent(VComponent):
       
    23     """abstract base class for additinal components displayed in content
       
    24     headers and footer according to:
       
    25     
       
    26     * the displayed entity's type
       
    27     * a context (currently 'header' or 'footer')
       
    28 
       
    29     it should be configured using .accepts, .etype, .rtype, .target and
       
    30     .context class attributes
       
    31     """
       
    32     
       
    33     __registry__ = 'contentnavigation'
       
    34     __registerer__ = action_registerer    
       
    35     __selectors__ = (onelinerset_selector, primaryview_selector,
       
    36                      contextprop_selector, etype_rtype_selector,
       
    37                      accept_rtype_selector, accept_selector,
       
    38                      rqlcondition_selector)
       
    39     
       
    40     property_defs = {
       
    41         _('visible'):  dict(type='Boolean', default=True,
       
    42                             help=_('display the box or not')),
       
    43         _('order'):    dict(type='Int', default=99,
       
    44                             help=_('display order of the component')),
       
    45         _('context'):  dict(type='String', default='header',
       
    46                             vocabulary=(_('navtop'), _('navbottom'), 
       
    47                                         _('navcontenttop'), _('navcontentbottom')),
       
    48                             #vocabulary=(_('header'), _('incontext'), _('footer')),
       
    49                             help=_('context where this component should be displayed')),
       
    50         _('htmlclass'):dict(type='String', default='mainRelated',
       
    51                             help=_('html class of the component')),
       
    52     }
       
    53     
       
    54     accepts = ('Any',)
       
    55     context = 'navcontentbottom' # 'footer' | 'header' | 'incontext'
       
    56     condition = None
       
    57     
       
    58     def call(self, view):
       
    59         raise RuntimeError()
       
    60 
       
    61     
       
    62 class NavigationComponent(VComponent):
       
    63     """abstract base class for navigation components"""
       
    64     __selectors__ = (largerset_selector,)
       
    65     id = 'navigation'
       
    66     page_size_property = 'navigation.page-size'
       
    67     start_param = '__start'
       
    68     stop_param = '__stop'
       
    69     page_link_templ = u'<span class="slice"><a href="%s" title="%s">%s</a></span>'
       
    70     selected_page_link_templ = u'<span class="selectedSlice"><a href="%s" title="%s">%s</a></span>'
       
    71     previous_page_link_templ = next_page_link_templ = page_link_templ
       
    72     no_previous_page_link = no_next_page_link = u''
       
    73     
       
    74     def __init__(self, req, rset):
       
    75         super(NavigationComponent, self).__init__(req, rset)
       
    76         self.starting_from = 0
       
    77         self.total = rset.rowcount
       
    78 
       
    79     def get_page_size(self):
       
    80         try:
       
    81             return self._page_size
       
    82         except AttributeError:
       
    83             self._page_size = self.req.property_value(self.page_size_property)
       
    84             return self._page_size
       
    85 
       
    86     def set_page_size(self, page_size):
       
    87         self._page_size = page_size
       
    88         
       
    89     page_size = property(get_page_size, set_page_size)
       
    90     
       
    91     def page_boundaries(self):
       
    92         try:
       
    93             stop = int(self.req.form[self.stop_param]) + 1
       
    94             start = int(self.req.form[self.start_param])
       
    95         except KeyError:
       
    96             start, stop = 0, self.page_size
       
    97         self.starting_from = start
       
    98         return start, stop
       
    99         
       
   100     def clean_params(self, params):
       
   101         if self.start_param in params:
       
   102             del params[self.start_param]
       
   103         if self.stop_param in params:
       
   104             del params[self.stop_param]
       
   105 
       
   106     def page_link(self, path, params, start, stop, content):
       
   107         url = self.build_url(path, **merge_dicts(params, {self.start_param : start,
       
   108                                                           self.stop_param : stop,}))
       
   109         url = html_escape(url)
       
   110         if start == self.starting_from:
       
   111             return self.selected_page_link_templ % (url, content, content)
       
   112         return self.page_link_templ % (url, content, content)
       
   113 
       
   114     def previous_link(self, params, content='&lt;&lt;', title=_('previous_results')):
       
   115         start = self.starting_from
       
   116         if not start :
       
   117             return self.no_previous_page_link
       
   118         start = max(0, start - self.page_size)
       
   119         stop = start + self.page_size - 1
       
   120         url = self.build_url(**merge_dicts(params, {self.start_param : start,
       
   121                                                     self.stop_param : stop,}))
       
   122         url = html_escape(url)
       
   123         return self.previous_page_link_templ % (url, title, content)
       
   124 
       
   125     def next_link(self, params, content='&gt;&gt;', title=_('next_results')):
       
   126         start = self.starting_from + self.page_size
       
   127         if start >= self.total:
       
   128             return self.no_next_page_link
       
   129         stop = start + self.page_size - 1
       
   130         url = self.build_url(**merge_dicts(params, {self.start_param : start,
       
   131                                                     self.stop_param : stop,}))
       
   132         url = html_escape(url)
       
   133         return self.next_page_link_templ % (url, title, content)
       
   134 
       
   135 
       
   136 class RelatedObjectsVComponent(EntityVComponent):
       
   137     """a section to display some related entities"""
       
   138     __selectors__ = (onelinerset_selector, primaryview_selector,
       
   139                      etype_rtype_selector, accept_rtype_selector,
       
   140                      contextprop_selector, accept_selector)
       
   141     vid = 'list'
       
   142 
       
   143     def rql(self):
       
   144         """override this method if you want to use a custom rql query.
       
   145         """
       
   146         return None
       
   147     
       
   148     def call(self, view=None):
       
   149         rql = self.rql()
       
   150         if rql is None:
       
   151             entity = self.rset.get_entity(0, 0)
       
   152             if self.target == 'object':
       
   153                 role = 'subject'
       
   154             else:
       
   155                 role = 'object'
       
   156             rset = entity.related(self.rtype, role)
       
   157         else:
       
   158             eid = self.rset[0][0]
       
   159             rset = self.req.execute(self.rql(), {'x': eid}, 'x')
       
   160         if not rset.rowcount:
       
   161             return
       
   162         self.w(u'<div class="%s">' % self.div_class())
       
   163         self.wview(self.vid, rset, title=self.req._(self.title).capitalize())
       
   164         self.w(u'</div>')