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.utils import UStringIO
from cubicweb.web.views import basetemplates, \
basecomponents
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)
self.template_footer(view)
@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_body_header(self, view):
self.w(u'<body>\n')
self.wview('header', rset=self.cw_rset, view=view)
@monkeypatch(basetemplates.TheMainTemplate)
def template_page_content(self, view):
w(u'<div id="page" class="container">\n'
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
@monkeypatch(basetemplates.TheMainTemplate)
def nav_column(self, view, boxes, context):
if boxes:
getlayout = self._cw.vreg['components'].select
self.w(u'<div id="aside-main-%s" class="col-md-3">\n' %
context)
self.w(u'<div class="navboxes" id="navColumn%s">\n' % context.capitalize())
for box in boxes:
box.render(w=self.w, view=view)
self.w(u'</div>\n')
self.w(u'</div>\n')
return len(boxes)
@monkeypatch(basetemplates.TheMainTemplate)
def content_column(self, view, content_cols):
w = self.w
w(u'<div id="pageContent" class="col-md-%(col)s" role="main">' % {
'col': content_cols})
components = self._cw.vreg['components']
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
self.content_components(view, components)
self.content_header(view)
vtitle = self._cw.form.get('vtitle')
if vtitle:
w(u'<div class="vtitle">%s</div>\n' % xml_escape(vtitle))
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#contentColumn in template_body_header
@monkeypatch(basetemplates.TheMainTemplate)
def content_footer(self, view=None):
# TODO : do not add the wrapping div if no data
self.w(u'<div class="row">') # metadata and so
self.wview('contentfooter', rset=self.cw_rset, view=view)
self.w(u'</div>\n') # closes row
@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)
self.w(u'</div>\n') # closes id="page"
self.w(u'</body>\n')
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# 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)
self.state_header()
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
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
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
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 class="container-fluid">')
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'))
self.w(u'<div class="container">')
for component in components:
component.render(w=self.w)
self.w(u'</div>')
self.w(u'</nav>')
@monkeypatch(basetemplates.HTMLPageHeader)
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" title="%(title)s">[%(title)s]</a>' % {
'url' : self._cw.build_url(str(eid),
vid='edition', __mode='normal'),
'title': _('cancel')}
msg = ' '.join((_("searching for"),
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.HTMLPageFooter)
def call(self, **kwargs):
self.w(u'<footer id="pagefooter">')
self.w(u'</div>\n')
self.w(u'</footer>\n')
def registration_callback(vreg):
vreg.register_all(globals().values(), __name__)
vreg.unregister(actions.CancelSelectAction)