~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)
1099.1.115 by William Grant
Add tabs to the new framework. Move the app icons into the apps themselves.
167
                new_app = {}
1116 by William Grant
Move the old tutorial views into the 'subjects' tab, so they get the right
168
                new_app['this_app'] = hasattr(self, 'tab') \
169
                                      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.
170
171
                # Icon name
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
178
                else:
179
                    new_app['has_icon'] = False
1498 by Matt Giuca
user.py: All user pages are now in the tab "users", so they show the user icon
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]:
183
                    # Admin-only tab
184
                    if not (req.user and req.user.admin):
185
                        break
1210 by William Grant
Use Request.make_path everywhere.
186
                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.
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)
191
192
        ctx['apps_in_tabs'].sort(key=lambda tab: tab['weight'])
193
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
194
    def render_overlays(self, req):
195
        """Generate XML streams for the overlays.
196
        
197
        Returns a list of streams. Populates the scripts, styles, and 
198
        scripts_init.
199
        """
200
        overlays = []
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
201
        styles = []
202
        scripts = []
203
        scripts_init = []
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
204
        if not self.allow_overlays:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
205
            return (overlays, styles, scripts, scripts_init)
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
206
1092.1.59 by William Grant
Move the plugin loading/indexing logic into ivle.config.Config.
207
        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
208
            for overclass in plugin.overlays:
209
                if overclass in self.overlay_blacklist:
210
                    continue
211
                overlay = overclass(req)
212
                #TODO: Re-factor this to look nicer
213
                for mplugin in overlay.plugin_scripts:
214
                    for path in overlay.plugin_scripts[mplugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
215
                        scripts.append(media_url(req, mplugin, path))
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
216
217
                for mplugin in overlay.plugin_styles:
218
                    for path in overlay.plugin_styles[mplugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
219
                        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
220
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
221
                scripts_init += overlay.plugin_scripts_init
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
222
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
223
                overlays.append(overlay.render(req))
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
224
        return (overlays, styles, scripts, scripts_init)
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
225
1099.1.95 by William Grant
Error views are now retrieved from a class method of the view, not the plugin
226
    @classmethod
227
    def get_error_view(cls, e):
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
228
        view_map = {HTTPError:    XHTMLErrorView,
229
                    Unauthorized: XHTMLUnauthorizedView}
1099.1.95 by William Grant
Error views are now retrieved from a class method of the view, not the plugin
230
        for exccls in inspect.getmro(type(e)):
231
            if exccls in view_map:
232
                return view_map[exccls]
233
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
234
class XHTMLErrorView(XHTMLView):
235
    template = 'xhtmlerror.html'
236
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
237
    def __init__(self, req, context, lastobj):
238
        super(XHTMLErrorView, self).__init__(req, context)
239
        self.lastobj = lastobj
240
241
    def get_context_ancestry(self, req):
1294.2.118 by William Grant
Merge from object-publishing.
242
        return req.publisher.get_ancestors(self.lastobj)
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
243
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
244
    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.
245
        ctx['req'] = req
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
246
        ctx['exception'] = self.context
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
247
248
class XHTMLUnauthorizedView(XHTMLErrorView):
249
    template = 'xhtmlunauthorized.html'
250
1294.2.109 by William Grant
Fix XHTMLUnauthorizedView to take the extra breadcrumb-related arg.
251
    def __init__(self, req, exception, lastobj):
252
        super(XHTMLUnauthorizedView, self).__init__(req, exception, lastobj)
1099.1.120 by William Grant
Move the login machinery to the new framework.
253
1294.2.134 by William Grant
Use a different set of routes for public mode, too.
254
        if not req.publicmode and req.user is None:
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
255
            # 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.
256
            if req.uri == '/':
257
                query_string = ''
258
            else:
259
                query_string = '?url=' + urllib.quote(req.uri, safe="/~")
260
            req.throw_redirect('/+login' + query_string)
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
261
262
        req.status = 403
1294.2.127 by William Grant
Allow views to specify text for their breadcrumb.
263
264
class ViewBreadcrumb(object):
265
    def __init__(self, req, context):
266
        self.req = req
267
        self.context = context
268
269
    @property
270
    def text(self):
271
        return self.context.breadcrumb_text