# HG changeset patch # User Adrien Di Mascio # Date 1234981197 -3600 # Node ID cb8ccbef8fa5b836a718e3b50a3312634ca1f723 # Parent e3d8db01f3f54be4c10e2f433fefac5b58fc489f 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 diff -r e3d8db01f3f5 -r cb8ccbef8fa5 cwvreg.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 diff -r e3d8db01f3f5 -r cb8ccbef8fa5 devtools/testlib.py --- 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 diff -r e3d8db01f3f5 -r cb8ccbef8fa5 web/application.py --- 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) diff -r e3d8db01f3f5 -r cb8ccbef8fa5 web/views/basecontrollers.py --- 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 diff -r e3d8db01f3f5 -r cb8ccbef8fa5 web/views/basetemplates.py --- 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'
\n') + vtitle = self.req.form.get('vtitle') + if vtitle: + w(u'

%s

\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'
\n') + view.dispatch(w=w) + w(u'
\n') # close id=contentmain + w(_(self.nav_html.getvalue())) + w(u'
\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'
\n') - vtitle = self.req.form.get('vtitle') - if vtitle: - w(u'

%s

\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'
\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'
\n') # close id=contentmain - w(_(self.nav_html.getvalue())) - w(u'
\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__) diff -r e3d8db01f3f5 -r cb8ccbef8fa5 web/webconfig.py --- 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', }),