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

« back to all changes in this revision

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

ivle.webapp.tutorial: Port www/apps/tutorial to new framework.

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
 
20
31
class BaseView(object):
21
32
    """
22
33
    Abstract base class for all view objects.
23
34
    """
24
 
    def render(self, req):
25
 
        raise NotImplementedError()
26
 
 
27
 
    def get_permissions(self, user):
28
 
        return self.context.get_permissions(user)
29
 
 
30
 
    def authorize(self, req):
31
 
        self.perms = self.get_permissions(req.user)
32
 
 
33
 
        return self.permission is None or self.permission in self.perms
 
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
        req.content_type = 'text/html' # TODO: Detect application/xhtml+xml
 
128
        ctx = genshi.template.Context()
 
129
        self.populate(req, ctx)
 
130
        self.populate_headings(req, ctx)
 
131
        
 
132
        ctx['app_styles'] = req.styles
 
133
        ctx['scripts'] = req.scripts
 
134
        ctx['scripts_init'] = req.scripts_init
 
135
        app_template = os.path.join(os.path.dirname(
 
136
                        inspect.getmodule(self).__file__), self.app_template) 
 
137
        req.write_html_head_foot = False
 
138
        loader = genshi.template.TemplateLoader(".", auto_reload=True)
 
139
        tmpl = loader.load(app_template)
 
140
        app = tmpl.generate(ctx)
 
141
        ctx['app_template'] = app
 
142
        tmpl = loader.load(os.path.join(os.path.dirname(__file__), 
 
143
                                                        'ivle-headings.html'))
 
144
        req.write(tmpl.generate(ctx).render('xhtml', doctype='xhtml'))
 
145
 
 
146
    def populate_headings(self, req, ctx):
 
147
        ctx['favicon'] = None
 
148
        ctx['root_dir'] = ivle.conf.root_dir
 
149
        ctx['public_host'] = ivle.conf.public_host
 
150
        ctx['write_javascript_settings'] = req.write_javascript_settings
 
151
        if req.user:
 
152
            ctx['login'] = req.user.login
 
153
            ctx['logged_in'] = True
 
154
            ctx['nick'] = req.user.nick
 
155
        else:
 
156
            ctx['login'] = None
 
157
        ctx['publicmode'] = req.publicmode
 
158
        ctx['apps_in_tabs'] = []
 
159
        for urlname in ivle.conf.apps.apps_in_tabs:
 
160
            new_app = {}
 
161
            app = ivle.conf.apps.app_url[urlname]
 
162
            new_app['this_app'] = urlname == self.appname
 
163
            if app.icon:
 
164
                new_app['has_icon'] = True
 
165
                icon_dir = ivle.conf.apps.app_icon_dir
 
166
                icon_url = ivle.util.make_path(os.path.join(icon_dir, app.icon))
 
167
                new_app['icon_url'] = icon_url
 
168
                if new_app['this_app']:
 
169
                    ctx['favicon'] = icon_url
 
170
            else:
 
171
                new_app['has_icon'] = False
 
172
            new_app['path'] = ivle.util.make_path(urlname)
 
173
            new_app['desc'] = app.desc
 
174
            new_app['name'] = app.name
 
175
            ctx['apps_in_tabs'].append(new_app)