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
--- 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',
}),