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

« back to all changes in this revision

Viewing changes to ivle/webapp/base/xhtml.py

  • Committer: William Grant
  • Date: 2010-02-15 08:48:29 UTC
  • Revision ID: grantw@unimelb.edu.au-20100215084829-m1bjpp9djl3q6kh2
Actually add the SemesterEdit template.

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
 
20
20
import inspect
21
21
import os.path
 
22
import urllib
22
23
 
23
24
import genshi.template
24
25
 
 
26
from ivle.webapp.media import media_url
 
27
from ivle.webapp.core import Plugin as CorePlugin
25
28
from ivle.webapp.base.views import BaseView
26
 
import ivle.conf
27
 
import ivle.util
 
29
from ivle.webapp.base.plugins import ViewPlugin, OverlayPlugin
 
30
from ivle.webapp.errors import HTTPError, Unauthorized
 
31
from ivle.webapp.publisher import NoPath
 
32
from ivle.webapp.breadcrumbs import Breadcrumber
28
33
 
29
34
class XHTMLView(BaseView):
30
35
    """
32
37
    It is expected that apps which use this view will be written using Genshi
33
38
    templates.
34
39
    """
35
 
    def __init__(self, req, **kwargs):
36
 
        for key in kwargs:
37
 
            setattr(self, key, kwargs[key])
 
40
 
 
41
    template = 'template.html'
 
42
    allow_overlays = True
 
43
    breadcrumb_text = None
 
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 = []
 
56
 
 
57
    def get_context_ancestry(self, req):
 
58
        return req.publisher.get_ancestors(self.context)
 
59
 
 
60
    def filter(self, stream, ctx):
 
61
        return stream
38
62
 
39
63
    def render(self, req):
40
64
        req.content_type = 'text/html' # TODO: Detect application/xhtml+xml
47
71
        # view.
48
72
        app_template = os.path.join(os.path.dirname(
49
73
                        inspect.getmodule(self).__file__), self.template) 
50
 
        req.write_html_head_foot = False
51
74
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
52
75
        tmpl = loader.load(app_template)
53
 
        app = tmpl.generate(viewctx)
 
76
        app = self.filter(tmpl.generate(viewctx), viewctx)
 
77
 
 
78
        view_scripts = []
 
79
        for plugin in self.plugin_scripts:
 
80
            for path in self.plugin_scripts[plugin]:
 
81
                view_scripts.append(media_url(req, plugin, path))
 
82
 
 
83
        view_styles = []
 
84
        for plugin in self.plugin_styles:
 
85
            for path in self.plugin_styles[plugin]:
 
86
                view_styles.append(media_url(req, plugin, path))
54
87
 
55
88
        # Global template
56
89
        ctx = genshi.template.Context()
57
 
        ctx['app_styles'] = req.styles
58
 
        ctx['scripts'] = req.scripts
59
 
        ctx['scripts_init'] = req.scripts_init
 
90
 
 
91
        overlay_bits = self.render_overlays(req) if req.user else [[]]*4
 
92
        ctx['overlays'] = overlay_bits[0]
 
93
 
 
94
        ctx['styles'] = [media_url(req, CorePlugin, 'ivle.css')]
 
95
        ctx['styles'] += view_styles
 
96
        ctx['styles'] += overlay_bits[1]
 
97
 
 
98
        ctx['scripts'] = [media_url(req, CorePlugin, path) for path in
 
99
                           ('util.js', 'json2.js', 'md5.js')]
 
100
        ctx['scripts'].append(media_url(req, '+external/jquery', 'jquery.js'))
 
101
        ctx['scripts'] += view_scripts
 
102
        ctx['scripts'] += overlay_bits[2]
 
103
 
 
104
        ctx['scripts_init'] = self.scripts_init + overlay_bits[3]
60
105
        ctx['app_template'] = app
 
106
        ctx['title_img'] = media_url(req, CorePlugin,
 
107
                                     "images/chrome/root-breadcrumb.png")
 
108
        try:
 
109
            ancestry = self.get_context_ancestry(req)
 
110
        except NoPath:
 
111
            ancestry = []
 
112
 
 
113
        crumber = Breadcrumber(req)
 
114
 
 
115
        ctx['breadcrumbs'] = []
 
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)
 
134
 
61
135
        self.populate_headings(req, ctx)
62
136
        tmpl = loader.load(os.path.join(os.path.dirname(__file__), 
63
137
                                                        'ivle-headings.html'))
64
138
        req.write(tmpl.generate(ctx).render('xhtml', doctype='xhtml'))
 
139
        
 
140
    def populate(self, req, ctx):
 
141
        raise NotImplementedError()
65
142
 
66
143
    def populate_headings(self, req, ctx):
67
144
        ctx['favicon'] = None
68
 
        ctx['root_dir'] = ivle.conf.root_dir
69
 
        ctx['public_host'] = ivle.conf.public_host
 
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']
70
148
        ctx['write_javascript_settings'] = req.write_javascript_settings
71
149
        if req.user:
72
150
            ctx['login'] = req.user.login
74
152
            ctx['nick'] = req.user.nick
75
153
        else:
76
154
            ctx['login'] = None
 
155
            ctx['logged_in'] = False
77
156
        ctx['publicmode'] = req.publicmode
 
157
        if hasattr(self, 'help'):
 
158
            ctx['help_path'] = self.help
 
159
 
78
160
        ctx['apps_in_tabs'] = []
79
 
        for urlname in ivle.conf.apps.apps_in_tabs:
80
 
            new_app = {}
81
 
            app = ivle.conf.apps.app_url[urlname]
82
 
            new_app['this_app'] = hasattr(self, 'appname') \
83
 
                                  and urlname == self.appname
84
 
            if app.icon:
85
 
                new_app['has_icon'] = True
86
 
                icon_dir = ivle.conf.apps.app_icon_dir
87
 
                icon_url = ivle.util.make_path(os.path.join(icon_dir, app.icon))
88
 
                new_app['icon_url'] = icon_url
89
 
                if new_app['this_app']:
90
 
                    ctx['favicon'] = icon_url
 
161
        for plugin in req.config.plugin_index[ViewPlugin]:
 
162
            if not hasattr(plugin, 'tabs'):
 
163
                continue
 
164
 
 
165
            for tab in plugin.tabs:
 
166
                # tab is a tuple: name, title, desc, icon, path, weight, admin
 
167
                # (Admin is optional, defaults to false)
 
168
                new_app = {}
 
169
                new_app['this_app'] = hasattr(self, 'tab') \
 
170
                                      and tab[0] == self.tab
 
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
 
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
 
187
                new_app['path'] = req.make_path(tab[4])
 
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
 
 
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 = []
 
202
        styles = []
 
203
        scripts = []
 
204
        scripts_init = []
 
205
        if not self.allow_overlays:
 
206
            return (overlays, styles, scripts, scripts_init)
 
207
 
 
208
        for plugin in req.config.plugin_index[OverlayPlugin]:
 
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]:
 
216
                        scripts.append(media_url(req, mplugin, path))
 
217
 
 
218
                for mplugin in overlay.plugin_styles:
 
219
                    for path in overlay.plugin_styles[mplugin]:
 
220
                        styles.append(media_url(req, mplugin, path))
 
221
 
 
222
                scripts_init += overlay.plugin_scripts_init
 
223
 
 
224
                overlays.append(overlay.render(req))
 
225
        return (overlays, styles, scripts, scripts_init)
 
226
 
 
227
    @classmethod
 
228
    def get_error_view(cls, e):
 
229
        view_map = {HTTPError:    XHTMLErrorView,
 
230
                    Unauthorized: XHTMLUnauthorizedView}
 
231
        for exccls in inspect.getmro(type(e)):
 
232
            if exccls in view_map:
 
233
                return view_map[exccls]
 
234
 
 
235
class XHTMLErrorView(XHTMLView):
 
236
    template = 'xhtmlerror.html'
 
237
 
 
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):
 
243
        return req.publisher.get_ancestors(self.lastobj)
 
244
 
 
245
    def populate(self, req, ctx):
 
246
        ctx['req'] = req
 
247
        ctx['exception'] = self.context
 
248
 
 
249
class XHTMLUnauthorizedView(XHTMLErrorView):
 
250
    template = 'xhtmlunauthorized.html'
 
251
 
 
252
    def __init__(self, req, exception, lastobj):
 
253
        super(XHTMLUnauthorizedView, self).__init__(req, exception, lastobj)
 
254
 
 
255
        if not req.publicmode and req.user is None:
 
256
            # Not logged in. Redirect to login page.
 
257
            if req.uri == '/':
 
258
                query_string = ''
91
259
            else:
92
 
                new_app['has_icon'] = False
93
 
            new_app['path'] = ivle.util.make_path(urlname)
94
 
            new_app['desc'] = app.desc
95
 
            new_app['name'] = app.name
96
 
            ctx['apps_in_tabs'].append(new_app)
 
260
                query_string = '?url=' + urllib.quote(req.uri, safe="/~")
 
261
            req.throw_redirect('/+login' + query_string)
 
262
 
 
263
        req.status = 403
 
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