Newer
Older
"""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
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) # noqa: F811
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
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(
'<meta http-equiv="content-type" content="%s; charset=%s"/>\n'
% (content_type, self._cw.encoding)
)
w(
'<meta name="viewport" content="initial-scale=1.0, '
'maximum-scale=1.0, width=device-width"/>'
)
w("\n".join(additional_headers) + "\n")
self.wview("htmlheader", rset=self.cw_rset)
@monkeypatch(basetemplates.TheMainTemplate)
def template_page_content(self, view):
w = self.w
self.w("<body>\n")
self.wview("header", rset=self.cw_rset, view=view)
w('<div id="page" class="%s">\n' % self.twbs_container_cls)
w('<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.content_column(view, content_cols)
self.nav_column(view, right_boxes, "right")
self.w("</div>\n") # closes class=row
self.w("</div>\n") # closes id="page" from template_page_content
@monkeypatch(basetemplates.TheMainTemplate) # noqa: F811
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
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 = """<a href="{url}" role="button"
class="btn btn-default" title="{title}">{title}</a>""".format(
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('<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(
'<aside id="aside-main-%s" class="%s%s cwjs-aside">\n'
% (context, self.twbs_col_cls, self.twbs_col_size)
)
@monkeypatch(basetemplates.TheMainTemplate)
def content_column(self, view, content_cols):
w = self.w
w(
'<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)
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(nav_html.getvalue())
self.content_footer(view)
w("</div>\n") # closes div#pageContent
w("</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):
# use basecomponents.ApplicationName.visible = False
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) # noqa: F811
def call(self, view, **kwargs): # noqa: F811
self.main_header(view)
self.breadcrumbs(view)
def get_components(self, view, context): # noqa: F811
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('<nav class="navbar %s" role="banner">' % self.css["navbar-extra"])
w('<div class="%s">' % self.css["container-cls"])
self.display_navbar_header(w, view)
w('<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("</div></div></nav>")
def display_navbar_header(self, w, view):
<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)
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:
basetemplates.HTMLPageHeader.display_header_components = display_header_components
@monkeypatch(basetemplates.HTMLPageHeader)
def breadcrumbs(self, view):
components = self.get_components(view, context="header-center")
self.w(
'<nav role="navigation" class="%s">'
% self.css.get("breadcrumbs", "breadcrumbs-defaul")
)
for component in components:
component.render(w=self.w)
@monkeypatch(basetemplates.HTMLContentFooter) # noqa: F811
def call(self, view, **kwargs): # noqa: F811
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
for comp in components:
comp.render(w=self.w, view=view)
@monkeypatch(basetemplates.HTMLPageFooter) # noqa: F811
def registration_callback(vreg):
vreg.register_all(globals().values(), __name__)
vreg.unregister(actions.CancelSelectAction)