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
32
from ivle.webapp.breadcrumbs import Breadcrumber
34
class XHTMLView(BaseView):
36
A view which provides a base class for views which need to return XHTML
37
It is expected that apps which use this view will be written using Genshi
41
template = 'template.html'
47
extra_breadcrumbs = []
50
overlay_blacklist = []
52
def filter(self, stream, ctx):
55
def render(self, req):
56
req.content_type = 'text/html' # TODO: Detect application/xhtml+xml
59
viewctx = genshi.template.Context()
60
self.populate(req, viewctx)
62
# The template is found in the directory of the module containing the
64
app_template = os.path.join(os.path.dirname(
65
inspect.getmodule(self).__file__), self.template)
66
loader = genshi.template.TemplateLoader(".", auto_reload=True)
67
tmpl = loader.load(app_template)
68
app = self.filter(tmpl.generate(viewctx), viewctx)
71
for plugin in self.plugin_scripts:
72
for path in self.plugin_scripts[plugin]:
73
view_scripts.append(media_url(req, plugin, path))
76
for plugin in self.plugin_styles:
77
for path in self.plugin_styles[plugin]:
78
view_styles.append(media_url(req, plugin, path))
81
ctx = genshi.template.Context()
83
overlay_bits = self.render_overlays(req) if req.user else [[]]*4
84
ctx['overlays'] = overlay_bits[0]
86
ctx['styles'] = [media_url(req, CorePlugin, 'ivle.css')]
87
ctx['styles'] += view_styles
88
ctx['styles'] += overlay_bits[1]
90
ctx['scripts'] = [media_url(req, CorePlugin, path) for path in
91
('util.js', 'json2.js', 'md5.js')]
92
ctx['scripts'].append(media_url(req, '+external/jquery', 'jquery.js'))
93
ctx['scripts'] += view_scripts
94
ctx['scripts'] += overlay_bits[2]
96
ctx['scripts_init'] = self.scripts_init + overlay_bits[3]
97
ctx['app_template'] = app
98
ctx['title_img'] = media_url(req, CorePlugin,
99
"images/chrome/root-breadcrumb.png")
101
ctx['ancestry'] = req.router.get_ancestors(self.context)
105
# Allow the view to add its own fake breadcrumbs.
106
ctx['extra_breadcrumbs'] = self.extra_breadcrumbs
108
ctx['crumb'] = Breadcrumber(req).crumb
109
self.populate_headings(req, ctx)
110
tmpl = loader.load(os.path.join(os.path.dirname(__file__),
111
'ivle-headings.html'))
112
req.write(tmpl.generate(ctx).render('xhtml', doctype='xhtml'))
114
def populate(self, req, ctx):
115
raise NotImplementedError()
117
def populate_headings(self, req, ctx):
118
ctx['favicon'] = None
119
ctx['root_dir'] = req.config['urls']['root']
120
ctx['public_host'] = req.config['urls']['public_host']
121
ctx['svn_base'] = req.config['urls']['svn_addr']
122
ctx['write_javascript_settings'] = req.write_javascript_settings
124
ctx['login'] = req.user.login
125
ctx['logged_in'] = True
126
ctx['nick'] = req.user.nick
129
ctx['logged_in'] = False
130
ctx['publicmode'] = req.publicmode
131
if hasattr(self, 'help'):
132
ctx['help_path'] = self.help
134
ctx['apps_in_tabs'] = []
135
for plugin in req.config.plugin_index[ViewPlugin]:
136
if not hasattr(plugin, 'tabs'):
139
for tab in plugin.tabs:
140
# tab is a tuple: name, title, desc, icon, path
142
new_app['this_app'] = hasattr(self, 'tab') \
143
and tab[0] == self.tab
146
if tab[3] is not None:
147
new_app['has_icon'] = True
148
icon_url = media_url(req, plugin, tab[3])
149
new_app['icon_url'] = icon_url
150
if new_app['this_app']:
151
ctx['favicon'] = icon_url
153
new_app['has_icon'] = False
154
new_app['path'] = req.make_path(tab[4])
155
new_app['desc'] = tab[2]
156
new_app['name'] = tab[1]
157
new_app['weight'] = tab[5]
158
ctx['apps_in_tabs'].append(new_app)
160
ctx['apps_in_tabs'].sort(key=lambda tab: tab['weight'])
162
def render_overlays(self, req):
163
"""Generate XML streams for the overlays.
165
Returns a list of streams. Populates the scripts, styles, and
172
if not self.allow_overlays:
173
return (overlays, styles, scripts, scripts_init)
175
for plugin in req.config.plugin_index[OverlayPlugin]:
176
for overclass in plugin.overlays:
177
if overclass in self.overlay_blacklist:
179
overlay = overclass(req)
180
#TODO: Re-factor this to look nicer
181
for mplugin in overlay.plugin_scripts:
182
for path in overlay.plugin_scripts[mplugin]:
183
scripts.append(media_url(req, mplugin, path))
185
for mplugin in overlay.plugin_styles:
186
for path in overlay.plugin_styles[mplugin]:
187
styles.append(media_url(req, mplugin, path))
189
scripts_init += overlay.plugin_scripts_init
191
overlays.append(overlay.render(req))
192
return (overlays, styles, scripts, scripts_init)
195
def get_error_view(cls, e):
196
view_map = {HTTPError: XHTMLErrorView,
197
Unauthorized: XHTMLUnauthorizedView}
198
for exccls in inspect.getmro(type(e)):
199
if exccls in view_map:
200
return view_map[exccls]
202
class XHTMLErrorView(XHTMLView):
203
template = 'xhtmlerror.html'
205
def populate(self, req, ctx):
207
ctx['exception'] = self.context
209
class XHTMLUnauthorizedView(XHTMLErrorView):
210
template = 'xhtmlunauthorized.html'
212
def __init__(self, req, exception):
213
super(XHTMLUnauthorizedView, self).__init__(req, exception)
216
# Not logged in. Redirect to login page.
220
query_string = '?url=' + urllib.quote(req.uri, safe="/~")
221
req.throw_redirect('/+login' + query_string)