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):
34
class XHTMLView(BaseView):
57
36
A view which provides a base class for views which need to return XHTML
58
37
It is expected that apps which use this view will be written using Genshi
62
41
template = 'template.html'
63
44
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)
45
overlay_blacklist = []
47
def __init__(self, req, **kwargs):
49
setattr(self, key, kwargs[key])
81
51
def filter(self, stream, ctx):
93
63
app_template = os.path.join(os.path.dirname(
94
64
inspect.getmodule(self).__file__), self.template)
95
tmpl = self._loader.load(app_template)
65
loader = genshi.template.TemplateLoader(".", auto_reload=True)
66
tmpl = loader.load(app_template)
96
67
app = self.filter(tmpl.generate(viewctx), viewctx)
99
69
for plugin in self.plugin_scripts:
100
70
for path in self.plugin_scripts[plugin]:
101
view_scripts.append(media_url(req, plugin, path))
71
req.scripts.append(media_url(req, plugin, path))
104
73
for plugin in self.plugin_styles:
105
74
for path in self.plugin_styles[plugin]:
106
view_styles.append(media_url(req, plugin, path))
75
req.styles.append(media_url(req, plugin, path))
109
78
ctx = genshi.template.Context()
111
overlay_bits = self.render_overlays(req) if req.user else [[]]*4
112
ctx['overlays'] = overlay_bits[0]
79
# XXX: Leave this here!! (Before req.styles is read)
80
ctx['overlays'] = self.render_overlays(req) if req.user else []
114
82
ctx['styles'] = [media_url(req, CorePlugin, 'ivle.css')]
115
ctx['styles'] += view_styles
116
ctx['styles'] += overlay_bits[1]
83
ctx['styles'] += req.styles
118
85
ctx['scripts'] = [media_url(req, CorePlugin, path) for path in
119
86
('util.js', 'json2.js', 'md5.js')]
120
87
ctx['scripts'].append(media_url(req, '+external/jquery', 'jquery.js'))
121
ctx['scripts'] += view_scripts
122
ctx['scripts'] += overlay_bits[2]
88
ctx['scripts'] += req.scripts
124
ctx['scripts_init'] = self.scripts_init + overlay_bits[3]
90
ctx['scripts_init'] = req.scripts_init
125
91
ctx['app_template'] = app
126
92
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)
93
"images/chrome/title.png")
155
94
self.populate_headings(req, ctx)
156
tmpl = self._loader.load(os.path.join(os.path.dirname(__file__),
95
tmpl = loader.load(os.path.join(os.path.dirname(__file__),
157
96
'ivle-headings.html'))
158
97
req.write(tmpl.generate(ctx).render('xhtml', doctype='xhtml'))
163
102
def populate_headings(self, req, ctx):
164
103
ctx['favicon'] = None
165
ctx['root_dir'] = req.config['urls']['root']
166
ctx['public_host'] = req.config['urls']['public_host']
167
ctx['svn_base'] = req.config['urls']['svn_addr']
104
ctx['root_dir'] = ivle.conf.root_dir
105
ctx['public_host'] = ivle.conf.public_host
106
ctx['svn_base'] = ivle.conf.svn_addr
168
107
ctx['write_javascript_settings'] = req.write_javascript_settings
170
109
ctx['login'] = req.user.login
198
136
ctx['favicon'] = icon_url
200
138
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
new_app['path'] = req.make_path(tab[4])
139
new_app['path'] = ivle.util.make_path(tab[4])
208
140
new_app['desc'] = tab[2]
209
141
new_app['name'] = tab[1]
210
142
new_app['weight'] = tab[5]
233
162
#TODO: Re-factor this to look nicer
234
163
for mplugin in overlay.plugin_scripts:
235
164
for path in overlay.plugin_scripts[mplugin]:
236
scripts.append(media_url(req, mplugin, path))
165
req.scripts.append(media_url(req, mplugin, path))
238
167
for mplugin in overlay.plugin_styles:
239
168
for path in overlay.plugin_styles[mplugin]:
240
styles.append(media_url(req, mplugin, path))
169
req.styles.append(media_url(req, mplugin, path))
242
scripts_init += overlay.plugin_scripts_init
171
req.scripts_init += overlay.plugin_scripts_init
244
173
overlays.append(overlay.render(req))
245
return (overlays, styles, scripts, scripts_init)
248
177
def get_error_view(cls, e):
255
184
class XHTMLErrorView(XHTMLView):
256
185
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)
187
def __init__(self, req, exception):
188
self.context = exception
265
190
def populate(self, req, ctx):
267
191
ctx['exception'] = self.context
268
req.headers_out['X-IVLE-Error'] = self.context.message
270
193
class XHTMLUnauthorizedView(XHTMLErrorView):
271
194
template = 'xhtmlunauthorized.html'
273
def __init__(self, req, exception, lastobj):
274
super(XHTMLUnauthorizedView, self).__init__(req, exception, lastobj)
196
def __init__(self, req, exception):
197
super(XHTMLUnauthorizedView, self).__init__(req, exception)
276
if not req.publicmode and req.user is None:
277
200
# Not logged in. Redirect to login page.
278
201
if req.uri == '/':
279
202
query_string = ''