common/view.py
changeset 543 c0f2b6378f70
parent 522 385ce5e0b30b
child 632 3a394a90b702
child 962 1cc3c240b2d5
equal deleted inserted replaced
542:46d4ff5aa917 543:c0f2b6378f70
    51  cubicweb:displayactions    CDATA   #IMPLIED
    51  cubicweb:displayactions    CDATA   #IMPLIED
    52  cubicweb:fallbackvid       CDATA   #IMPLIED
    52  cubicweb:fallbackvid       CDATA   #IMPLIED
    53  cubicweb:vid               CDATA   #IMPLIED
    53  cubicweb:vid               CDATA   #IMPLIED
    54  cubicweb:rql               CDATA   #IMPLIED
    54  cubicweb:rql               CDATA   #IMPLIED
    55  cubicweb:actualrql         CDATA   #IMPLIED
    55  cubicweb:actualrql         CDATA   #IMPLIED
    56  cubicweb:rooteid           CDATA   #IMPLIED   
    56  cubicweb:rooteid           CDATA   #IMPLIED
    57  cubicweb:dataurl           CDATA   #IMPLIED
    57  cubicweb:dataurl           CDATA   #IMPLIED
    58  cubicweb:size              CDATA   #IMPLIED   
    58  cubicweb:size              CDATA   #IMPLIED
    59  cubicweb:tlunit            CDATA   #IMPLIED
    59  cubicweb:tlunit            CDATA   #IMPLIED
    60  cubicweb:loadurl           CDATA   #IMPLIED
    60  cubicweb:loadurl           CDATA   #IMPLIED
    61  cubicweb:lazyloadurl       CDATA   #IMPLIED
       
    62  cubicweb:uselabel          CDATA   #IMPLIED
    61  cubicweb:uselabel          CDATA   #IMPLIED
    63  cubicweb:facetargs         CDATA   #IMPLIED
    62  cubicweb:facetargs         CDATA   #IMPLIED
    64  cubicweb:facetName         CDATA   #IMPLIED
    63  cubicweb:facetName         CDATA   #IMPLIED
    65   "> ] '''
    64   "> ] '''
    66 
    65 
    72     """abstract view class, used as base for every renderable object such
    71     """abstract view class, used as base for every renderable object such
    73     as views, templates, some components...web
    72     as views, templates, some components...web
    74 
    73 
    75     A view is instantiated to render a [part of a] result set. View
    74     A view is instantiated to render a [part of a] result set. View
    76     subclasses may be parametred using the following class attributes:
    75     subclasses may be parametred using the following class attributes:
    77     
    76 
    78     * `templatable` indicates if the view may be embeded in a main
    77     * `templatable` indicates if the view may be embeded in a main
    79       template or if it has to be rendered standalone (i.e. XML for
    78       template or if it has to be rendered standalone (i.e. XML for
    80       instance)
    79       instance)
    81     * if the view is not templatable, it should set the `content_type` class
    80     * if the view is not templatable, it should set the `content_type` class
    82       attribute to the correct MIME type (text/xhtml by default)
    81       attribute to the correct MIME type (text/xhtml by default)
    86     At instantiation time, the standard `req`, `rset`, and `cursor`
    85     At instantiation time, the standard `req`, `rset`, and `cursor`
    87     attributes are added and the `w` attribute will be set at rendering
    86     attributes are added and the `w` attribute will be set at rendering
    88     time to a write function to use.
    87     time to a write function to use.
    89     """
    88     """
    90     __registry__ = 'views'
    89     __registry__ = 'views'
    91     
    90 
    92     templatable = True
    91     templatable = True
    93     need_navigation = True
    92     need_navigation = True
    94     # content_type = 'application/xhtml+xml' # text/xhtml'
    93     # content_type = 'application/xhtml+xml' # text/xhtml'
    95     binary = False
    94     binary = False
    96     add_to_breadcrumbs = True
    95     add_to_breadcrumbs = True
    97     category = 'view'
    96     category = 'view'
    98     
    97 
    99     def __init__(self, req, rset):
    98     def __init__(self, req, rset):
   100         super(View, self).__init__(req, rset)
    99         super(View, self).__init__(req, rset)
   101         self.w = None
   100         self.w = None
   102 
   101 
   103     @property
   102     @property
   104     def content_type(self):
   103     def content_type(self):
   105         if self.req.xhtml_browser():
   104         if self.req.xhtml_browser():
   106             return 'application/xhtml+xml'
   105             return 'application/xhtml+xml'
   107         return 'text/html'
   106         return 'text/html'
   108     
   107 
   109     def set_stream(self, w=None):
   108     def set_stream(self, w=None):
   110         if self.w is not None:
   109         if self.w is not None:
   111             return
   110             return
   112         if w is None:
   111         if w is None:
   113             if self.binary:
   112             if self.binary:
   119             stream = None
   118             stream = None
   120         self.w = w
   119         self.w = w
   121         return stream
   120         return stream
   122 
   121 
   123     # main view interface #####################################################
   122     # main view interface #####################################################
   124             
   123 
   125     def dispatch(self, w=None, **context):
   124     def dispatch(self, w=None, **context):
   126         """called to render a view object for a result set.
   125         """called to render a view object for a result set.
   127 
   126 
   128         This method is a dispatched to an actual method selected
   127         This method is a dispatched to an actual method selected
   129         according to optional row and col parameters, which are locating
   128         according to optional row and col parameters, which are locating
   130         a particular row or cell in the result set:
   129         a particular row or cell in the result set:
   131         
   130 
   132         * if row [and col] are specified, `cell_call` is called
   131         * if row [and col] are specified, `cell_call` is called
   133         * if none of them is supplied, the view is considered to apply on
   132         * if none of them is supplied, the view is considered to apply on
   134           the whole result set (which may be None in this case), `call` is
   133           the whole result set (which may be None in this case), `call` is
   135           called
   134           called
   136         """
   135         """
   148             return self._stream.getvalue()
   147             return self._stream.getvalue()
   149 
   148 
   150     # should default .call() method add a <div classs="section"> around each
   149     # should default .call() method add a <div classs="section"> around each
   151     # rset item
   150     # rset item
   152     add_div_section = True
   151     add_div_section = True
   153     
   152 
   154     def call(self, **kwargs):
   153     def call(self, **kwargs):
   155         """the view is called for an entire result set, by default loop
   154         """the view is called for an entire result set, by default loop
   156         other rows of the result set and call the same view on the
   155         other rows of the result set and call the same view on the
   157         particular row
   156         particular row
   158 
   157 
   170                 self.w(u"</div>")
   169                 self.w(u"</div>")
   171 
   170 
   172     def cell_call(self, row, col, **kwargs):
   171     def cell_call(self, row, col, **kwargs):
   173         """the view is called for a particular result set cell"""
   172         """the view is called for a particular result set cell"""
   174         raise NotImplementedError, self
   173         raise NotImplementedError, self
   175         
   174 
   176     def linkable(self):
   175     def linkable(self):
   177         """return True if the view may be linked in a menu
   176         """return True if the view may be linked in a menu
   178         
   177 
   179         by default views without title are not meant to be displayed
   178         by default views without title are not meant to be displayed
   180         """
   179         """
   181         if not getattr(self, 'title', None):
   180         if not getattr(self, 'title', None):
   182             return False
   181             return False
   183         return True
   182         return True
   184 
   183 
   185     def is_primary(self):
   184     def is_primary(self):
   186         return self.id == 'primary'
   185         return self.id == 'primary'
   187     
   186 
   188     def url(self):
   187     def url(self):
   189         """return the url associated with this view. Should not be
   188         """return the url associated with this view. Should not be
   190         necessary for non linkable views, but a default implementation
   189         necessary for non linkable views, but a default implementation
   191         is provided anyway.
   190         is provided anyway.
   192         """
   191         """
   198     def set_request_content_type(self):
   197     def set_request_content_type(self):
   199         """set the content type returned by this view"""
   198         """set the content type returned by this view"""
   200         self.req.set_content_type(self.content_type)
   199         self.req.set_content_type(self.content_type)
   201 
   200 
   202     # view utilities ##########################################################
   201     # view utilities ##########################################################
   203     
   202 
   204     def view(self, __vid, rset, __fallback_vid=None, **kwargs):
   203     def view(self, __vid, rset, __fallback_vid=None, **kwargs):
   205         """shortcut to self.vreg.render method avoiding to pass self.req"""
   204         """shortcut to self.vreg.render method avoiding to pass self.req"""
   206         try:
   205         try:
   207             view = self.vreg.select_view(__vid, self.req, rset, **kwargs)
   206             view = self.vreg.select_view(__vid, self.req, rset, **kwargs)
   208         except NoSelectableObject:
   207         except NoSelectableObject:
   209             if __fallback_vid is None:
   208             if __fallback_vid is None:
   210                 raise
   209                 raise
   211             view = self.vreg.select_view(__fallback_vid, self.req, rset, **kwargs)
   210             view = self.vreg.select_view(__fallback_vid, self.req, rset, **kwargs)
   212         return view.dispatch(**kwargs)
   211         return view.dispatch(**kwargs)
   213     
   212 
   214     def wview(self, __vid, rset, __fallback_vid=None, **kwargs):
   213     def wview(self, __vid, rset, __fallback_vid=None, **kwargs):
   215         """shortcut to self.view method automatically passing self.w as argument
   214         """shortcut to self.view method automatically passing self.w as argument
   216         """
   215         """
   217         self.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
   216         self.view(__vid, rset, __fallback_vid, w=self.w, **kwargs)
   218 
   217 
   235                                          row=row)
   234                                          row=row)
   236         if action:
   235         if action:
   237             label = label or self.req._(action.title)
   236             label = label or self.req._(action.title)
   238             return u'<a href="%s">%s</a>' % (html_escape(action.url()), label)
   237             return u'<a href="%s">%s</a>' % (html_escape(action.url()), label)
   239         return u''
   238         return u''
   240     
   239 
   241     def html_headers(self):
   240     def html_headers(self):
   242         """return a list of html headers (eg something to be inserted between
   241         """return a list of html headers (eg something to be inserted between
   243         <head> and </head> of the returned page
   242         <head> and </head> of the returned page
   244 
   243 
   245         by default return a meta tag to disable robot indexation of the page
   244         by default return a meta tag to disable robot indexation of the page
   246         """
   245         """
   247         return [NOINDEX]
   246         return [NOINDEX]
   248     
   247 
   249     def page_title(self):
   248     def page_title(self):
   250         """returns a title according to the result set - used for the
   249         """returns a title according to the result set - used for the
   251         title in the HTML header
   250         title in the HTML header
   252         """
   251         """
   253         vtitle = self.req.form.get('vtitle')
   252         vtitle = self.req.form.get('vtitle')
   293 
   292 
   294     def create_url(self, etype, **kwargs):
   293     def create_url(self, etype, **kwargs):
   295         """ return the url of the entity creation form for a given entity type"""
   294         """ return the url of the entity creation form for a given entity type"""
   296         return self.req.build_url('add/%s'%etype, **kwargs)
   295         return self.req.build_url('add/%s'%etype, **kwargs)
   297 
   296 
   298         
   297 
   299 # concrete views base classes #################################################
   298 # concrete views base classes #################################################
   300 
   299 
   301 class EntityView(View):
   300 class EntityView(View):
   302     """base class for views applying on an entity (i.e. uniform result set)
   301     """base class for views applying on an entity (i.e. uniform result set)
   303     """
   302     """
   304     __registerer__ = accepts_registerer
   303     __registerer__ = accepts_registerer
   305     __selectors__ = (accept,)
   304     __selectors__ = (accept,)
   306     category = 'entityview'
   305     category = 'entityview'
   307     
   306 
   308     def field(self, label, value, row=True, show_label=True, w=None, tr=True):
   307     def field(self, label, value, row=True, show_label=True, w=None, tr=True):
   309         """ read-only field """
   308         """ read-only field """
   310         if w is None:
   309         if w is None:
   311             w = self.w
   310             w = self.w
   312         if row:
   311         if row:
   317             w(u'<span class="label">%s</span>' % label)
   316             w(u'<span class="label">%s</span>' % label)
   318         w(u'<div class="field">%s</div>' % value)
   317         w(u'<div class="field">%s</div>' % value)
   319         if row:
   318         if row:
   320             w(u'</div>')
   319             w(u'</div>')
   321 
   320 
   322         
   321 
   323 class StartupView(View):
   322 class StartupView(View):
   324     """base class for views which doesn't need a particular result set
   323     """base class for views which doesn't need a particular result set
   325     to be displayed (so they can always be displayed !)
   324     to be displayed (so they can always be displayed !)
   326     """
   325     """
   327     __registerer__ = priority_registerer
   326     __registerer__ = priority_registerer
   328     __selectors__ = (match_user_group, none_rset)
   327     __selectors__ = (match_user_group, none_rset)
   329     require_groups = ()
   328     require_groups = ()
   330     category = 'startupview'
   329     category = 'startupview'
   331     
   330 
   332     def url(self):
   331     def url(self):
   333         """return the url associated with this view. We can omit rql here"""
   332         """return the url associated with this view. We can omit rql here"""
   334         return self.build_url('view', vid=self.id)
   333         return self.build_url('view', vid=self.id)
   335 
   334 
   336     def html_headers(self):
   335     def html_headers(self):
   346     """base class for entity views which may also be applied to None
   345     """base class for entity views which may also be applied to None
   347     result set (usually a default rql is provided by the view class)
   346     result set (usually a default rql is provided by the view class)
   348     """
   347     """
   349     __registerer__ = accepts_registerer
   348     __registerer__ = accepts_registerer
   350     __selectors__ = (chainfirst(none_rset, accept),)
   349     __selectors__ = (chainfirst(none_rset, accept),)
   351     
   350 
   352     default_rql = None
   351     default_rql = None
   353     
   352 
   354     def __init__(self, req, rset):
   353     def __init__(self, req, rset):
   355         super(EntityStartupView, self).__init__(req, rset)
   354         super(EntityStartupView, self).__init__(req, rset)
   356         if rset is None:
   355         if rset is None:
   357             # this instance is not in the "entityview" category
   356             # this instance is not in the "entityview" category
   358             self.category = 'startupview'
   357             self.category = 'startupview'
   359 
   358 
   360     def startup_rql(self):
   359     def startup_rql(self):
   361         """return some rql to be executedif the result set is None"""
   360         """return some rql to be executedif the result set is None"""
   362         return self.default_rql
   361         return self.default_rql
   363     
   362 
   364     def call(self, **kwargs):
   363     def call(self, **kwargs):
   365         """override call to execute rql returned by the .startup_rql
   364         """override call to execute rql returned by the .startup_rql
   366         method if necessary
   365         method if necessary
   367         """
   366         """
   368         if self.rset is None:
   367         if self.rset is None:
   377         """
   376         """
   378         if not self.__select__(self.req, self.rset):
   377         if not self.__select__(self.req, self.rset):
   379             return self.build_url(vid=self.id)
   378             return self.build_url(vid=self.id)
   380         return super(EntityStartupView, self).url()
   379         return super(EntityStartupView, self).url()
   381 
   380 
   382     
   381 
   383 class AnyRsetView(View):
   382 class AnyRsetView(View):
   384     """base class for views applying on any non empty result sets"""
   383     """base class for views applying on any non empty result sets"""
   385     __registerer__ = priority_registerer
   384     __registerer__ = priority_registerer
   386     __selectors__ = (nonempty_rset,)
   385     __selectors__ = (nonempty_rset,)
   387     
   386 
   388     category = 'anyrsetview'
   387     category = 'anyrsetview'
   389     
   388 
   390     def columns_labels(self, tr=True):
   389     def columns_labels(self, tr=True):
   391         if tr:
   390         if tr:
   392             translate = display_name
   391             translate = display_name
   393         else:
   392         else:
   394             translate = lambda req, val: val
   393             translate = lambda req, val: val
   401                                  for et in self.rset.column_types(colindex))
   400                                  for et in self.rset.column_types(colindex))
   402             else:
   401             else:
   403                 label = translate(self.req, attr)
   402                 label = translate(self.req, attr)
   404             labels.append(label)
   403             labels.append(label)
   405         return labels
   404         return labels
   406     
   405 
   407 
   406 
   408 class EmptyRsetView(View):
   407 class EmptyRsetView(View):
   409     """base class for views applying on any empty result sets"""
   408     """base class for views applying on any empty result sets"""
   410     __registerer__ = priority_registerer
   409     __registerer__ = priority_registerer
   411     __selectors__ = (empty_rset,)
   410     __selectors__ = (empty_rset,)
   420     __registry__ = 'templates'
   419     __registry__ = 'templates'
   421     __registerer__ = priority_registerer
   420     __registerer__ = priority_registerer
   422     __selectors__ = (match_user_group,)
   421     __selectors__ = (match_user_group,)
   423 
   422 
   424     require_groups = ()
   423     require_groups = ()
   425     
   424 
   426     def template(self, oid, **kwargs):
   425     def template(self, oid, **kwargs):
   427         """shortcut to self.registry.render method on the templates registry"""
   426         """shortcut to self.registry.render method on the templates registry"""
   428         w = kwargs.pop('w', self.w)
   427         w = kwargs.pop('w', self.w)
   429         self.vreg.render('templates', oid, self.req, w=w, **kwargs)
   428         self.vreg.render('templates', oid, self.req, w=w, **kwargs)
   430 
   429