20
20
'''Media file support for the framework.'''
27
28
from ivle.webapp.base.views import BaseView
28
from ivle.webapp.base.plugins import ViewPlugin, MediaPlugin
29
from ivle.webapp.base.plugins import PublicViewPlugin, ViewPlugin, MediaPlugin
29
30
from ivle.webapp.errors import NotFound, Forbidden
31
32
def media_url(req, plugin, path):
32
33
'''Generates a URL to a media file.
34
35
Plugin can be a string, in which case it is put into the path literally,
35
or a plugin object, in which case its name is looked up.'''
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
36
41
if not isinstance(plugin, basestring):
37
plugin = req.reverse_plugins[plugin]
39
return os.path.join(ivle.conf.root_dir, '+media', plugin, path)
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))
41
49
class BaseMediaFileView(BaseView):
42
50
'''A view for media files.
106
114
def get_permissions(self, user):
109
class Plugin(ViewPlugin):
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):
199
('+media/+:version/+external/:ns/*path', ExternalVersionedMediaFileView),
200
('+media/+external/:ns/*path', ExternalMediaFileView),
201
('+media/+:version/:ns/*path', VersionedMediaFileView),
111
202
('+media/:ns/*path', MediaFileView),