~azzar1/unity/add-show-desktop-key

1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
1
# IVLE - Informatics Virtual Learning Environment
2
# Copyright (C) 2007-2009 The University of Melbourne
3
#
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.
8
#
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.
13
#
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
17
18
# Author: Nick Chadwick
19
20
import inspect
21
import os.path
1099.1.120 by William Grant
Move the login machinery to the new framework.
22
import urllib
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
23
24
import genshi.template
25
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
26
from ivle.webapp.media import media_url
1092.1.24 by William Grant
Convince the main XHTML template to use versioned styles/scripts, and move the
27
from ivle.webapp.core import Plugin as CorePlugin
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
28
from ivle.webapp.base.views import BaseView
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
29
from ivle.webapp.base.plugins import ViewPlugin, OverlayPlugin
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
30
from ivle.webapp.errors import HTTPError, Unauthorized
1294.2.116 by William Grant
Merge from object-publishing.
31
from ivle.webapp.publisher import NoPath
1294.2.90 by William Grant
Produce breadcrumb menus.
32
from ivle.webapp.breadcrumbs import Breadcrumber
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
33
34
class XHTMLView(BaseView):
35
    """
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
38
    templates.
39
    """
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
40
41
    template = 'template.html'
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
42
    allow_overlays = True
1294.2.127 by William Grant
Allow views to specify text for their breadcrumb.
43
    breadcrumb_text = None
1294.2.115 by William Grant
Don't share XHTMLView's attributes between instances. oops.
44
45
    def __init__(self, *args, **kwargs):
46
        super(XHTMLView, self).__init__(*args, **kwargs)
47
48
        self.overlay_blacklist = []
49
50
        self.plugin_scripts = {}
51
        self.plugin_styles = {}
52
        self.scripts_init = []
53
54
        self.extra_breadcrumbs = []
55
        self.overlay_blacklist = []
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
56
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
57
    def get_context_ancestry(self, req):
1294.2.118 by William Grant
Merge from object-publishing.
58
        return req.publisher.get_ancestors(self.context)
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
59
1148 by William Grant
Allow XHTMLView subclasses to apply filters to the generated app XML stream.
60
    def filter(self, stream, ctx):
61
        return stream
62
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
63
    def render(self, req):
64
        req.content_type = 'text/html' # TODO: Detect application/xhtml+xml
1099.1.38 by William Grant
ivle.webapp.base.xhtml.XHTMLView: Split the global and view contexts.
65
66
        # View template
67
        viewctx = genshi.template.Context()
68
        self.populate(req, viewctx)
69
70
        # The template is found in the directory of the module containing the
71
        # view.
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
72
        app_template = os.path.join(os.path.dirname(
1099.1.35 by William Grant
ivle.webapp.base.xhtml#XHTMLView: Rename app_template to template (the things
73
                        inspect.getmodule(self).__file__), self.template) 
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
74
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
75
        tmpl = loader.load(app_template)
1148 by William Grant
Allow XHTMLView subclasses to apply filters to the generated app XML stream.
76
        app = self.filter(tmpl.generate(viewctx), viewctx)
1099.1.38 by William Grant
ivle.webapp.base.xhtml.XHTMLView: Split the global and view contexts.
77
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
78
        view_scripts = []
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
79
        for plugin in self.plugin_scripts:
80
            for path in self.plugin_scripts[plugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
81
                view_scripts.append(media_url(req, plugin, path))
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
82
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
83
        view_styles = []
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
84
        for plugin in self.plugin_styles:
85
            for path in self.plugin_styles[plugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
86
                view_styles.append(media_url(req, plugin, path))
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
87
1099.1.38 by William Grant
ivle.webapp.base.xhtml.XHTMLView: Split the global and view contexts.
88
        # Global template
89
        ctx = genshi.template.Context()
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
90
91
        overlay_bits = self.render_overlays(req) if req.user else [[]]*4
92
        ctx['overlays'] = overlay_bits[0]
1092.1.24 by William Grant
Convince the main XHTML template to use versioned styles/scripts, and move the
93
94
        ctx['styles'] = [media_url(req, CorePlugin, 'ivle.css')]
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
95
        ctx['styles'] += view_styles
96
        ctx['styles'] += overlay_bits[1]
1092.1.24 by William Grant
Convince the main XHTML template to use versioned styles/scripts, and move the
97
98
        ctx['scripts'] = [media_url(req, CorePlugin, path) for path in
1092.1.28 by William Grant
Only load tos.js on /+tos.
99
                           ('util.js', 'json2.js', 'md5.js')]
1160 by William Grant
Swap the version and +external in media URLs, and use media_url for jQuery.
100
        ctx['scripts'].append(media_url(req, '+external/jquery', 'jquery.js'))
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
101
        ctx['scripts'] += view_scripts
102
        ctx['scripts'] += overlay_bits[2]
1092.1.24 by William Grant
Convince the main XHTML template to use versioned styles/scripts, and move the
103
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
104
        ctx['scripts_init'] = self.scripts_init + overlay_bits[3]
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
105
        ctx['app_template'] = app
1100.1.9 by Matt Giuca
The IVLE logo is now an <img> element, rather than a CSS background.
106
        ctx['title_img'] = media_url(req, CorePlugin,
1294.2.74 by William Grant
Replace the title image in the root template with the breadcrumb.
107
                                     "images/chrome/root-breadcrumb.png")
1294.2.79 by William Grant
Don't crash if the ancestry can't be calculated; assume none instead.
108
        try:
1465.1.1 by William Grant
Allow breadcrumbs to specify additional crumbs to surround them.
109
            ancestry = self.get_context_ancestry(req)
1294.2.79 by William Grant
Don't crash if the ancestry can't be calculated; assume none instead.
110
        except NoPath:
1465.1.1 by William Grant
Allow breadcrumbs to specify additional crumbs to surround them.
111
            ancestry = []
112
113
        crumber = Breadcrumber(req)
114
115
        ctx['breadcrumbs'] = []
116
        for ancestor in ancestry:
117
            crumb = crumber.crumb(ancestor)
118
            if crumb is None:
119
                continue
120
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)
1294.2.101 by William Grant
Allow views to specify extra breadcrumbs.
126
1294.2.127 by William Grant
Allow views to specify text for their breadcrumb.
127
        # If the view has specified text for a breadcrumb, add one.
128
        if self.breadcrumb_text:
1465.1.1 by William Grant
Allow breadcrumbs to specify additional crumbs to surround them.
129
            ctx['breadcrumbs'].append(ViewBreadcrumb(req, self))
1294.2.127 by William Grant
Allow views to specify text for their breadcrumb.
130
1294.2.101 by William Grant
Allow views to specify extra breadcrumbs.
131
        # Allow the view to add its own fake breadcrumbs.
1465.1.1 by William Grant
Allow breadcrumbs to specify additional crumbs to surround them.
132
        ctx['breadcrumbs'].extend(self.extra_breadcrumbs)
1294.2.101 by William Grant
Allow views to specify extra breadcrumbs.
133
1099.1.38 by William Grant
ivle.webapp.base.xhtml.XHTMLView: Split the global and view contexts.
134
        self.populate_headings(req, ctx)
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
135
        tmpl = loader.load(os.path.join(os.path.dirname(__file__), 
136
                                                        'ivle-headings.html'))
137
        req.write(tmpl.generate(ctx).render('xhtml', doctype='xhtml'))
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
138
        
139
    def populate(self, req, ctx):
140
        raise NotImplementedError()
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
141
142
    def populate_headings(self, req, ctx):
143
        ctx['favicon'] = None
1202 by William Grant
XHTMLView no longer uses ivle.conf.
144
        ctx['root_dir'] = req.config['urls']['root']
145
        ctx['public_host'] = req.config['urls']['public_host']
146
        ctx['svn_base'] = req.config['urls']['svn_addr']
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
147
        ctx['write_javascript_settings'] = req.write_javascript_settings
148
        if req.user:
149
            ctx['login'] = req.user.login
150
            ctx['logged_in'] = True
151
            ctx['nick'] = req.user.nick
152
        else:
153
            ctx['login'] = None
1099.1.92 by William Grant
Fix the XHTML base template to not crash when not logged in.
154
            ctx['logged_in'] = False
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
155
        ctx['publicmode'] = req.publicmode
1099.1.100 by Nick Chadwick
Created a new help system.
156
        if hasattr(self, 'help'):
157
            ctx['help_path'] = self.help
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
158
159
        ctx['apps_in_tabs'] = []
1092.1.59 by William Grant
Move the plugin loading/indexing logic into ivle.config.Config.
160
        for plugin in req.config.plugin_index[ViewPlugin]:
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
161
            if not hasattr(plugin, 'tabs'):
162
                continue
163
164
            for tab in plugin.tabs:
1497 by Matt Giuca
Added Users tab to drop-down menu, for admins only.
165
                # tab is a tuple: name, title, desc, icon, path, weight, admin
166
                # (Admin is optional, defaults to false)
167
                if len(tab) > 6 and tab[6]:
168
                    # Admin-only tab
169
                    if not (req.user and req.user.admin):
170
                        break
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
171
                new_app = {}
1116 by William Grant
Move the old tutorial views into the 'subjects' tab, so they get the right
172
                new_app['this_app'] = hasattr(self, 'tab') \
173
                                      and tab[0] == self.tab
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
174
175
                # Icon name
176
                if tab[3] is not None:
177
                    new_app['has_icon'] = True
178
                    icon_url = media_url(req, plugin, tab[3])
179
                    new_app['icon_url'] = icon_url
180
                    if new_app['this_app']:
181
                        ctx['favicon'] = icon_url
182
                else:
183
                    new_app['has_icon'] = False
1210 by William Grant
Use Request.make_path everywhere.
184
                new_app['path'] = req.make_path(tab[4])
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
185
                new_app['desc'] = tab[2]
186
                new_app['name'] = tab[1]
187
                new_app['weight'] = tab[5]
188
                ctx['apps_in_tabs'].append(new_app)
189
190
        ctx['apps_in_tabs'].sort(key=lambda tab: tab['weight'])
191
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
192
    def render_overlays(self, req):
193
        """Generate XML streams for the overlays.
194
        
195
        Returns a list of streams. Populates the scripts, styles, and 
196
        scripts_init.
197
        """
198
        overlays = []
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
199
        styles = []
200
        scripts = []
201
        scripts_init = []
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
202
        if not self.allow_overlays:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
203
            return (overlays, styles, scripts, scripts_init)
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
204
1092.1.59 by William Grant
Move the plugin loading/indexing logic into ivle.config.Config.
205
        for plugin in req.config.plugin_index[OverlayPlugin]:
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
206
            for overclass in plugin.overlays:
207
                if overclass in self.overlay_blacklist:
208
                    continue
209
                overlay = overclass(req)
210
                #TODO: Re-factor this to look nicer
211
                for mplugin in overlay.plugin_scripts:
212
                    for path in overlay.plugin_scripts[mplugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
213
                        scripts.append(media_url(req, mplugin, path))
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
214
215
                for mplugin in overlay.plugin_styles:
216
                    for path in overlay.plugin_styles[mplugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
217
                        styles.append(media_url(req, mplugin, path))
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
218
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
219
                scripts_init += overlay.plugin_scripts_init
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
220
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
221
                overlays.append(overlay.render(req))
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
222
        return (overlays, styles, scripts, scripts_init)
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
223
1099.1.95 by William Grant
Error views are now retrieved from a class method of the view, not the plugin
224
    @classmethod
225
    def get_error_view(cls, e):
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
226
        view_map = {HTTPError:    XHTMLErrorView,
227
                    Unauthorized: XHTMLUnauthorizedView}
1099.1.95 by William Grant
Error views are now retrieved from a class method of the view, not the plugin
228
        for exccls in inspect.getmro(type(e)):
229
            if exccls in view_map:
230
                return view_map[exccls]
231
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
232
class XHTMLErrorView(XHTMLView):
233
    template = 'xhtmlerror.html'
234
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
235
    def __init__(self, req, context, lastobj):
236
        super(XHTMLErrorView, self).__init__(req, context)
237
        self.lastobj = lastobj
238
239
    def get_context_ancestry(self, req):
1294.2.118 by William Grant
Merge from object-publishing.
240
        return req.publisher.get_ancestors(self.lastobj)
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
241
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
242
    def populate(self, req, ctx):
1294.2.50 by William Grant
Replace all base view __init__s with one at the root that sets the context.
243
        ctx['req'] = req
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
244
        ctx['exception'] = self.context
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
245
246
class XHTMLUnauthorizedView(XHTMLErrorView):
247
    template = 'xhtmlunauthorized.html'
248
1294.2.109 by William Grant
Fix XHTMLUnauthorizedView to take the extra breadcrumb-related arg.
249
    def __init__(self, req, exception, lastobj):
250
        super(XHTMLUnauthorizedView, self).__init__(req, exception, lastobj)
1099.1.120 by William Grant
Move the login machinery to the new framework.
251
1294.2.134 by William Grant
Use a different set of routes for public mode, too.
252
        if not req.publicmode and req.user is None:
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
253
            # Not logged in. Redirect to login page.
1099.1.209 by William Grant
Don't escape / in paths when they are in +login/+tos URLs.
254
            if req.uri == '/':
255
                query_string = ''
256
            else:
257
                query_string = '?url=' + urllib.quote(req.uri, safe="/~")
258
            req.throw_redirect('/+login' + query_string)
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
259
260
        req.status = 403
1294.2.127 by William Grant
Allow views to specify text for their breadcrumb.
261
262
class ViewBreadcrumb(object):
263
    def __init__(self, req, context):
264
        self.req = req
265
        self.context = context
266
267
    @property
268
    def text(self):
269
        return self.context.breadcrumb_text