28
28
from ivle.webapp.base.views import BaseView
29
29
from ivle.webapp.base.plugins import ViewPlugin, OverlayPlugin
30
30
from ivle.webapp.errors import HTTPError, Unauthorized
31
from ivle.webapp.publisher import NoPath
32
from ivle.webapp.breadcrumbs import Breadcrumber
35
class GenshiLoaderMixin(object):
36
"""Mixin for classes which need to render Genshi templates.
38
A TemplateLoader is shared between all instances, so templates are
39
cached across multiple instances and therefore also requests.
43
def __init__(self, *args, **kwargs):
44
super(GenshiLoaderMixin, self).__init__(*args, **kwargs)
46
# We use a single loader for all views, so we can cache the
47
# parsed templates. auto_reload is convenient and has a minimal
48
# performance penalty, so we'll leave it on.
49
if GenshiLoaderMixin._loader is None:
50
GenshiLoaderMixin._loader = genshi.template.TemplateLoader(
51
".", auto_reload=True,
55
class XHTMLView(GenshiLoaderMixin, BaseView):
32
class XHTMLView(BaseView):
57
34
A view which provides a base class for views which need to return XHTML
58
35
It is expected that apps which use this view will be written using Genshi
62
39
template = 'template.html'
63
45
allow_overlays = True
64
breadcrumb_text = None
66
def __init__(self, *args, **kwargs):
67
super(XHTMLView, self).__init__(*args, **kwargs)
69
self.overlay_blacklist = []
71
self.plugin_scripts = {}
72
self.plugin_styles = {}
73
self.scripts_init = []
75
self.extra_breadcrumbs = []
76
self.overlay_blacklist = []
78
def get_context_ancestry(self, req):
79
return req.publisher.get_ancestors(self.context)
46
overlay_blacklist = []
48
def __init__(self, req, **kwargs):
50
setattr(self, key, kwargs[key])
81
52
def filter(self, stream, ctx):
93
64
app_template = os.path.join(os.path.dirname(
94
65
inspect.getmodule(self).__file__), self.template)
95
tmpl = self._loader.load(app_template)
66
loader = genshi.template.TemplateLoader(".", auto_reload=True)
67
tmpl = loader.load(app_template)
96
68
app = self.filter(tmpl.generate(viewctx), viewctx)
124
96
ctx['scripts_init'] = self.scripts_init + overlay_bits[3]
125
97
ctx['app_template'] = app
126
98
ctx['title_img'] = media_url(req, CorePlugin,
127
"images/chrome/root-breadcrumb.png")
129
ancestry = self.get_context_ancestry(req)
133
crumber = Breadcrumber(req)
135
ctx['breadcrumbs'] = []
136
if not req.publicmode:
137
for ancestor in ancestry:
138
crumb = crumber.crumb(ancestor)
142
if hasattr(crumb, 'extra_breadcrumbs_before'):
143
ctx['breadcrumbs'].extend(crumb.extra_breadcrumbs_before)
144
ctx['breadcrumbs'].append(crumb)
145
if hasattr(crumb, 'extra_breadcrumbs_after'):
146
ctx['breadcrumbs'].extend(crumb.extra_breadcrumbs_after)
148
# If the view has specified text for a breadcrumb, add one.
149
if self.breadcrumb_text:
150
ctx['breadcrumbs'].append(ViewBreadcrumb(req, self))
152
# Allow the view to add its own fake breadcrumbs.
153
ctx['breadcrumbs'].extend(self.extra_breadcrumbs)
99
"images/chrome/title.png")
155
100
self.populate_headings(req, ctx)
156
tmpl = self._loader.load(os.path.join(os.path.dirname(__file__),
101
tmpl = loader.load(os.path.join(os.path.dirname(__file__),
157
102
'ivle-headings.html'))
158
103
req.write(tmpl.generate(ctx).render('xhtml', doctype='xhtml'))
185
130
for tab in plugin.tabs:
186
# tab is a tuple: name, title, desc, icon, path, weight, admin
187
# (Admin is optional, defaults to false)
131
# tab is a tuple: name, title, desc, icon, path
189
133
new_app['this_app'] = hasattr(self, 'tab') \
190
134
and tab[0] == self.tab
198
142
ctx['favicon'] = icon_url
200
144
new_app['has_icon'] = False
201
# The following check is here, so it is AFTER setting the
202
# icon, but BEFORE actually installing the tab in the menu
203
if len(tab) > 6 and tab[6]:
205
if not (req.user and req.user.admin):
207
145
new_app['path'] = req.make_path(tab[4])
208
146
new_app['desc'] = tab[2]
209
147
new_app['name'] = tab[1]
255
193
class XHTMLErrorView(XHTMLView):
256
194
template = 'xhtmlerror.html'
258
def __init__(self, req, context, lastobj):
259
super(XHTMLErrorView, self).__init__(req, context)
260
self.lastobj = lastobj
262
def get_context_ancestry(self, req):
263
return req.publisher.get_ancestors(self.lastobj)
196
def __init__(self, req, exception):
197
self.context = exception
265
199
def populate(self, req, ctx):
267
200
ctx['exception'] = self.context
268
req.headers_out['X-IVLE-Error'] = self.context.message
270
202
class XHTMLUnauthorizedView(XHTMLErrorView):
271
203
template = 'xhtmlunauthorized.html'
273
def __init__(self, req, exception, lastobj):
274
super(XHTMLUnauthorizedView, self).__init__(req, exception, lastobj)
205
def __init__(self, req, exception):
206
super(XHTMLUnauthorizedView, self).__init__(req, exception)
276
if not req.publicmode and req.user is None:
277
209
# Not logged in. Redirect to login page.
278
210
if req.uri == '/':
279
211
query_string = ''
282
214
req.throw_redirect('/+login' + query_string)
286
class ViewBreadcrumb(object):
287
def __init__(self, req, context):
289
self.context = context
293
return self.context.breadcrumb_text