Commit 35eb5a16 by David Ormsbee

Merge pull request #11413 from edx/ormsbee/course_image_cdn

Allow course images to be served from the CDN.
parents 3f58954f 1a5b9172
...@@ -3,6 +3,7 @@ Declaration of CourseOverview model ...@@ -3,6 +3,7 @@ Declaration of CourseOverview model
""" """
import json import json
import logging import logging
from urlparse import urlunparse
from django.db import models, transaction from django.db import models, transaction
from django.db.models.fields import BooleanField, DateTimeField, DecimalField, TextField, FloatField, IntegerField from django.db.models.fields import BooleanField, DateTimeField, DecimalField, TextField, FloatField, IntegerField
...@@ -17,6 +18,7 @@ from opaque_keys.edx.keys import CourseKey ...@@ -17,6 +18,7 @@ from opaque_keys.edx.keys import CourseKey
from config_models.models import ConfigurationModel from config_models.models import ConfigurationModel
from lms.djangoapps import django_comment_client from lms.djangoapps import django_comment_client
from openedx.core.djangoapps.models.course_details import CourseDetails from openedx.core.djangoapps.models.course_details import CourseDetails
from static_replace.models import AssetBaseUrlConfig
from util.date_utils import strftime_localized from util.date_utils import strftime_localized
from xmodule import course_metadata_utils from xmodule import course_metadata_utils
from xmodule.course_module import CourseDescriptor, DEFAULT_START_DATE from xmodule.course_module import CourseDescriptor, DEFAULT_START_DATE
...@@ -549,7 +551,28 @@ class CourseOverview(TimeStampedModel): ...@@ -549,7 +551,28 @@ class CourseOverview(TimeStampedModel):
urls['small'] = self.image_set.small_url or raw_image_url urls['small'] = self.image_set.small_url or raw_image_url
urls['large'] = self.image_set.large_url or raw_image_url urls['large'] = self.image_set.large_url or raw_image_url
return urls return self._apply_cdn(urls)
def _apply_cdn(self, image_urls):
"""
Given a dict of resolutions -> urls, return a copy with CDN applied.
If CDN does not exist or is disabled, just returns the original. The
URLs that we store in CourseOverviewImageSet are all already top level
paths, so we don't need to go through the /static remapping magic that
happens with other course assets. We just need to add the CDN server if
appropriate.
"""
cdn_config = AssetBaseUrlConfig.current()
if not cdn_config.enabled:
return image_urls
base_url = cdn_config.base_url
return {
resolution: urlunparse((None, base_url, url, None, None, None))
for resolution, url in image_urls.items()
}
def __unicode__(self): def __unicode__(self):
"""Represent ourselves with the course key.""" """Represent ourselves with the course key."""
......
...@@ -17,6 +17,7 @@ from PIL import Image ...@@ -17,6 +17,7 @@ from PIL import Image
from lms.djangoapps.certificates.api import get_active_web_certificate from lms.djangoapps.certificates.api import get_active_web_certificate
from openedx.core.djangoapps.models.course_details import CourseDetails from openedx.core.djangoapps.models.course_details import CourseDetails
from openedx.core.lib.courses import course_image_url from openedx.core.lib.courses import course_image_url
from static_replace.models import AssetBaseUrlConfig
from xmodule.assetstore.assetmgr import AssetManager from xmodule.assetstore.assetmgr import AssetManager
from xmodule.contentstore.django import contentstore from xmodule.contentstore.django import contentstore
from xmodule.contentstore.content import StaticContent from xmodule.contentstore.content import StaticContent
...@@ -644,6 +645,32 @@ class CourseOverviewImageSetTestCase(ModuleStoreTestCase): ...@@ -644,6 +645,32 @@ class CourseOverviewImageSetTestCase(ModuleStoreTestCase):
) )
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split) @ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_cdn(self, modulestore_type):
"""
Test that we return CDN prefixed URLs if it is enabled.
"""
with self.store.default_store(modulestore_type):
course = CourseFactory.create(default_store=modulestore_type)
overview = CourseOverview.get_from_id(course.id)
# First the behavior when there's no CDN enabled...
AssetBaseUrlConfig.objects.all().delete()
if modulestore_type == ModuleStoreEnum.Type.mongo:
expected_path_start = "/c4x/"
elif modulestore_type == ModuleStoreEnum.Type.split:
expected_path_start = "/asset-v1:"
for url in overview.image_urls.values():
self.assertTrue(url.startswith(expected_path_start))
# Now enable the CDN...
AssetBaseUrlConfig.objects.create(enabled=True, base_url='fakecdn.edx.org')
expected_cdn_url = "//fakecdn.edx.org" + expected_path_start
for url in overview.image_urls.values():
self.assertTrue(url.startswith(expected_cdn_url))
@ddt.data(ModuleStoreEnum.Type.mongo, ModuleStoreEnum.Type.split)
def test_error_generating_thumbnails(self, modulestore_type): def test_error_generating_thumbnails(self, modulestore_type):
""" """
Test a scenario where thumbnails cannot be generated. Test a scenario where thumbnails cannot be generated.
......
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