~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.115 by William Grant
Don't share XHTMLView's attributes between instances. oops.
43
44
    def __init__(self, *args, **kwargs):
45
        super(XHTMLView, self).__init__(*args, **kwargs)
46
47
        self.overlay_blacklist = []
48
49
        self.plugin_scripts = {}
50
        self.plugin_styles = {}
51
        self.scripts_init = []
52
53
        self.extra_breadcrumbs = []
54
        self.overlay_blacklist = []
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
55
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
56
    def get_context_ancestry(self, req):
1294.2.118 by William Grant
Merge from object-publishing.
57
        return req.publisher.get_ancestors(self.context)
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
58
1148 by William Grant
Allow XHTMLView subclasses to apply filters to the generated app XML stream.
59
    def filter(self, stream, ctx):
60
        return stream
61
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
62
    def render(self, req):
63
        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.
64
65
        # View template
66
        viewctx = genshi.template.Context()
67
        self.populate(req, viewctx)
68
69
        # The template is found in the directory of the module containing the
70
        # view.
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
71
        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
72
                        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
73
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
74
        tmpl = loader.load(app_template)
1148 by William Grant
Allow XHTMLView subclasses to apply filters to the generated app XML stream.
75
        app = self.filter(tmpl.generate(viewctx), viewctx)
1099.1.38 by William Grant
ivle.webapp.base.xhtml.XHTMLView: Split the global and view contexts.
76
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
77
        view_scripts = []
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
78
        for plugin in self.plugin_scripts:
79
            for path in self.plugin_scripts[plugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
80
                view_scripts.append(media_url(req, plugin, path))
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
81
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
82
        view_styles = []
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
83
        for plugin in self.plugin_styles:
84
            for path in self.plugin_styles[plugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
85
                view_styles.append(media_url(req, plugin, path))
1099.1.60 by William Grant
Add support in XHTMLView for plugin styles and scripts.
86
1099.1.38 by William Grant
ivle.webapp.base.xhtml.XHTMLView: Split the global and view contexts.
87
        # Global template
88
        ctx = genshi.template.Context()
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
89
90
        overlay_bits = self.render_overlays(req) if req.user else [[]]*4
91
        ctx['overlays'] = overlay_bits[0]
1092.1.24 by William Grant
Convince the main XHTML template to use versioned styles/scripts, and move the
92
93
        ctx['styles'] = [media_url(req, CorePlugin, 'ivle.css')]
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
94
        ctx['styles'] += view_styles
95
        ctx['styles'] += overlay_bits[1]
1092.1.24 by William Grant
Convince the main XHTML template to use versioned styles/scripts, and move the
96
97
        ctx['scripts'] = [media_url(req, CorePlugin, path) for path in
1092.1.28 by William Grant
Only load tos.js on /+tos.
98
                           ('util.js', 'json2.js', 'md5.js')]
1160 by William Grant
Swap the version and +external in media URLs, and use media_url for jQuery.
99
        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.
100
        ctx['scripts'] += view_scripts
101
        ctx['scripts'] += overlay_bits[2]
1092.1.24 by William Grant
Convince the main XHTML template to use versioned styles/scripts, and move the
102
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
103
        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
104
        ctx['app_template'] = app
1100.1.9 by Matt Giuca
The IVLE logo is now an <img> element, rather than a CSS background.
105
        ctx['title_img'] = media_url(req, CorePlugin,
1294.2.74 by William Grant
Replace the title image in the root template with the breadcrumb.
106
                                     "images/chrome/root-breadcrumb.png")
1294.2.79 by William Grant
Don't crash if the ancestry can't be calculated; assume none instead.
107
        try:
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
108
            ctx['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.
109
        except NoPath:
110
            ctx['ancestry'] = []
1294.2.101 by William Grant
Allow views to specify extra breadcrumbs.
111
112
        # Allow the view to add its own fake breadcrumbs.
113
        ctx['extra_breadcrumbs'] = self.extra_breadcrumbs
114
1294.2.90 by William Grant
Produce breadcrumb menus.
115
        ctx['crumb'] = Breadcrumber(req).crumb
1099.1.38 by William Grant
ivle.webapp.base.xhtml.XHTMLView: Split the global and view contexts.
116
        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
117
        tmpl = loader.load(os.path.join(os.path.dirname(__file__), 
118
                                                        'ivle-headings.html'))
119
        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
120
        
121
    def populate(self, req, ctx):
122
        raise NotImplementedError()
1099.1.34 by William Grant
Split up ivle.webapp.base.views into ivle.webapp.base.{rest,xhtml}, as it was
123
124
    def populate_headings(self, req, ctx):
125
        ctx['favicon'] = None
1202 by William Grant
XHTMLView no longer uses ivle.conf.
126
        ctx['root_dir'] = req.config['urls']['root']
127
        ctx['public_host'] = req.config['urls']['public_host']
128
        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
129
        ctx['write_javascript_settings'] = req.write_javascript_settings
130
        if req.user:
131
            ctx['login'] = req.user.login
132
            ctx['logged_in'] = True
133
            ctx['nick'] = req.user.nick
134
        else:
135
            ctx['login'] = None
1099.1.92 by William Grant
Fix the XHTML base template to not crash when not logged in.
136
            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
137
        ctx['publicmode'] = req.publicmode
1099.1.100 by Nick Chadwick
Created a new help system.
138
        if hasattr(self, 'help'):
139
            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.
140
141
        ctx['apps_in_tabs'] = []
1092.1.59 by William Grant
Move the plugin loading/indexing logic into ivle.config.Config.
142
        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.
143
            if not hasattr(plugin, 'tabs'):
144
                continue
145
146
            for tab in plugin.tabs:
147
                # tab is a tuple: name, title, desc, icon, path
148
                new_app = {}
1116 by William Grant
Move the old tutorial views into the 'subjects' tab, so they get the right
149
                new_app['this_app'] = hasattr(self, 'tab') \
150
                                      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.
151
152
                # Icon name
153
                if tab[3] is not None:
154
                    new_app['has_icon'] = True
155
                    icon_url = media_url(req, plugin, tab[3])
156
                    new_app['icon_url'] = icon_url
157
                    if new_app['this_app']:
158
                        ctx['favicon'] = icon_url
159
                else:
160
                    new_app['has_icon'] = False
1210 by William Grant
Use Request.make_path everywhere.
161
                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.
162
                new_app['desc'] = tab[2]
163
                new_app['name'] = tab[1]
164
                new_app['weight'] = tab[5]
165
                ctx['apps_in_tabs'].append(new_app)
166
167
        ctx['apps_in_tabs'].sort(key=lambda tab: tab['weight'])
168
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
169
    def render_overlays(self, req):
170
        """Generate XML streams for the overlays.
171
        
172
        Returns a list of streams. Populates the scripts, styles, and 
173
        scripts_init.
174
        """
175
        overlays = []
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
176
        styles = []
177
        scripts = []
178
        scripts_init = []
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
179
        if not self.allow_overlays:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
180
            return (overlays, styles, scripts, scripts_init)
1099.1.129 by William Grant
Allow XHTML views to specify that they cannot have overlays.
181
1092.1.59 by William Grant
Move the plugin loading/indexing logic into ivle.config.Config.
182
        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
183
            for overclass in plugin.overlays:
184
                if overclass in self.overlay_blacklist:
185
                    continue
186
                overlay = overclass(req)
187
                #TODO: Re-factor this to look nicer
188
                for mplugin in overlay.plugin_scripts:
189
                    for path in overlay.plugin_scripts[mplugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
190
                        scripts.append(media_url(req, mplugin, path))
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
191
192
                for mplugin in overlay.plugin_styles:
193
                    for path in overlay.plugin_styles[mplugin]:
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
194
                        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
195
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
196
                scripts_init += overlay.plugin_scripts_init
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
197
1099.1.74 by Nick Chadwick
Added overlay system and console overlay. Note that the console overlay
198
                overlays.append(overlay.render(req))
1282.1.2 by William Grant
Remove use of req.{styles,scripts{,_init}} from XHTMLView.
199
        return (overlays, styles, scripts, scripts_init)
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
200
1099.1.95 by William Grant
Error views are now retrieved from a class method of the view, not the plugin
201
    @classmethod
202
    def get_error_view(cls, e):
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
203
        view_map = {HTTPError:    XHTMLErrorView,
204
                    Unauthorized: XHTMLUnauthorizedView}
1099.1.95 by William Grant
Error views are now retrieved from a class method of the view, not the plugin
205
        for exccls in inspect.getmro(type(e)):
206
            if exccls in view_map:
207
                return view_map[exccls]
208
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
209
class XHTMLErrorView(XHTMLView):
210
    template = 'xhtmlerror.html'
211
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
212
    def __init__(self, req, context, lastobj):
213
        super(XHTMLErrorView, self).__init__(req, context)
214
        self.lastobj = lastobj
215
216
    def get_context_ancestry(self, req):
1294.2.118 by William Grant
Merge from object-publishing.
217
        return req.publisher.get_ancestors(self.lastobj)
1294.2.108 by William Grant
Display breadcrumbs on error pages too.
218
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
219
    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.
220
        ctx['req'] = req
1099.1.91 by William Grant
Add support for custom error views. Plugins can now declare that errors
221
        ctx['exception'] = self.context
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
222
223
class XHTMLUnauthorizedView(XHTMLErrorView):
224
    template = 'xhtmlunauthorized.html'
225
1294.2.109 by William Grant
Fix XHTMLUnauthorizedView to take the extra breadcrumb-related arg.
226
    def __init__(self, req, exception, lastobj):
227
        super(XHTMLUnauthorizedView, self).__init__(req, exception, lastobj)
1099.1.120 by William Grant
Move the login machinery to the new framework.
228
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
229
        if req.user is None:
230
            # 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.
231
            if req.uri == '/':
232
                query_string = ''
233
            else:
234
                query_string = '?url=' + urllib.quote(req.uri, safe="/~")
235
            req.throw_redirect('/+login' + query_string)
1099.1.96 by William Grant
Add an XHTMLUnauthorizedView which redirects unauthenticated users to the
236
237
        req.status = 403