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