diff -r 058bb3dc685f -r 0b59724cb3f2 web/views/basetemplates.py --- a/web/views/basetemplates.py Mon Jan 04 18:40:30 2016 +0100 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,530 +0,0 @@ -# copyright 2003-2012 LOGILAB S.A. (Paris, FRANCE), all rights reserved. -# contact http://www.logilab.fr/ -- mailto:contact@logilab.fr -# -# This file is part of CubicWeb. -# -# CubicWeb is free software: you can redistribute it and/or modify it under the -# terms of the GNU Lesser General Public License as published by the Free -# Software Foundation, either version 2.1 of the License, or (at your option) -# any later version. -# -# CubicWeb is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -# FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -# details. -# -# You should have received a copy of the GNU Lesser General Public License along -# with CubicWeb. If not, see . -"""default templates for CubicWeb web client""" - -__docformat__ = "restructuredtext en" -from cubicweb import _ - -from logilab.mtconverter import xml_escape -from logilab.common.deprecation import class_renamed -from logilab.common.registry import objectify_predicate -from logilab.common.decorators import classproperty - -from cubicweb.predicates import match_kwargs, no_cnx, anonymous_user -from cubicweb.view import View, MainTemplate, NOINDEX, NOFOLLOW, StartupView -from cubicweb.utils import UStringIO -from cubicweb.schema import display_name -from cubicweb.web import component, formfields as ff, formwidgets as fw -from cubicweb.web.views import forms - -# main templates ############################################################## - -class LogInOutTemplate(MainTemplate): - - def call(self): - self.set_request_content_type() - w = self.w - self.write_doctype() - self.template_header('text/html', self._cw._('login_action')) - w(u'\n') - self.content(w) - w(u'') - - def template_header(self, content_type, view=None, page_title='', additional_headers=()): - w = self.whead - # explictly close the tag to avoid IE 6 bugs while browsing DOM - w(u'' % xml_escape(self._cw.base_url())) - w(u'\n' - % (content_type, self._cw.encoding)) - w(NOINDEX) - w(NOFOLLOW) - w(u'\n'.join(additional_headers) + u'\n') - self.wview('htmlheader', rset=self.cw_rset) - w(u'%s\n' % xml_escape(page_title)) - - def content(self): - raise NotImplementedError() - - -class LogInTemplate(LogInOutTemplate): - __regid__ = 'login' - __select__ = anonymous_user() - title = 'log in' - - def content(self, w): - self.wview('logform', rset=self.cw_rset, id='loginBox', klass='') - - -class LoggedOutTemplate(StartupView): - __regid__ = 'loggedout' - __select__ = anonymous_user() - title = 'logged out' - - def call(self): - msg = self._cw._('you have been logged out') - if self._cw.cnx: - comp = self._cw.vreg['components'].select('applmessages', self._cw) - comp.render(w=self.w, msg=msg) - self.wview('index') - else: - self.w(u'

%s

' % msg) - - -@objectify_predicate -def modal_view(cls, req, rset, *args, **kwargs): - if req.form.get('__modal', None): - return 1 - -@objectify_predicate -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 '__notemplate' in req.form: - return 0 - return view.templatable - - -class NonTemplatableViewTemplate(MainTemplate): - """main template for any non templatable views (xml, binaries, etc.)""" - __regid__ = 'main-template' - __select__ = ~templatable_view() - - def call(self, view): - view.set_request_content_type() - view.set_stream() - if (('__notemplate' in self._cw.form) - and view.templatable - and view.content_type == self._cw.html_content_type()): - view.w(u'
') - view.render() - view.w(u'
') - else: - view.render() - # have to replace our stream by view's stream (which may be a binary - # stream) - self._stream = view._stream - - -class ModalMainTemplate(MainTemplate): - """ a no-decoration main template for standard views - that typically live in a modal context """ - __regid__ = 'main-template' - __select__ = templatable_view() & modal_view() - - def call(self, view): - view.set_request_content_type() - view.render(w=self.w) - - -class TheMainTemplate(MainTemplate): - """default main template : - - - call header / footer templates - """ - __regid__ = '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._cw.form.get('vtitle') - if vtitle: - w(u'
%s
\n' % xml_escape(vtitle)) - # display entity type restriction component - etypefilter = self._cw.vreg['components'].select_or_none( - 'etypenavigation', self._cw, rset=self.cw_rset) - if etypefilter and etypefilter.cw_propval('visible'): - etypefilter.render(w=w) - nav_html = UStringIO() - if view and not view.handle_pagination: - view.paginate(w=nav_html.write) - w(nav_html.getvalue()) - w(u'
\n') - view.render(w=w) - w(u'
\n') # close id=contentmain - w(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() - additional_headers = additional_headers or view.html_headers() - self.template_html_header(content_type, page_title, additional_headers) - self.template_body_header(view) - - def template_html_header(self, content_type, page_title, additional_headers=()): - w = self.whead - lang = self._cw.lang - self.write_doctype() - self._cw.html_headers.define_var('BASE_URL', self._cw.base_url()) - self._cw.html_headers.define_var('DATA_URL', self._cw.datadir_url) - w(u'\n' - % (content_type, self._cw.encoding)) - w(u'\n'.join(additional_headers) + u'\n') - self.wview('htmlheader', rset=self.cw_rset) - if page_title: - w(u'%s\n' % xml_escape(page_title)) - - def template_body_header(self, view): - w = self.w - w(u'\n') - self.wview('header', rset=self.cw_rset, view=view) - w(u'
\n') - self.nav_column(view, 'left') - w(u'\n') - self.nav_column(view, 'right') - self.w(u'
\n') - components = self._cw.vreg['components'] - rqlcomp = components.select_or_none('rqlinput', self._cw, rset=self.cw_rset) - if rqlcomp: - rqlcomp.render(w=self.w, view=view) - msgcomp = components.select_or_none('applmessages', self._cw, rset=self.cw_rset) - if msgcomp: - msgcomp.render(w=self.w) - self.content_header(view) - - def template_footer(self, view=None): - self.content_footer(view) - self.w(u'
\n') - self.wview('footer', rset=self.cw_rset) - self.w(u'') - - def nav_column(self, view, context): - boxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects( - self._cw, rset=self.cw_rset, view=view, context=context)) - if boxes: - getlayout = self._cw.vreg['components'].select - self.w(u'\n') - - def content_header(self, view=None): - """by default, display informal messages in content header""" - self.wview('contentheader', rset=self.cw_rset, view=view) - - def content_footer(self, view=None): - self.wview('contentfooter', rset=self.cw_rset, view=view) - - -class ErrorTemplate(TheMainTemplate): - """fallback template if an internal error occurred during displaying the main - template. This template may be called for authentication error, which means - that req.cnx and req.user may not be set. - """ - __regid__ = 'error-template' - - def call(self): - """display an unexpected error""" - self.set_request_content_type() - self._cw.reset_headers() - view = self._cw.vreg['views'].select('error', self._cw, rset=self.cw_rset) - self.template_header(self.content_type, view, self._cw._('an error occurred'), - [NOINDEX, NOFOLLOW]) - view.render(w=self.w) - self.template_footer(view) - - def template_header(self, content_type, view=None, page_title='', additional_headers=()): - w = self.whead - lang = self._cw.lang - self.write_doctype() - w(u'\n' - % (content_type, self._cw.encoding)) - w(u'\n'.join(additional_headers)) - self.wview('htmlheader', rset=self.cw_rset) - w(u'%s\n' % xml_escape(page_title)) - self.w(u'\n') - - def template_footer(self, view=None): - self.w(u'') - - -class SimpleMainTemplate(TheMainTemplate): - - __regid__ = 'main-no-top' - - def template_header(self, content_type, view=None, page_title='', additional_headers=()): - page_title = page_title or view.page_title() - additional_headers = additional_headers or view.html_headers() - whead = self.whead - lang = self._cw.lang - self.write_doctype() - whead(u'\n' - % (content_type, self._cw.encoding)) - whead(u'\n'.join(additional_headers) + u'\n') - self.wview('htmlheader', rset=self.cw_rset) - w = self.w - whead(u'%s\n' % xml_escape(page_title)) - w(u'\n') - w(u'
') - w(u'\n') - w(u'') - w(u'
') - - def topleft_header(self): - logo = self._cw.vreg['components'].select_or_none('logo', self._cw, - rset=self.cw_rset) - if logo and logo.cw_propval('visible'): - w = self.w - w(u'\n') - w(u'\n') - w(u'\n') - - -# page parts templates ######################################################## - -class HTMLHeader(View): - """default html headers""" - __regid__ = 'htmlheader' - - def call(self, **kwargs): - self.favicon() - self.stylesheets() - self.javascripts() - self.alternates() - - def favicon(self): - favicon = self._cw.uiprops.get('FAVICON', None) - if favicon: - self.whead(u'\n' % favicon) - - def stylesheets(self): - req = self._cw - add_css = req.add_css - for css in req.uiprops['STYLESHEETS']: - add_css(css, localfile=False) - for css in req.uiprops['STYLESHEETS_PRINT']: - add_css(css, u'print', localfile=False) - for css in req.uiprops['STYLESHEETS_IE']: - add_css(css, localfile=False, ieonly=True) - - def javascripts(self): - for jscript in self._cw.uiprops['JAVASCRIPTS']: - self._cw.add_js(jscript, localfile=False) - - def alternates(self): - urlgetter = self._cw.vreg['components'].select_or_none('rss_feed_url', - self._cw, rset=self.cw_rset) - if urlgetter is not None: - self.whead(u'\n' - % xml_escape(urlgetter.feed_url())) - - -class HTMLPageHeader(View): - """default html page header""" - __regid__ = 'header' - main_cell_components = ('appliname', 'breadcrumbs') - headers = (('headtext', 'header-left'), - ('header-center', 'header-center'), - ('header-right', 'header-right') - ) - - def call(self, view, **kwargs): - self.main_header(view) - self.w(u'
') - self.state_header() - self.w(u'
') - - def main_header(self, view): - """build the top menu with authentification info and the rql box""" - w = self.w - w(u'\n') - for colid, context in self.headers: - w(u'') - w(u'\n') - - def state_header(self): - state = self._cw.search_state - if state[0] == 'normal': - return - _ = self._cw._ - value = self._cw.view('oneline', self._cw.eid_rset(state[1][1])) - msg = ' '.join((_("searching for"), - display_name(self._cw, state[1][3]), - _("to associate with"), value, - _("by relation"), '"', - display_name(self._cw, state[1][2], state[1][0]), - '"')) - return self.w(u'
%s
' % msg) - - -class HTMLPageFooter(View): - """default html page footer: include footer actions""" - __regid__ = 'footer' - - def call(self, **kwargs): - self.w(u'') - - def footer_content(self): - actions = self._cw.vreg['actions'].possible_actions(self._cw, - rset=self.cw_rset) - footeractions = actions.get('footer', ()) - for i, action in enumerate(footeractions): - self.w(u'%s' % (action.url(), - self._cw._(action.title))) - if i < (len(footeractions) - 1): - self.w(u' | ') - -class HTMLContentHeader(View): - """default html page content header: - * include message component if selectable for this request - * include selectable content navigation components - """ - __regid__ = 'contentheader' - - def call(self, view, **kwargs): - """by default, display informal messages in content header""" - components = self._cw.vreg['ctxcomponents'].poss_visible_objects( - self._cw, rset=self.cw_rset, view=view, context='navtop') - if components: - self.w(u'
') - for comp in components: - comp.render(w=self.w, view=view) - self.w(u'
') - - -class HTMLContentFooter(View): - """default html page content footer: include selectable content navigation - components - """ - __regid__ = 'contentfooter' - - def call(self, view, **kwargs): - components = self._cw.vreg['ctxcomponents'].poss_visible_objects( - self._cw, rset=self.cw_rset, view=view, context='navbottom') - if components: - self.w(u'
') - for comp in components: - comp.render(w=self.w, view=view) - self.w(u'
') - -class BaseLogForm(forms.FieldsForm): - """Abstract Base login form to be used by any login form - """ - __abstract__ = True - - __regid__ = 'logform' - domid = 'loginForm' - needs_css = ('cubicweb.login.css',) - - onclick_base = "javascript: cw.htmlhelpers.popupLoginBox('%s', '%s');" - onclick_args = (None, None) - - @classproperty - def form_buttons(cls): - # we use a property because sub class will need to define their own onclick_args. - # Therefor we can't juste make the string formating when instanciating this class - onclick = cls.onclick_base % cls.onclick_args - form_buttons = [fw.SubmitButton(label=_('log in'), - attrs={'class': 'loginButton'}), - fw.ResetButton(label=_('cancel'), - attrs={'class': 'loginButton', - 'onclick': onclick}),] - ## Can't shortcut next access because __dict__ is a "dictproxy" which - ## does not support items assignement. - # cls.__dict__['form_buttons'] = form_buttons - return form_buttons - - def form_action(self): - if self.action is None: - # reuse existing redirection if it exists - target = self._cw.form.get('postlogin_path', - self._cw.relative_path()) - url_args = {} - if target and target != '/': - url_args['postlogin_path'] = target - return self._cw.build_url('login', __secure__=True, **url_args) - return super(BaseLogForm, self).form_action() - -class LogForm(BaseLogForm): - """Simple login form that send username and password - """ - __regid__ = 'logform' - domid = 'loginForm' - needs_css = ('cubicweb.login.css',) - # XXX have to recall fields name since python is mangling __login/__password - __login = ff.StringField('__login', widget=fw.TextInput({'class': 'data'})) - __password = ff.StringField('__password', label=_('password'), - widget=fw.PasswordSingleInput({'class': 'data'})) - - onclick_args = ('popupLoginBox', '__login') - - -class LogFormView(View): - # XXX an awful lot of hardcoded assumptions there - # makes it unobvious to reuse/specialize - __regid__ = 'logform' - __select__ = match_kwargs('id', 'klass') - - title = 'log in' - - def call(self, id, klass, title=True, showmessage=True): - w = self.w - w(u'
' % (id, klass)) - if title: - stitle = self._cw.property_value('ui.site-title') - if stitle: - stitle = xml_escape(stitle) - else: - stitle = u' ' - w(u'
%s
' % stitle) - w(u'
\n') - if showmessage and self._cw.message: - w(u'
%s
\n' % self._cw.message) - config = self._cw.vreg.config - if config['auth-mode'] != 'http': - self.login_form(id) # Cookie authentication - w(u'
') - w(u'
\n') - - def login_form(self, id): - cw = self._cw - form = cw.vreg['forms'].select('logform', cw) - if cw.vreg.config['allow-email-login']: - label = cw._('login or email') - else: - label = cw.pgettext('CWUser', 'login') - form.field_by_name('__login').label = label - form.render(w=self.w, table_class='', display_progress_div=False) - cw.html_headers.add_onload('jQuery("#__login:visible").focus()') - -LogFormTemplate = class_renamed('LogFormTemplate', LogFormView)