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

« back to all changes in this revision

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

  • Committer: me at id
  • Date: 2009-01-14 23:23:12 UTC
  • mto: This revision was merged to the branch mainline in revision 1090.
  • Revision ID: svn-v3-trunk0:2b9c9e99-6f39-0410-b283-7f802c844ae2:branches%2Fstorm:1138
ivle.auth.autherror: Remove, moving AuthError into ivle.auth itself.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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: Matt Giuca, Will Grant
19
 
 
20
 
import inspect
21
 
import cgi
22
 
import os.path
23
 
 
24
 
import cjson
25
 
import genshi.template
26
 
 
27
 
import ivle.conf
28
 
import ivle.util
29
 
from ivle.webapp.errors import BadRequest, MethodNotAllowed
30
 
 
31
 
class BaseView(object):
32
 
    """
33
 
    Abstract base class for all view objects.
34
 
    """
35
 
    def __init__(self, req, **kwargs):
36
 
        pass
37
 
    def render(self, req):
38
 
        pass
39
 
 
40
 
class RESTView(BaseView):
41
 
    """
42
 
    A view which provides a RESTful interface. The content type is
43
 
    unspecified (see JSONRESTView for a specific content type).
44
 
    """
45
 
    content_type = "application/octet-stream"
46
 
 
47
 
    def __init__(self, req, *args, **kwargs):
48
 
        pass
49
 
 
50
 
    def render(self, req):
51
 
        if req.method == 'GET':
52
 
            outstr = self.GET(req)
53
 
        # XXX PATCH hack
54
 
        if req.method == 'PUT':
55
 
            outstr = self.PATCH(req, req.read())
56
 
        req.content_type = self.content_type
57
 
        req.write(outstr)
58
 
 
59
 
class JSONRESTView(RESTView):
60
 
    """
61
 
    A special case of RESTView which deals entirely in JSON.
62
 
    """
63
 
    content_type = "application/json"
64
 
 
65
 
    def render(self, req):
66
 
        if req.method == 'GET':
67
 
            outjson = self.GET(req)
68
 
        # Since PATCH isn't yet an official HTTP method, we allow users to
69
 
        # turn a PUT into a PATCH by supplying a special header.
70
 
        elif req.method == 'PATCH' or (req.method == 'PUT' and
71
 
              'X-IVLE-Patch-Semantics' in req.headers_in and
72
 
              req.headers_in['X-IVLE-Patch-Semantics'].lower() == 'yes'):
73
 
            outjson = self.PATCH(req, cjson.decode(req.read()))
74
 
        elif req.method == 'PUT':
75
 
            outjson = self.PUT(req, cjson.decode(req.read()))
76
 
        # POST implies named operation.
77
 
        elif req.method == 'POST':
78
 
            # TODO: Check Content-Type and implement multipart/form-data.
79
 
            opargs = dict(cgi.parse_qsl(req.read()))
80
 
            try:
81
 
                opname = opargs['ivle.op']
82
 
                del opargs['ivle.op']
83
 
            except KeyError:
84
 
                raise BadRequest('No named operation specified.')
85
 
 
86
 
            try:
87
 
                op = getattr(self, opname)
88
 
            except AttributeError:
89
 
                raise BadRequest('Invalid named operation.')
90
 
 
91
 
            if not hasattr(op, '_rest_api_callable') or \
92
 
               not op._rest_api_callable:
93
 
                raise BadRequest('Invalid named operation.')
94
 
 
95
 
            # Find any missing arguments, except for the first one (self).
96
 
            argspec = inspect.getargspec(op)
97
 
            missing = frozenset(argspec[0][1:]) - frozenset(opargs.keys())
98
 
            if missing:
99
 
                raise BadRequest('Missing arguments: ' + ', '.join(missing))
100
 
 
101
 
            outjson = op(**opargs)
102
 
        else:
103
 
            raise MethodNotAllowed()
104
 
 
105
 
        req.content_type = self.content_type
106
 
        if outjson is not None:
107
 
            req.write(cjson.encode(outjson))
108
 
            req.write("\n")
109
 
 
110
 
def named_operation(meth):
111
 
    '''Declare a function to be accessible to HTTP users via the REST API.
112
 
    '''
113
 
    meth._rest_api_callable = True
114
 
    return meth
115
 
 
116
 
class XHTMLView(BaseView):
117
 
    """
118
 
    A view which provides a base class for views which need to return XHTML
119
 
    It is expected that apps which use this view will be written using Genshi
120
 
    templates.
121
 
    """
122
 
    def __init__(self, req, **kwargs):
123
 
        for key in kwargs:
124
 
          setattr(self, key, kwargs[key])
125
 
        
126
 
    def render(self, req):
127
 
        ctx = genshi.template.Context()
128
 
        self.populate(req, ctx)
129
 
        self.populate_headings(req, ctx)
130
 
        
131
 
        ctx['app_styles'] = req.styles
132
 
        ctx['scripts'] = req.scripts
133
 
        ctx['scripts_init'] = req.scripts_init
134
 
        app_template = os.path.join(os.path.dirname(
135
 
                        inspect.getmodule(self).__file__), self.app_template) 
136
 
        req.write_html_head_foot = False
137
 
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
138
 
        tmpl = loader.load(app_template)
139
 
        app = tmpl.generate(ctx)
140
 
        ctx['app_template'] = app
141
 
        tmpl = loader.load(os.path.join(os.path.dirname(__file__), 
142
 
                                                        'ivle-headings.html'))
143
 
        req.write(tmpl.generate(ctx).render('xhtml', doctype='xhtml'))
144
 
 
145
 
    def populate_headings(self, req, ctx):
146
 
        ctx['favicon'] = None
147
 
        ctx['root_dir'] = ivle.conf.root_dir
148
 
        ctx['public_host'] = ivle.conf.public_host
149
 
        ctx['write_javascript_settings'] = req.write_javascript_settings
150
 
        if req.user:
151
 
            ctx['login'] = req.user.login
152
 
            ctx['logged_in'] = True
153
 
            ctx['nick'] = req.user.nick
154
 
        else:
155
 
            ctx['login'] = None
156
 
        ctx['publicmode'] = req.publicmode
157
 
        ctx['apps_in_tabs'] = []
158
 
        for urlname in ivle.conf.apps.apps_in_tabs:
159
 
            new_app = {}
160
 
            app = ivle.conf.apps.app_url[urlname]
161
 
            new_app['this_app'] = urlname == self.appname
162
 
            if app.icon:
163
 
                new_app['has_icon'] = True
164
 
                icon_dir = ivle.conf.apps.app_icon_dir
165
 
                icon_url = ivle.util.make_path(os.path.join(icon_dir, app.icon))
166
 
                new_app['icon_url'] = icon_url
167
 
                if new_app['this_app']:
168
 
                    ctx['favicon'] = icon_url
169
 
            else:
170
 
                new_app['has_icon'] = False
171
 
            new_app['path'] = ivle.util.make_path(urlname)
172
 
            new_app['desc'] = app.desc
173
 
            new_app['name'] = app.name
174
 
            ctx['apps_in_tabs'].append(new_app)