Commit 78370f36 by Toby Lawrence

Merge pull request #11400 from edx/PERF-258

Add the ability to exclude files from URL canonicalization.
parents dffbc69a e57d1a20
......@@ -5,7 +5,7 @@ from django.contrib.staticfiles.storage import staticfiles_storage
from django.contrib.staticfiles import finders
from django.conf import settings
from static_replace.models import AssetBaseUrlConfig
from static_replace.models import AssetBaseUrlConfig, AssetExcludedExtensionsConfig
from xmodule.modulestore.django import modulestore
from xmodule.modulestore import ModuleStoreEnum
from xmodule.contentstore.content import StaticContent
......@@ -182,7 +182,8 @@ def replace_static_urls(text, data_directory=None, course_id=None, static_asset_
# if not, then assume it's courseware specific content and then look in the
# Mongo-backed database
base_url = AssetBaseUrlConfig.get_base_url()
url = StaticContent.get_canonicalized_asset_path(course_id, rest, base_url)
excluded_exts = AssetExcludedExtensionsConfig.get_excluded_extensions()
url = StaticContent.get_canonicalized_asset_path(course_id, rest, base_url, excluded_exts)
if AssetLocator.CANONICAL_NAMESPACE in url:
url = url.replace('block@', 'block/', 1)
......
# -*- coding: utf-8 -*-
from __future__ import unicode_literals
from django.db import migrations, models
import django.db.models.deletion
from django.conf import settings
class Migration(migrations.Migration):
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
('static_replace', '0001_initial'),
]
operations = [
migrations.CreateModel(
name='AssetExcludedExtensionsConfig',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('change_date', models.DateTimeField(auto_now_add=True, verbose_name='Change date')),
('enabled', models.BooleanField(default=False, verbose_name='Enabled')),
('excluded_extensions', models.TextField(default=b'html', help_text=b'The file extensions to exclude from canonicalization. No leading period required. Values should be space separated i.e. "html svg css"')),
('changed_by', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, editable=False, to=settings.AUTH_USER_MODEL, null=True, verbose_name='Changed by')),
],
),
]
......@@ -27,3 +27,28 @@ class AssetBaseUrlConfig(ConfigurationModel):
def __unicode__(self):
return unicode(repr(self))
class AssetExcludedExtensionsConfig(ConfigurationModel):
"""Configuration for the the excluded file extensions when canonicalizing static asset paths."""
class Meta(object):
app_label = 'static_replace'
excluded_extensions = TextField(
default='html',
help_text='The file extensions to exclude from canonicalization. No leading period required. ' +
'Values should be space separated i.e. "html svg css"'
)
@classmethod
def get_excluded_extensions(cls):
"""Gets the excluded file extensions when canonicalizing static asset paths"""
add_period = lambda x: '.' + x
return map(add_period, cls.current().excluded_extensions.split())
def __repr__(self):
return '<AssetExcludedExtensionsConfig(extensions={})>'.format(self.get_excluded_extensions().split())
def __unicode__(self):
return unicode(repr(self))
......@@ -168,7 +168,7 @@ class StaticContent(object):
return StaticContent.compute_location(course_key, path)
@staticmethod
def get_canonicalized_asset_path(course_key, path, base_url):
def get_canonicalized_asset_path(course_key, path, base_url, excluded_exts):
"""
Returns a fully-qualified path to a piece of static content.
......@@ -199,17 +199,22 @@ class StaticContent(object):
# If we can't find the item, just treat it as if it's locked.
serve_from_cdn = False
# See if this is an allowed file extension to serve. Some files aren't served through the
# CDN in order to avoid same-origin policy/CORS-related issues.
if any(relative_path.lower().endswith(excluded_ext.lower()) for excluded_ext in excluded_exts):
serve_from_cdn = False
# Update any query parameter values that have asset paths in them. This is for assets that
# require their own after-the-fact values, like a Flash file that needs the path of a config
# file passed to it e.g. /static/visualization.swf?configFile=/static/visualization.xml
query_params = parse_qsl(query_string)
updated_query_params = []
for query_name, query_value in query_params:
if query_value.startswith("/static/"):
new_query_value = StaticContent.get_canonicalized_asset_path(course_key, query_value, base_url)
updated_query_params.append((query_name, new_query_value))
for query_name, query_val in query_params:
if query_val.startswith("/static/"):
new_val = StaticContent.get_canonicalized_asset_path(course_key, query_val, base_url, excluded_exts)
updated_query_params.append((query_name, new_val))
else:
updated_query_params.append((query_name, query_value))
updated_query_params.append((query_name, query_val))
serialized_asset_key = StaticContent.serialize_asset_key_with_slash(asset_key)
base_url = base_url if serve_from_cdn else ''
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment