20
20
'''Media file support for the framework.'''
28
27
from ivle.webapp.base.views import BaseView
29
from ivle.webapp.base.plugins import PublicViewPlugin, ViewPlugin, MediaPlugin
28
from ivle.webapp.base.plugins import ViewPlugin
30
29
from ivle.webapp.errors import NotFound, Forbidden
32
31
def media_url(req, plugin, path):
33
32
'''Generates a URL to a media file.
35
34
Plugin can be a string, in which case it is put into the path literally,
36
or a plugin object, in which case its name is looked up.
38
If a version is specified in the IVLE configuration, a versioned URL will
35
or a plugin object, in which case its name is looked up.'''
41
36
if not isinstance(plugin, basestring):
42
plugin = req.config.reverse_plugins[plugin]
44
media_path = os.path.join('+media', '+' + req.config['media']['version']) \
45
if req.config['media']['version'] else '+media'
47
return req.make_path(os.path.join(media_path, plugin, path))
49
class BaseMediaFileView(BaseView):
37
plugin = req.reverse_plugins[plugin]
39
return os.path.join(ivle.conf.root_dir, '+media', plugin, path)
41
class MediaFileView(BaseView):
50
42
'''A view for media files.
52
44
This serves static files from directories registered by plugins.
84
88
req.content_type = type
85
89
req.sendfile(filename)
88
class MediaFileView(BaseMediaFileView):
89
'''A view for media files.
91
This serves static files from directories registered by plugins.
93
Plugins wishing to export media should declare a 'media' attribute,
94
pointing to the directory to serve (relative to the module's directory).
95
The contents of that directory will then be available under
96
/+media/python.path.to.module.
100
def _make_filename(self, req):
102
plugin = req.config.plugins[self.ns]
106
if not issubclass(plugin, MediaPlugin):
109
mediadir = plugin.media
110
plugindir = os.path.dirname(inspect.getmodule(plugin).__file__)
112
return os.path.join(plugindir, mediadir, self.path)
114
def get_permissions(self, user):
117
class VersionedMediaFileView(MediaFileView):
118
'''A view for versioned media files, with aggressive caching.
120
This serves static media files with a version string, and requests that
121
browsers cache them for a long time.
124
def __init__(self, req, ns, path, version):
125
super(VersionedMediaFileView, self).__init__(req, ns, path)
126
self.version = version
128
def _make_filename(self, req):
129
if self.version != req.config['media']['version']:
132
# Don't expire for a year.
133
req.headers_out['Expires'] = email.utils.formatdate(
134
timeval=time.time() + (60*60*24*365),
137
return super(VersionedMediaFileView, self)._make_filename(req)
140
# This maps a media namespace to an external dependency directory (in this
141
# case specified by the configuration option media/externals/jquery) and a
142
# list of permitted subpaths.
143
EXTERNAL_MEDIA_MAP = {'jquery': ('jquery', ['jquery.js'])}
145
class ExternalMediaFileView(BaseMediaFileView):
146
'''A view for media files from external dependencies.
148
This serves specific static files from external dependencies as defined in
149
the IVLE configuration.
153
def _make_filename(self, req):
155
extern = EXTERNAL_MEDIA_MAP[self.ns]
159
# Unless it's a whitelisted path, we don't want to hear about it.
160
if self.path not in extern[1]:
163
# Grab the admin-configured path for this particular external dep.
164
externdir = req.config['media']['externals'][extern[0]]
166
assert isinstance(externdir, basestring)
168
return os.path.join(externdir, self.path)
170
def get_permissions(self, user):
173
class ExternalVersionedMediaFileView(ExternalMediaFileView):
174
'''A view for versioned media files from external dependencies, with
177
This serves specific static media files from external dependencies with a
178
version string, and requests that browsers cache them for a long time.
181
def __init__(self, req, ns, path, version):
182
super(ExternalVersionedMediaFileView, self).__init__(req, ns, path)
183
self.version = version
185
def _make_filename(self, req):
186
if self.version != req.config['media']['version']:
189
# Don't expire for a year.
190
req.headers_out['Expires'] = email.utils.formatdate(
191
timeval=time.time() + (60*60*24*365),
194
return super(ExternalVersionedMediaFileView, self)._make_filename(req)
197
class Plugin(ViewPlugin, PublicViewPlugin):
91
class Plugin(ViewPlugin):
199
('+media/+:version/+external/:ns/*path', ExternalVersionedMediaFileView),
200
('+media/+external/:ns/*path', ExternalMediaFileView),
201
('+media/+:version/:ns/*path', VersionedMediaFileView),
202
93
('+media/:ns/*path', MediaFileView),