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

« back to all changes in this revision

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

  • Committer: William Grant
  • Date: 2009-12-08 05:17:41 UTC
  • Revision ID: grantw@unimelb.edu.au-20091208051741-ge3tbktd1ebyt6p4
Use the publishing framework to generate URLs to projectsets.

Show diffs side-by-side

added added

removed removed

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