23
24
import genshi.template
25
26
from ivle.webapp.media import media_url
27
from ivle.webapp.core import Plugin as CorePlugin
26
28
from ivle.webapp.base.views import BaseView
27
from ivle.webapp.base.plugins import OverlayPlugin
29
from ivle.webapp.base.plugins import ViewPlugin, OverlayPlugin
28
30
from ivle.webapp.errors import HTTPError, Unauthorized
31
from ivle.webapp.publisher import NoPath
32
from ivle.webapp.breadcrumbs import Breadcrumber
32
34
class XHTMLView(BaseView):
39
41
template = 'template.html'
42
overlay_blacklist = []
44
def __init__(self, req, **kwargs):
46
setattr(self, key, kwargs[key])
43
breadcrumb_text = None
45
def __init__(self, *args, **kwargs):
46
super(XHTMLView, self).__init__(*args, **kwargs)
48
self.overlay_blacklist = []
50
self.plugin_scripts = {}
51
self.plugin_styles = {}
52
self.scripts_init = []
54
self.extra_breadcrumbs = []
55
self.overlay_blacklist = []
57
def get_context_ancestry(self, req):
58
return req.publisher.get_ancestors(self.context)
60
def filter(self, stream, ctx):
48
63
def render(self, req):
49
64
req.content_type = 'text/html' # TODO: Detect application/xhtml+xml
57
72
app_template = os.path.join(os.path.dirname(
58
73
inspect.getmodule(self).__file__), self.template)
59
req.write_html_head_foot = False
60
74
loader = genshi.template.TemplateLoader(".", auto_reload=True)
61
75
tmpl = loader.load(app_template)
62
app = tmpl.generate(viewctx)
76
app = self.filter(tmpl.generate(viewctx), viewctx)
64
79
for plugin in self.plugin_scripts:
65
80
for path in self.plugin_scripts[plugin]:
66
req.scripts.append(media_url(req, plugin, path))
81
view_scripts.append(media_url(req, plugin, path))
68
84
for plugin in self.plugin_styles:
69
85
for path in self.plugin_styles[plugin]:
70
req.styles.append(media_url(req, plugin, path))
86
view_styles.append(media_url(req, plugin, path))
73
89
ctx = genshi.template.Context()
74
# XXX: Leave this here!! (Before req.styles is read)
75
ctx['overlays'] = self.render_overlays(req)
76
ctx['app_styles'] = req.styles
77
ctx['scripts'] = req.scripts
78
ctx['scripts_init'] = req.scripts_init
91
overlay_bits = self.render_overlays(req) if req.user else [[]]*4
92
ctx['overlays'] = overlay_bits[0]
94
ctx['styles'] = [media_url(req, CorePlugin, 'ivle.css')]
95
ctx['styles'] += view_styles
96
ctx['styles'] += overlay_bits[1]
98
ctx['scripts'] = [media_url(req, CorePlugin, path) for path in
99
('util.js', 'json2.js', 'md5.js')]
100
ctx['scripts'].append(media_url(req, '+external/jquery', 'jquery.js'))
101
ctx['scripts'] += view_scripts
102
ctx['scripts'] += overlay_bits[2]
104
ctx['scripts_init'] = self.scripts_init + overlay_bits[3]
79
105
ctx['app_template'] = app
106
ctx['title_img'] = media_url(req, CorePlugin,
107
"images/chrome/root-breadcrumb.png")
109
ancestry = self.get_context_ancestry(req)
113
crumber = Breadcrumber(req)
115
ctx['breadcrumbs'] = []
116
for ancestor in ancestry:
117
crumb = crumber.crumb(ancestor)
121
if hasattr(crumb, 'extra_breadcrumbs_before'):
122
ctx['breadcrumbs'].extend(crumb.extra_breadcrumbs_before)
123
ctx['breadcrumbs'].append(crumb)
124
if hasattr(crumb, 'extra_breadcrumbs_after'):
125
ctx['breadcrumbs'].extend(crumb.extra_breadcrumbs_after)
127
# If the view has specified text for a breadcrumb, add one.
128
if self.breadcrumb_text:
129
ctx['breadcrumbs'].append(ViewBreadcrumb(req, self))
131
# Allow the view to add its own fake breadcrumbs.
132
ctx['breadcrumbs'].extend(self.extra_breadcrumbs)
80
134
self.populate_headings(req, ctx)
81
135
tmpl = loader.load(os.path.join(os.path.dirname(__file__),
82
136
'ivle-headings.html'))
98
153
ctx['login'] = None
99
154
ctx['logged_in'] = False
100
155
ctx['publicmode'] = req.publicmode
156
if hasattr(self, 'help'):
157
ctx['help_path'] = self.help
101
159
ctx['apps_in_tabs'] = []
102
for urlname in ivle.conf.apps.apps_in_tabs:
104
app = ivle.conf.apps.app_url[urlname]
105
new_app['this_app'] = hasattr(self, 'appname') \
106
and urlname == self.appname
108
new_app['has_icon'] = True
109
icon_dir = ivle.conf.apps.app_icon_dir
110
icon_url = ivle.util.make_path(os.path.join(icon_dir, app.icon))
111
new_app['icon_url'] = icon_url
112
if new_app['this_app']:
113
ctx['favicon'] = icon_url
115
new_app['has_icon'] = False
116
new_app['path'] = ivle.util.make_path(urlname)
117
new_app['desc'] = app.desc
118
new_app['name'] = app.name
119
ctx['apps_in_tabs'].append(new_app)
160
for plugin in req.config.plugin_index[ViewPlugin]:
161
if not hasattr(plugin, 'tabs'):
164
for tab in plugin.tabs:
165
# tab is a tuple: name, title, desc, icon, path, weight, admin
166
# (Admin is optional, defaults to false)
168
new_app['this_app'] = hasattr(self, 'tab') \
169
and tab[0] == self.tab
172
if tab[3] is not None:
173
new_app['has_icon'] = True
174
icon_url = media_url(req, plugin, tab[3])
175
new_app['icon_url'] = icon_url
176
if new_app['this_app']:
177
ctx['favicon'] = icon_url
179
new_app['has_icon'] = False
180
# The following check is here, so it is AFTER setting the
181
# icon, but BEFORE actually installing the tab in the menu
182
if len(tab) > 6 and tab[6]:
184
if not (req.user and req.user.admin):
186
new_app['path'] = req.make_path(tab[4])
187
new_app['desc'] = tab[2]
188
new_app['name'] = tab[1]
189
new_app['weight'] = tab[5]
190
ctx['apps_in_tabs'].append(new_app)
192
ctx['apps_in_tabs'].sort(key=lambda tab: tab['weight'])
121
194
def render_overlays(self, req):
122
195
"""Generate XML streams for the overlays.
133
212
#TODO: Re-factor this to look nicer
134
213
for mplugin in overlay.plugin_scripts:
135
214
for path in overlay.plugin_scripts[mplugin]:
136
req.scripts.append(media_url(req, mplugin, path))
215
scripts.append(media_url(req, mplugin, path))
138
217
for mplugin in overlay.plugin_styles:
139
218
for path in overlay.plugin_styles[mplugin]:
140
req.styles.append(media_url(req, mplugin, path))
219
styles.append(media_url(req, mplugin, path))
142
req.scripts_init += overlay.plugin_scripts_init
221
scripts_init += overlay.plugin_scripts_init
144
223
overlays.append(overlay.render(req))
224
return (overlays, styles, scripts, scripts_init)
148
227
def get_error_view(cls, e):
155
234
class XHTMLErrorView(XHTMLView):
156
235
template = 'xhtmlerror.html'
158
def __init__(self, req, exception):
159
self.context = exception
237
def __init__(self, req, context, lastobj):
238
super(XHTMLErrorView, self).__init__(req, context)
239
self.lastobj = lastobj
241
def get_context_ancestry(self, req):
242
return req.publisher.get_ancestors(self.lastobj)
161
244
def populate(self, req, ctx):
162
246
ctx['exception'] = self.context
164
248
class XHTMLUnauthorizedView(XHTMLErrorView):
165
249
template = 'xhtmlunauthorized.html'
167
def __init__(self, req, exception):
168
super(XHTMLUnauthorizedView, self).__init__(req, exception)
251
def __init__(self, req, exception, lastobj):
252
super(XHTMLUnauthorizedView, self).__init__(req, exception, lastobj)
254
if not req.publicmode and req.user is None:
170
255
# Not logged in. Redirect to login page.
171
req.throw_redirect('/') # XXX: Need proper URL.
259
query_string = '?url=' + urllib.quote(req.uri, safe="/~")
260
req.throw_redirect('/+login' + query_string)
264
class ViewBreadcrumb(object):
265
def __init__(self, req, context):
267
self.context = context
271
return self.context.breadcrumb_text