~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'] = []
1585 by William Grant
Disable breadcrumbs in public mode.
116
        if not req.publicmode:
117
            for ancestor in ancestry:
118
                crumb = crumber.crumb(ancestor)
119
                if crumb is None:
120
                    continue
121
122
                if hasattr(crumb, 'extra_breadcrumbs_before'):
123
                    ctx['breadcrumbs'].extend(crumb.extra_breadcrumbs_before)
124
                ctx['breadcrumbs'].append(crumb)
125
                if hasattr(crumb, 'extra_breadcrumbs_after'):
126
                    ctx['breadcrumbs'].extend(crumb.extra_breadcrumbs_after)
127
128
            # If the view has specified text for a breadcrumb, add one.
129
            if self.breadcrumb_text:
130
                ctx['breadcrumbs'].append(ViewBreadcrumb(req, self))
131
132
            # Allow the view to add its own fake breadcrumbs.
133
            ctx['breadcrumbs'].extend(self.extra_breadcrumbs)
1294.2.101 by William Grant
Allow views to specify extra breadcrumbs.
134
1099.1.38 by William Grant
ivle.webapp.base.xhtml.XHTMLView: Split the global and view contexts.
135
        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
136
        tmpl = loader.load(os.path.join(os.path.dirname(__file__), 
137
                                                        'ivle-headings.html'))
138
        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
139
        
140
    def populate(self, req, ctx):
141
        raise NotImplementedError()
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
142
143
    def populate_headings(self, req, ctx):
144
        ctx['favicon'] = None
1202 by William Grant
XHTMLView no longer uses ivle.conf.
145
        ctx['root_dir'] = req.config['urls']['root']
146
        ctx['public_host'] = req.config['urls']['public_host']
147
        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
148
        ctx['write_javascript_settings'] = req.write_javascript_settings
149
        if req.user:
150
            ctx['login'] = req.user.login
151
            ctx['logged_in'] = True
152
            ctx['nick'] = req.user.nick
153
        else:
154
            ctx['login'] = None
1099.1.92 by William Grant
Fix the XHTML base template to not crash when not logged in.
155
            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
156
        ctx['publicmode'] = req.publicmode
1099.1.100 by Nick Chadwick
Created a new help system.
157
        if hasattr(self, 'help'):
158
            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.
159
160
        ctx['apps_in_tabs'] = []
1092.1.59 by William Grant
Move the plugin loading/indexing logic into ivle.config.Config.
161
        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.
162
            if not hasattr(plugin, 'tabs'):
163
                continue
164
165
            for tab in plugin.tabs:
1497 by Matt Giuca
Added Users tab to drop-down menu, for admins only.
166
                # tab is a tuple: name, title, desc, icon, path, weight, admin
167
                # (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.
168
                new_app = {}
1116 by William Grant
Move the old tutorial views into the 'subjects' tab, so they get the right
169
                new_app['this_app'] = hasattr(self, 'tab') \
170
                                      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.
171
172
                # Icon name
173
                if tab[3] is not None:
174
                    new_app['has_icon'] = True
175
                    icon_url = media_url(req, plugin, tab[3])
176
                    new_app['icon_url'] = icon_url
177
                    if new_app['this_app']:
178
                        ctx['favicon'] = icon_url
179
                else:
180
                    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
181
                # The following check is here, so it is AFTER setting the
182
                # icon, but BEFORE actually installing the tab in the menu
183
                if len(tab) > 6 and tab[6]:
184
                    # Admin-only tab
185
                    if not (req.user and req.user.admin):
186
                        break
1210 by William Grant
Use Request.make_path everywhere.
187
                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.
188
                new_app['desc'] = tab[2]
189
                new_app['name'] = tab[1]
190
                new_app['weight'] = tab[5]
191
                ctx['apps_in_tabs'].append(new_app)
192
193
        ctx['apps_in_tabs'].sort(key=lambda tab: tab['weight'])
194
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
195
    def render_overlays(self, req):
196
        """Generate XML streams for the overlays.
197
        
198
        Returns a list of streams. Populates the scripts, styles, and 
199
        scripts_init.
200
        """
201
        overlays = []
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
202
        styles = []
203
        scripts = []
204
        scripts_init = []
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
205
        if not self.allow_overlays:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
206
            return (overlays, styles, scripts, scripts_init)
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
207
1092.1.59 by William Grant
Move the plugin loading/indexing logic into ivle.config.Config.
208
        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
209
            for overclass in plugin.overlays:
210
                if overclass in self.overlay_blacklist:
211
                    continue
212
                overlay = overclass(req)
213
                #TODO: Re-factor this to look nicer
214
                for mplugin in overlay.plugin_scripts:
215
                    for path in overlay.plugin_scripts[mplugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
216
                        scripts.append(media_url(req, mplugin, path))
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
217
218
                for mplugin in overlay.plugin_styles:
219
                    for path in overlay.plugin_styles[mplugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
220
                        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
221
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
222
                scripts_init += overlay.plugin_scripts_init
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
223
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
224
                overlays.append(overlay.render(req))
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
225
        return (overlays, styles, scripts, scripts_init)
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
226
1099.1.95 by William Grant
Error views are now retrieved from a class method of the view, not the plugin
227
    @classmethod
228
    def get_error_view(cls, e):
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
229
        view_map = {HTTPError:    XHTMLErrorView,
230
                    Unauthorized: XHTMLUnauthorizedView}
1099.1.95 by William Grant
Error views are now retrieved from a class method of the view, not the plugin
231
        for exccls in inspect.getmro(type(e)):
232
            if exccls in view_map:
233
                return view_map[exccls]
234
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
235
class XHTMLErrorView(XHTMLView):
236
    template = 'xhtmlerror.html'
237
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
238
    def __init__(self, req, context, lastobj):
239
        super(XHTMLErrorView, self).__init__(req, context)
240
        self.lastobj = lastobj
241
242
    def get_context_ancestry(self, req):
1294.2.118 by William Grant
Merge from object-publishing.
243
        return req.publisher.get_ancestors(self.lastobj)
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
244
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
245
    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.
246
        ctx['req'] = req
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
247
        ctx['exception'] = self.context
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
248
249
class XHTMLUnauthorizedView(XHTMLErrorView):
250
    template = 'xhtmlunauthorized.html'
251
1294.2.109 by William Grant
Fix XHTMLUnauthorizedView to take the extra breadcrumb-related arg.
252
    def __init__(self, req, exception, lastobj):
253
        super(XHTMLUnauthorizedView, self).__init__(req, exception, lastobj)
1099.1.120 by William Grant
Move the login machinery to the new framework.
254
1294.2.134 by William Grant
Use a different set of routes for public mode, too.
255
        if not req.publicmode and req.user is None:
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
256
            # 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.
257
            if req.uri == '/':
258
                query_string = ''
259
            else:
260
                query_string = '?url=' + urllib.quote(req.uri, safe="/~")
261
            req.throw_redirect('/+login' + query_string)
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
262
263
        req.status = 403
1294.2.127 by William Grant
Allow views to specify text for their breadcrumb.
264
265
class ViewBreadcrumb(object):
266
    def __init__(self, req, context):
267
        self.req = req
268
        self.context = context
269
270
    @property
271
    def text(self):
272
        return self.context.breadcrumb_text