1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2009 The University of Melbourne
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
# GNU General Public License for more details.
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# Author: Nick Chadwick
24
import genshi.template
26
from ivle.webapp.media import media_url
27
from ivle.webapp.core import Plugin as CorePlugin
28
from ivle.webapp.base.views import BaseView
29
from ivle.webapp.base.plugins import ViewPlugin, OverlayPlugin
30
from ivle.webapp.errors import HTTPError, Unauthorized
31
from ivle.webapp.routing import NoPath
33
class XHTMLView(BaseView):
35
A view which provides a base class for views which need to return XHTML
36
It is expected that apps which use this view will be written using Genshi
40
template = 'template.html'
47
overlay_blacklist = []
49
def filter(self, stream, ctx):
52
def render(self, req):
53
req.content_type = 'text/html' # TODO: Detect application/xhtml+xml
56
viewctx = genshi.template.Context()
57
self.populate(req, viewctx)
59
# The template is found in the directory of the module containing the
61
app_template = os.path.join(os.path.dirname(
62
inspect.getmodule(self).__file__), self.template)
63
loader = genshi.template.TemplateLoader(".", auto_reload=True)
64
tmpl = loader.load(app_template)
65
app = self.filter(tmpl.generate(viewctx), viewctx)
68
for plugin in self.plugin_scripts:
69
for path in self.plugin_scripts[plugin]:
70
view_scripts.append(media_url(req, plugin, path))
73
for plugin in self.plugin_styles:
74
for path in self.plugin_styles[plugin]:
75
view_styles.append(media_url(req, plugin, path))
78
ctx = genshi.template.Context()
80
overlay_bits = self.render_overlays(req) if req.user else [[]]*4
81
ctx['overlays'] = overlay_bits[0]
83
ctx['styles'] = [media_url(req, CorePlugin, 'ivle.css')]
84
ctx['styles'] += view_styles
85
ctx['styles'] += overlay_bits[1]
87
ctx['scripts'] = [media_url(req, CorePlugin, path) for path in
88
('util.js', 'json2.js', 'md5.js')]
89
ctx['scripts'].append(media_url(req, '+external/jquery', 'jquery.js'))
90
ctx['scripts'] += view_scripts
91
ctx['scripts'] += overlay_bits[2]
93
ctx['scripts_init'] = self.scripts_init + overlay_bits[3]
94
ctx['app_template'] = app
95
ctx['title_img'] = media_url(req, CorePlugin,
96
"images/chrome/root-breadcrumb.png")
98
ctx['ancestry'] = req.router.get_ancestors(self.context)
101
ctx['breadcrumb_text'] = lambda x: x # TODO: Do it properly.
102
ctx['url'] = req.router.generate
103
self.populate_headings(req, ctx)
104
tmpl = loader.load(os.path.join(os.path.dirname(__file__),
105
'ivle-headings.html'))
106
req.write(tmpl.generate(ctx).render('xhtml', doctype='xhtml'))
108
def populate(self, req, ctx):
109
raise NotImplementedError()
111
def populate_headings(self, req, ctx):
112
ctx['favicon'] = None
113
ctx['root_dir'] = req.config['urls']['root']
114
ctx['public_host'] = req.config['urls']['public_host']
115
ctx['svn_base'] = req.config['urls']['svn_addr']
116
ctx['write_javascript_settings'] = req.write_javascript_settings
118
ctx['login'] = req.user.login
119
ctx['logged_in'] = True
120
ctx['nick'] = req.user.nick
123
ctx['logged_in'] = False
124
ctx['publicmode'] = req.publicmode
125
if hasattr(self, 'help'):
126
ctx['help_path'] = self.help
128
ctx['apps_in_tabs'] = []
129
for plugin in req.config.plugin_index[ViewPlugin]:
130
if not hasattr(plugin, 'tabs'):
133
for tab in plugin.tabs:
134
# tab is a tuple: name, title, desc, icon, path
136
new_app['this_app'] = hasattr(self, 'tab') \
137
and tab[0] == self.tab
140
if tab[3] is not None:
141
new_app['has_icon'] = True
142
icon_url = media_url(req, plugin, tab[3])
143
new_app['icon_url'] = icon_url
144
if new_app['this_app']:
145
ctx['favicon'] = icon_url
147
new_app['has_icon'] = False
148
new_app['path'] = req.make_path(tab[4])
149
new_app['desc'] = tab[2]
150
new_app['name'] = tab[1]
151
new_app['weight'] = tab[5]
152
ctx['apps_in_tabs'].append(new_app)
154
ctx['apps_in_tabs'].sort(key=lambda tab: tab['weight'])
156
def render_overlays(self, req):
157
"""Generate XML streams for the overlays.
159
Returns a list of streams. Populates the scripts, styles, and
166
if not self.allow_overlays:
167
return (overlays, styles, scripts, scripts_init)
169
for plugin in req.config.plugin_index[OverlayPlugin]:
170
for overclass in plugin.overlays:
171
if overclass in self.overlay_blacklist:
173
overlay = overclass(req)
174
#TODO: Re-factor this to look nicer
175
for mplugin in overlay.plugin_scripts:
176
for path in overlay.plugin_scripts[mplugin]:
177
scripts.append(media_url(req, mplugin, path))
179
for mplugin in overlay.plugin_styles:
180
for path in overlay.plugin_styles[mplugin]:
181
styles.append(media_url(req, mplugin, path))
183
scripts_init += overlay.plugin_scripts_init
185
overlays.append(overlay.render(req))
186
return (overlays, styles, scripts, scripts_init)
189
def get_error_view(cls, e):
190
view_map = {HTTPError: XHTMLErrorView,
191
Unauthorized: XHTMLUnauthorizedView}
192
for exccls in inspect.getmro(type(e)):
193
if exccls in view_map:
194
return view_map[exccls]
196
class XHTMLErrorView(XHTMLView):
197
template = 'xhtmlerror.html'
199
def populate(self, req, ctx):
201
ctx['exception'] = self.context
203
class XHTMLUnauthorizedView(XHTMLErrorView):
204
template = 'xhtmlunauthorized.html'
206
def __init__(self, req, exception):
207
super(XHTMLUnauthorizedView, self).__init__(req, exception)
210
# Not logged in. Redirect to login page.
214
query_string = '?url=' + urllib.quote(req.uri, safe="/~")
215
req.throw_redirect('/+login' + query_string)