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

« back to all changes in this revision

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

Moved groups over to the new class-based xhtml templating way of being
displayed

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
 
        perms = self.get_permissions(req.user)
32
 
 
33
 
        return self.permission is None or self.permission in 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
        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)