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
HTML5 = u'<!DOCTYPE html>'
basetemplates.TheMainTemplate.doctype = HTML5
Rémi Cardona
committed
# options which can be changed freely
basetemplates.TheMainTemplate.twbs_container_cls = 'container-fluid'
basetemplates.TheMainTemplate.twbs_col_cls = 'col-xs-'
Rémi Cardona
committed
@monkeypatch(basetemplates.TheMainTemplate)
def call(self, view):
self.grid_nb_cols = 12
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())

Katia Saurfelt
committed
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):
self.w(u'<body>\n')
self.wview('header', rset=self.cw_rset, view=view)
Rémi Cardona
committed
w(u'<div id="page" class="%s">\n' % self.twbs_container_cls)
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 = 12
if nb_boxes:
content_cols = self.grid_nb_cols-(3*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)
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
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:
Rémi Cardona
committed
stream = UStringIO()
for box in boxes:
Rémi Cardona
committed
box.render(w=stream.write, view=view)
html = stream.getvalue()

Katia Saurfelt
committed
if html:
# only display aside columns if html availble
self.w(u'<aside id="aside-main-%s" class="%s3 cwjs-aside">\n' %
(context, self.twbs_col_cls))
Rémi Cardona
committed
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)
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
# main header
basecomponents.ApplLogo.context = 'header-logo'
# use basecomponents.ApplicationName.visible = False
basecomponents.ApplicationName.context = 'header-logo'
basecomponents.ApplLogo.order = 1
basecomponents.ApplicationName.order = 10
basecomponents.CookieLoginComponent.order = 10
basecomponents.AuthenticatedUserStatus.order = 5
SearchBox.order = 0
SearchBox.context = 'header-right'
SearchBox.layout_id = 'simple-layout'
basetemplates.HTMLPageHeader.headers_classes = {
'header-left':'navbar-left',
'header-right':'navbar-right',
}
@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 = {'header-navbar' : 'navbar-default',
'breadcrumbs' : 'cw-breadcrumbs-default'}
@monkeypatch(basetemplates.HTMLPageHeader)
def main_header(self, view):
w = self.w
title = self._cw.property_value('ui.site-title')
w(u'<header class="navbar %s" role="banner">' %
self.css.get('header-navbar', 'header-navbar'))
w(u'<nav>')
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
self.dispaly_navbar_header(w, view)
self.display_header_components(w, view, 'header-left')
self.display_header_components(w, view, 'header-right')
w(u'</nav></header>')
def dispaly_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.dispaly_navbar_header = dispaly_navbar_header
def display_header_components(self, w, view, context):
components = self.get_components(view, context=context)
if components:
w(u'<div id="tools-group" class="collapse navbar-collapse">')
w(u'<ul class="nav navbar-nav %s">' % \
self.headers_classes[context])
for component in components:
w(u'<li>')
component.render(w=w)
w(u'</li>')
w(u'</ul>')
w(u'</div>')
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.w(u'</footer>\n')
def registration_callback(vreg):
vreg.register_all(globals().values(), __name__)
vreg.unregister(actions.CancelSelectAction)