-
Adrien Di Mascio authored
separator should be ",", not ";"
Adrien Di Mascio authoredseparator should be ",", not ";"
basetemplates.py 10.73 KiB
"""bootstrap implementation of base templates
:organization: Logilab
:copyright: 2013 LOGILAB S.A. (Paris, FRANCE), license is LGPL.
:contact: http://www.logilab.fr/ -- mailto:contact@logilab.fr
"""
__docformat__ = "restructuredtext en"
from logilab.common.decorators import monkeypatch
from logilab.mtconverter import xml_escape
from cubicweb.schema import display_name
from cubicweb.utils import UStringIO
from cubicweb.web.views import basetemplates, \
basecomponents, actions
from cubicweb.web.views.boxes import SearchBox
HTML5 = u'<!DOCTYPE html>'
basetemplates.TheMainTemplate.doctype = HTML5
# options which can be changed freely
basetemplates.TheMainTemplate.twbs_container_cls = 'container-fluid'
basetemplates.TheMainTemplate.twbs_col_cls = 'col-xs-'
basetemplates.TheMainTemplate.twbs_col_size = 2
# options which require recompiling bootstrap.css from source
basetemplates.TheMainTemplate.twbs_grid_columns = 12
@monkeypatch(basetemplates.TheMainTemplate)
def call(self, view):
self.set_request_content_type()
self.template_header(self.content_type, view)
self.template_page_content(view)
@monkeypatch(basetemplates.TheMainTemplate)
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)
@monkeypatch(basetemplates.TheMainTemplate)
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'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
% (content_type, self._cw.encoding))
w(u'<meta name="viewport" content="initial-scale=1.0, '
u'maximum-scale=1.0, width=device-width"/>')
w(u'\n'.join(additional_headers) + u'\n')
self.wview('htmlheader', rset=self.cw_rset)
if page_title:
w(u'<title>%s</title>\n' % xml_escape(page_title))
@monkeypatch(basetemplates.TheMainTemplate)
def template_page_content(self, view):
w = self.w
self.w(u'<body>\n')
self.wview('header', rset=self.cw_rset, view=view)
w(u'<div id="page" class="%s">\n' % self.twbs_container_cls)
w(u'<div class="row">\n')
left_boxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects(
self._cw, rset=self.cw_rset, view=view, context='left'))
right_boxes = list(self._cw.vreg['ctxcomponents'].poss_visible_objects(
self._cw, rset=self.cw_rset, view=view, context='right'))
nb_boxes = int(bool(left_boxes)) + int(bool(right_boxes))
content_cols = self.twbs_grid_columns
if nb_boxes:
content_cols = self.twbs_grid_columns - self.twbs_col_size * nb_boxes
self.nav_column(view, left_boxes, 'left')
self.content_column(view, content_cols)
self.nav_column(view, right_boxes, 'right')
self.w(u'</div>\n') # closes class=row
self.w(u'</div>\n') # closes id="page" from template_page_content
self.template_footer(view)
self.w(u'</body>\n')
@monkeypatch(basetemplates.TheMainTemplate)
def get_components(self, view, context):
ctxcomponents = self._cw.vreg['ctxcomponents']
return ctxcomponents.poss_visible_objects(self._cw,
rset=self.cw_rset,
view=view,
context=context)
@monkeypatch(basetemplates.TheMainTemplate)
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]))
target, eid, r_type, searched_type = self._cw.search_state[1]
cancel_link = u'<a href="%(url)s" role="button" class="btn btn-default" title="%(title)s">%(title)s</a>' % {
'url' : self._cw.build_url(str(eid),
vid='edition', __mode='normal'),
'title': _('cancel')}
msg = ' '.join((_("searching for"),
'<strong>"%s"</strong>' % \
display_name(self._cw, state[1][3]),
_("to associate with"), value,
_("by relation"),
'<strong>"%s"</strong>' % \
display_name(self._cw, state[1][2], state[1][0]),
cancel_link))
return self.w(u'<div class="alert alert-info">%s</div>' % msg)
@monkeypatch(basetemplates.TheMainTemplate)
def nav_column(self, view, boxes, context):
if boxes:
stream = UStringIO()
for box in boxes:
box.render(w=stream.write, view=view)
html = stream.getvalue()
if html:
# only display aside columns if html availble
self.w(u'<aside id="aside-main-%s" class="%s%s cwjs-aside">\n' %
(context, self.twbs_col_cls, self.twbs_col_size))
self.w(html)
self.w(u'</aside>\n')
return len(boxes)
@monkeypatch(basetemplates.TheMainTemplate)
def content_column(self, view, content_cols):
w = self.w
w(u'<div id="main-center" class="%(prefix)s%(col)s" role="main">' % {
'prefix': self.twbs_col_cls, 'col': content_cols})
components = self._cw.vreg['components']
self.content_components(view, components)
w(u'<div id="pageContent">')
self.content_header(view)
vtitle = self._cw.form.get('vtitle')
if vtitle:
w(u'<div class="vtitle">%s</div>\n' % xml_escape(vtitle))
self.state_header()
self.content_navrestriction_components(view, components)
nav_html = UStringIO()
if view and not view.handle_pagination:
view.paginate(w=nav_html.write)
w(nav_html.getvalue())
w(u'<div id="contentmain">\n')
view.render(w=w)
w(u'</div>\n') # closes id=contentmain
w(nav_html.getvalue())
self.content_footer(view)
w(u'</div>\n') # closes div#pageContent
w(u'</div>\n') # closes div.%(prefix)s-%(col)s
@monkeypatch(basetemplates.TheMainTemplate)
def content_components(self, view, components):
"""TODO : should use context"""
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)
@monkeypatch(basetemplates.TheMainTemplate)
def content_navrestriction_components(self, view, components):
# display entity type restriction component
etypefilter = components.select_or_none(
'etypenavigation', self._cw, rset=self.cw_rset)
if etypefilter and etypefilter.cw_propval('visible'):
etypefilter.render(w=self.w)
@monkeypatch(basetemplates.TheMainTemplate)
def template_footer(self, view=None):
self.wview('footer', rset=self.cw_rset)
# main header
basecomponents.ApplLogo.context = 'header-logo'
# use basecomponents.ApplicationName.visible = False
basecomponents.ApplicationName.context = 'header-left'
basecomponents.ApplLogo.order = 1
basecomponents.ApplicationName.order = 10
basecomponents.CookieLoginComponent.order = 10
basecomponents.AuthenticatedUserStatus.order = 5
SearchBox.order = -1
SearchBox.context = 'header-right'
SearchBox.layout_id = 'simple-layout'
@monkeypatch(basetemplates.HTMLPageHeader)
def call(self, view, **kwargs):
self.main_header(view)
self.breadcrumbs(view)
def get_components(self, view, context):
ctxcomponents = self._cw.vreg['ctxcomponents']
return ctxcomponents.poss_visible_objects(self._cw,
rset=self.cw_rset,
view=view,
context=context)
basetemplates.HTMLPageHeader.get_components = get_components
basetemplates.HTMLPageHeader.css = {
'navbar-extra': 'navbar-default',
'breadcrumbs': 'cw-breadcrumb',
'container-cls': basetemplates.TheMainTemplate.twbs_container_cls,
'header-left': '',
'header-right': 'navbar-right',
}
@monkeypatch(basetemplates.HTMLPageHeader)
def main_header(self, view):
w = self.w
w(u'<nav class="navbar %s" role="banner">' % self.css['navbar-extra'])
w(u'<div class="%s">' % self.css['container-cls'])
self.display_navbar_header(w, view)
w(u'<div id="tools-group" class="collapse navbar-collapse">')
self.display_header_components(w, view, 'header-left')
self.display_header_components(w, view, 'header-right')
w(u'</div></div></nav>')
def display_navbar_header(self, w, view):
w(u'''<div class="navbar-header">
<button class="navbar-toggle" data-target="#tools-group" data-toggle="collapse" type="button">
<span class="sr-only">%(toggle_label)s</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>''' % {'toggle_label':self._cw._('Toggle navigation')})
components = self.get_components(view, context='header-logo')
if components:
for component in components:
component.render(w=w)
w(u'</div>')
basetemplates.HTMLPageHeader.display_navbar_header = display_navbar_header
def display_header_components(self, w, view, context):
components = self.get_components(view, context=context)
if components:
w(u'<ul class="nav navbar-nav %s">' % self.css[context])
for component in components:
w(u'<li>')
component.render(w=w)
w(u'</li>')
w(u'</ul>')
basetemplates.HTMLPageHeader.display_header_components = display_header_components
@monkeypatch(basetemplates.HTMLPageHeader)
def breadcrumbs(self, view):
components = self.get_components(view, context='header-center')
if components:
self.w(u'<nav role="navigation" class="%s">' %
self.css.get('breadcrumbs', 'breadcrumbs-defaul'))
for component in components:
component.render(w=self.w)
self.w(u'</nav>')
@monkeypatch(basetemplates.HTMLContentFooter)
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:
# the row is needed here to correctly put the HTML flux
self.w(u'<div id="contentfooter">')
for comp in components:
comp.render(w=self.w, view=view)
self.w(u'</div>')
@monkeypatch(basetemplates.HTMLPageFooter)
def call(self, **kwargs):
self.w(u'<footer id="pagefooter" role="contentinfo">')
self.footer_content()
self.w(u'</footer>\n')
def registration_callback(vreg):
vreg.register_all(globals().values(), __name__)
vreg.unregister(actions.CancelSelectAction)