main template refactoring tls-sprint
authorAdrien Di Mascio <Adrien.DiMascio@logilab.fr>
Wed, 18 Feb 2009 19:19:57 +0100
branchtls-sprint
changeset 823 cb8ccbef8fa5
parent 822 e3d8db01f3f5
child 824 a5e6acffde30
child 826 51cb3d85c059
main template refactoring there are 2 main templates : - one for non templatable views (xml, binaries, etc.) - one for the other views The right one is chosen according to its selectors
cwvreg.py
devtools/testlib.py
web/application.py
web/views/basecontrollers.py
web/views/basetemplates.py
web/webconfig.py
--- a/cwvreg.py	Wed Feb 18 18:06:41 2009 +0100
+++ b/cwvreg.py	Wed Feb 18 19:19:57 2009 +0100
@@ -193,7 +193,7 @@
         selected = self.select(objclss, req, rset, **context)
         return selected.dispatch(**context)
         
-    def main_template(self, req, oid='main', **context):
+    def main_template(self, req, oid='main-template', **context):
         """display query by calling the given template (default to main),
         and returning the output as a string instead of requiring the [w]rite
         method as argument
--- a/devtools/testlib.py	Wed Feb 18 18:06:41 2009 +0100
+++ b/devtools/testlib.py	Wed Feb 18 19:19:57 2009 +0100
@@ -158,7 +158,7 @@
         self.commit()
 
     @nocoverage
-    def _check_html(self, output, view, template='main'):
+    def _check_html(self, output, view, template='main-template'):
         """raises an exception if the HTML is invalid"""
         try:
             validatorclass = self.vid_validators[view.id]
@@ -175,7 +175,7 @@
         return validator.parse_string(output.strip())
 
 
-    def view(self, vid, rset, req=None, template='main', **kwargs):
+    def view(self, vid, rset, req=None, template='main-template', **kwargs):
         """This method tests the view `vid` on `rset` using `template`
 
         If no error occured while rendering the view, the HTML is analyzed
@@ -200,7 +200,7 @@
         viewfunc = lambda **k: self.vreg.main_template(req, template, **kwargs)
         if template is None: # raw view testing, no template
             viewfunc = view.dispatch
-        elif template == 'main':
+        elif template == 'main-template':
             _select_view_and_rset = TheMainTemplate._select_view_and_rset
             # patch TheMainTemplate.process_rql to avoid recomputing resultset
             def __select_view_and_rset(self, view=view, rset=rset):
@@ -210,11 +210,11 @@
         try:
             return self._test_view(viewfunc, view, template, **kwargs)
         finally:
-            if template == 'main':
+            if template == 'main-template':
                 TheMainTemplate._select_view_and_rset = _select_view_and_rset
 
 
-    def _test_view(self, viewfunc, view, template='main', **kwargs):
+    def _test_view(self, viewfunc, view, template='main-template', **kwargs):
         """this method does the actual call to the view
 
         If no error occured while rendering the view, the HTML is analyzed
@@ -332,7 +332,7 @@
             backup_rset = rset._prepare_copy(rset.rows, rset.description)
             yield InnerTest(self._testname(rset, view.id, 'view'),
                             self.view, view.id, rset,
-                            rset.req.reset_headers(), 'main')
+                            rset.req.reset_headers(), 'main-template')
             # We have to do this because some views modify the
             # resultset's syntax tree
             rset = backup_rset
--- a/web/application.py	Wed Feb 18 18:06:41 2009 +0100
+++ b/web/application.py	Wed Feb 18 19:19:57 2009 +0100
@@ -384,7 +384,8 @@
             if tb:
                 req.data['excinfo'] = excinfo
             req.form['vid'] = 'error'
-            content = self.vreg.main_template(req, 'main')
+            errview = self.vreg.select_view('error', req, None)
+            content = self.vreg.main_template(req, 'main-template', view=errview)
         except:
             content = self.vreg.main_template(req, 'error')
         raise StatusResponse(500, content)
@@ -396,9 +397,10 @@
         return self.vreg.main_template(req, 'loggedout')
     
     def notfound_content(self, req):
-        template = req.property_value('ui.main-template') or 'main'
+        template = req.property_value('ui.main-template') or 'main-template'
         req.form['vid'] = '404'
-        return self.vreg.main_template(req, template)
+        view404 = self.vreg.select_view('404', req, None)
+        return self.vreg.main_template(req, template, view=view)
 
 
 set_log_methods(CubicWebPublisher, LOGGER)
--- a/web/views/basecontrollers.py	Wed Feb 18 18:06:41 2009 +0100
+++ b/web/views/basecontrollers.py	Wed Feb 18 19:19:57 2009 +0100
@@ -54,15 +54,75 @@
 
 
 class ViewController(Controller):
+    """standard entry point :
+    - build result set
+    - select and call main template
+    """
     id = 'view'
-    template = 'main'
+    template = 'main-template'
     
     def publish(self, rset=None):
         """publish a request, returning an encoded string"""
+        view, rset = self._select_view_and_rset(rset)
+        self.add_to_breadcrumbs(view)
+        self.validate_cache(view)
         template = self.req.property_value('ui.main-template')
-        if template not in self.vreg.registry('templates') :
+        if template not in self.vreg.registry('views') :
             template = self.template
-        return self.vreg.main_template(self.req, template, rset=rset)
+        return self.vreg.main_template(self.req, template, rset=rset, view=view)
+
+    def _select_view_and_rset(self, rset):
+        req = self.req
+        if rset is None and not hasattr(req, '_rql_processed'):
+            req._rql_processed = True
+            rset = self.process_rql(req.form.get('rql'))
+        if rset and rset.rowcount == 1 and '__method' in req.form:
+            entity = rset.get_entity(0, 0)
+            try:
+                method = getattr(entity, req.form.pop('__method'))
+                method()
+            except Exception, ex:
+                self.exception('while handling __method')
+                req.set_message(req._("error while handling __method: %s") % req._(ex))
+        vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
+        try:
+            view = self.vreg.select_view(vid, req, rset)
+        except ObjectNotFound:
+            self.warning("the view %s could not be found", vid)
+            req.set_message(req._("The view %s could not be found") % vid)
+            vid = vid_from_rset(req, rset, self.schema)
+            view = self.vreg.select_view(vid, req, rset)
+        except NoSelectableObject:
+            if rset:
+                req.set_message(req._("The view %s can not be applied to this query") % vid)
+            else:
+                req.set_message(req._("You have no access to this view or it's not applyable to current data"))
+            self.warning("the view %s can not be applied to this query", vid)
+            vid = vid_from_rset(req, rset, self.schema)
+            view = self.vreg.select_view(vid, req, rset)
+        return view, rset
+
+    def process_rql(self, rql):
+        """execute rql if specified"""
+        if rql:
+            self.ensure_ro_rql(rql)
+            if not isinstance(rql, unicode):
+                rql = unicode(rql, self.req.encoding)
+            pp = self.vreg.select_component('magicsearch', self.req)
+            self.rset = pp.process_query(rql, self.req)
+            return self.rset
+        return None
+
+    def add_to_breadcrumbs(self, view):
+        # update breadcrumps **before** validating cache, unless the view
+        # specifies explicitly it should not be added to breadcrumb or the
+        # view is a binary view
+        if view.add_to_breadcrumbs and not view.binary:
+            self.req.update_breadcrumbs()
+
+    def validate_cache(self, view):
+        view.set_http_cache_headers()
+        self.req.validate_cache()
 
     def execute_linkto(self, eid=None):
         """XXX __linkto parameter may cause security issue
--- a/web/views/basetemplates.py	Wed Feb 18 18:06:41 2009 +0100
+++ b/web/views/basetemplates.py	Wed Feb 18 19:19:57 2009 +0100
@@ -11,10 +11,10 @@
 from logilab.mtconverter import html_escape
 
 from cubicweb import NoSelectableObject, ObjectNotFound
+from cubicweb.vregistry import objectify_selector
 from cubicweb.selectors import match_kwargs
 from cubicweb.view import View, MainTemplate,  NOINDEX, NOFOLLOW
 from cubicweb.utils import make_uid, UStringIO
-from cubicweb.web.views.baseviews import vid_from_rset
 
 # main templates ##############################################################
 
@@ -64,89 +64,67 @@
                 html_escape(indexurl),
                 self.req._('go back to the index page')))
 
+@objectify_selector
+def templatable_view(cls, req, rset, *args, **kwargs):
+    view = kwargs.pop('view', None)
+    if view is None:
+        return 1
+    if view.binary:
+        return 0
+    if req.form.has_key('__notemplate'):
+        return 0
+    return view.templatable
+
+@objectify_selector
+def non_templatable_view(cls, req, rset, *args, **kwargs):
+    return not templatable_view()(cls, req, rset, *args, **kwargs)
+
+
+class NonTemplatableViewTemplate(MainTemplate):
+    """main template for any non templatable views (xml, binaries, etc.)"""
+    id = 'main-template'
+    __select__ = non_templatable_view()
+    
+    def call(self, view):
+        view.set_request_content_type()
+        self.set_stream(templatable=False)
+        # have to replace our unicode stream using view's binary stream
+        view.dispatch()
+        assert self._stream, 'duh, template used as a sub-view ?? (%s)' % self._stream
+        self._stream = view._stream
+
 
 class TheMainTemplate(MainTemplate):
     """default main template :
 
     - call header / footer templates
-    - build result set
-    - guess and call an appropriate view through the view manager
     """
-    id = 'main'
-
-    def _select_view_and_rset(self):
-        req = self.req
-        if self.rset is None and not hasattr(req, '_rql_processed'):
-            req._rql_processed = True
-            rset = self.process_rql(req.form.get('rql'))
-        else:
-            rset = self.rset
-        # handle special "method" param when necessary
-        # XXX this should probably not be in the template (controller ?), however:
-        #     * we need to have the displayed rset
-        #     * we don't want to handle it in each view
-        if rset and rset.rowcount == 1 and '__method' in req.form:
-            entity = rset.get_entity(0, 0)
-            try:
-                method = getattr(entity, req.form.pop('__method'))
-                method()
-            except Exception, ex:
-                self.exception('while handling __method')
-                req.set_message(req._("error while handling __method: %s") % req._(ex))
-        vid = req.form.get('vid') or vid_from_rset(req, rset, self.schema)
-        try:
-            view = self.vreg.select_view(vid, req, rset)
-        except ObjectNotFound:
-            self.warning("the view %s could not be found", vid)
-            req.set_message(req._("The view %s could not be found") % vid)
-            vid = vid_from_rset(req, rset, self.schema)
-            view = self.vreg.select_view(vid, req, rset)
-        except NoSelectableObject:
-            if rset:
-                req.set_message(req._("The view %s can not be applied to this query") % vid)
-            else:
-                req.set_message(req._("You have no access to this view or it's not applyable to current data"))
-            self.warning("the view %s can not be applied to this query", vid)
-            vid = vid_from_rset(req, rset, self.schema)
-            view = self.vreg.select_view(vid, req, rset)
-        return view, rset
-
-    def call(self):
-        view, rset = self._select_view_and_rset()
-        req = self.req
-        # update breadcrumps **before** validating cache, unless the view
-        # specifies explicitly it should not be added to breadcrumb or the
-        # view is a binary view
-        if view.add_to_breadcrumbs and not view.binary:
-            req.update_breadcrumbs()
-        view.set_http_cache_headers()
-        req.validate_cache()
-        with_templates = self.with_templates(view)
-        if with_templates:
-            self.set_request_content_type()
-            content_type = self.content_type
-            self.template_header(content_type, view)
-        else:
-            view.set_request_content_type()
-            self.set_stream(templatable=False)
-        self.wview('page-content', view=view, rset=rset)
-        if with_templates:
-            self.template_footer(view)
-
-    def with_templates(self, view):
-        return (not view.binary and view.templatable and
-                not self.req.form.has_key('__notemplate'))
-
-    def process_rql(self, rql):
-        """execute rql if specified"""
-        if rql:
-            self.ensure_ro_rql(rql)
-            if not isinstance(rql, unicode):
-                rql = unicode(rql, self.req.encoding)
-            pp = self.vreg.select_component('magicsearch', self.req)
-            self.rset = pp.process_query(rql, self.req)
-            return self.rset
-        return None
+    id = 'main-template'
+    __select__ = templatable_view()
+    
+    def call(self, view):
+        self.set_request_content_type()
+        self.template_header(self.content_type, view)
+        w = self.w
+        w(u'<div id="pageContent">\n')
+        vtitle = self.req.form.get('vtitle')
+        if vtitle:
+            w(u'<h1 class="vtitle">%s</h1>\n' % html_escape(vtitle))
+        # display entity type restriction component
+        etypefilter = self.vreg.select_component('etypenavigation',
+                                                 self.req, self.rset)
+        if etypefilter and etypefilter.propval('visible'):
+            etypefilter.dispatch(w=w)
+        self.nav_html = UStringIO()
+        self.pagination(self.req, self.rset, self.nav_html.write,
+                        not (view and view.need_navigation))
+        w(_(self.nav_html.getvalue()))
+        w(u'<div id="contentmain">\n')
+        view.dispatch(w=w)
+        w(u'</div>\n') # close id=contentmain
+        w(_(self.nav_html.getvalue()))
+        w(u'</div>\n') # closes id=pageContent
+        self.template_footer(view)
 
     def template_header(self, content_type, view=None, page_title='', additional_headers=()):
         page_title = page_title or view.page_title()
@@ -206,44 +184,6 @@
         self.wview('contentfooter', rset=self.rset, view=view)
 
 
-class PageContentTemplate(TheMainTemplate):
-    id = 'page-content'
-
-    def call(self, view=None, rset=None):
-        if view is None:
-            view, rset = self._select_view_and_rset()
-        with_templates = self.with_templates(view)
-        w = self.w
-        if with_templates:
-            w(u'<div id="pageContent">\n')
-            vtitle = self.req.form.get('vtitle')
-            if vtitle:
-                w(u'<h1 class="vtitle">%s</h1>\n' % html_escape(vtitle))
-            # display entity type restriction component
-            etypefilter = self.vreg.select_component('etypenavigation',
-                                                     self.req, self.rset)
-            if etypefilter and etypefilter.propval('visible'):
-                etypefilter.dispatch(w=w)
-            self.nav_html = UStringIO()
-            self.pagination(self.req, self.rset, self.nav_html.write,
-                            not (view and view.need_navigation))
-            w(_(self.nav_html.getvalue()))
-            w(u'<div id="contentmain">\n')
-        else:
-            self.set_stream(templatable=False)            
-        if view.binary:
-            # have to replace our unicode stream using view's binary stream
-            view.dispatch()
-            assert self._stream, 'duh, template used as a sub-view ?? (%s)' % self._stream
-            self._stream = view._stream
-        else:
-            view.dispatch(w=w)
-        if with_templates:
-            w(u'</div>\n') # close id=contentmain
-            w(_(self.nav_html.getvalue()))
-            w(u'</div>\n') # closes id=pageContent
-
-
 class ErrorTemplate(TheMainTemplate):
     """fallback template if an internal error occured during displaying the
     main template. This template may be called for authentication error,
@@ -534,3 +474,7 @@
         return req.url().replace(req.base_url(), config['https-url'])
     return req.url()
 
+
+## vregistry registration callback ############################################
+def registration_callback(vreg):
+    vreg.register_all(globals().values(), modname=__name__)
--- a/web/webconfig.py	Wed Feb 18 18:06:41 2009 +0100
+++ b/web/webconfig.py	Wed Feb 18 19:19:57 2009 +0100
@@ -26,7 +26,7 @@
       'sitewide': True, 'group': 'ui', 
       }),
     ('main-template',
-     {'type' : 'string', 'default': 'main',
+     {'type' : 'string', 'default': 'main-template',
       'help': _('id of main template used to render pages'),
       'sitewide': True, 'group': 'ui',
       }),