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
@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()
# explictly close the <base> tag to avoid IE 6 bugs while browsing DOM
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')
# FIXME this is a quick option to make cw work in IE9
# you'll lose all IE9 functionality, the browser will act as IE8.
w(u'<meta http-equiv="X-UA-Compatible" content="IE=8" />\n')
w(u'<!-- Le HTML5 shim, for IE6-8 support of HTML elements -->\n'
u' <!--[if lt IE 9]>\n'
u' <script src="%s"></script>\n'
u' <![endif]-->\n' % self._cw.data_url('js/html5.js'))
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)
w(u'<div id="page" class="container">\n')
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 = 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)
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
114
115
116
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:

Katia Saurfelt
committed
html = []
for box in boxes:

Katia Saurfelt
committed
box.render(w=html.append, view=view)
if html:
# only display aside columns if html availble
self.w(u'<aside id="aside-main-%s" class="col-md-3 cwjs-aside">\n' %

Katia Saurfelt
committed
context)
self.w(u'\n'.join(html))
self.w(u'</aside>\n')
return len(boxes)
@monkeypatch(basetemplates.TheMainTemplate)
def content_column(self, view, content_cols):
w = self.w
Rémi Cardona
committed
w(u'<div id="main-center" class="col-md-%(col)s" role="main">' % {
'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
@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)
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
211
212
# 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>')
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
264
265
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)