Commit 23f02d94 by Daniel Friedman Committed by Andy Armstrong

Configurations for filesystem and s3 backends.

TNL-1789
parent 44c78c60
......@@ -40,8 +40,8 @@ from lms.envs.common import (
# The following PROFILE_IMAGE_* settings are included as they are
# indirectly accessed through the email opt-in API, which is
# technically accessible through the CMS via legacy URLs.
PROFILE_IMAGE_BACKEND, PROFILE_IMAGE_DOMAIN, PROFILE_IMAGE_URL_PATH, PROFILE_IMAGE_DEFAULT_FILENAME,
PROFILE_IMAGE_DEFAULT_FILE_EXTENSION, PROFILE_IMAGE_SECRET_KEY,
PROFILE_IMAGE_BACKEND, PROFILE_IMAGE_DEFAULT_FILENAME, PROFILE_IMAGE_DEFAULT_FILE_EXTENSION,
PROFILE_IMAGE_SECRET_KEY,
)
from path import path
from warnings import simplefilter
......
......@@ -131,6 +131,10 @@ if STATIC_URL_BASE:
if not STATIC_URL.endswith("/"):
STATIC_URL += "/"
# MEDIA_ROOT specifies the directory where user-uploaded files are stored.
MEDIA_ROOT = ENV_TOKENS.get('MEDIA_ROOT', MEDIA_ROOT)
MEDIA_URL = ENV_TOKENS.get('MEDIA_URL', MEDIA_URL)
PLATFORM_NAME = ENV_TOKENS.get('PLATFORM_NAME', PLATFORM_NAME)
# For displaying on the receipt. At Stanford PLATFORM_NAME != MERCHANT_NAME, but PLATFORM_NAME is a fine default
PLATFORM_TWITTER_ACCOUNT = ENV_TOKENS.get('PLATFORM_TWITTER_ACCOUNT', PLATFORM_TWITTER_ACCOUNT)
......@@ -594,3 +598,7 @@ if FEATURES.get('INDIVIDUAL_DUE_DATES'):
FIELD_OVERRIDE_PROVIDERS += (
'courseware.student_field_overrides.IndividualStudentOverrideProvider',
)
# PROFILE IMAGE CONFIG
PROFILE_IMAGE_BACKEND = ENV_TOKENS.get('PROFILE_IMAGE_BACKEND', PROFILE_IMAGE_BACKEND)
......@@ -780,6 +780,10 @@ STATICFILES_DIRS = [
FAVICON_PATH = 'images/favicon.ico'
# User-uploaded content
MEDIA_ROOT = '/edx/var/edxapp/media/'
MEDIA_URL = '/media/'
# Locale/Internationalization
TIME_ZONE = 'America/New_York' # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name
LANGUAGE_CODE = 'en' # http://www.i18nguy.com/unicode/language-identifiers.html
......@@ -2258,20 +2262,19 @@ CHECKPOINT_PATTERN = r'(?P<checkpoint_name>\w+)'
FIELD_OVERRIDE_PROVIDERS = ()
# PROFILE IMAGE CONFIG
# TODO: add these settings to aws.py as well
# WARNING: Certain django storage backends do not support atomic
# file overwrites (including the default, specified below) - instead
# file overwrites (including the default, OverwriteStorage) - instead
# there are separate calls to delete and then write a new file in the
# storage backend. This introduces the risk of a race condition
# occurring when a user uploads a new profile image to replace an
# earlier one (the file will temporarily be deleted).
PROFILE_IMAGE_BACKEND = 'storages.backends.overwrite.OverwriteStorage'
# PROFILE_IMAGE_DOMAIN points to the domain from which we serve image
# files from. When this is '/', it refers to the same domain as the
# app server. If serving from a different domain, specify that here
# i.e. 'http://www.example-image-server.com/'
PROFILE_IMAGE_DOMAIN = '/'
PROFILE_IMAGE_URL_PATH = 'media/profile_images/'
PROFILE_IMAGE_BACKEND = {
'class': 'storages.backends.overwrite.OverwriteStorage',
'options': {
'location': os.path.join(MEDIA_ROOT, 'profile-images/'),
'base_url': os.path.join(MEDIA_URL, 'profile-images/'),
},
}
PROFILE_IMAGE_DEFAULT_FILENAME = (
'images/edx-theme/default-profile' if FEATURES['IS_EDX_DOMAIN'] else 'images/default-theme/default-profile'
)
......
......@@ -478,9 +478,13 @@ MIDDLEWARE_CLASSES += ('ccx.overrides.CcxMiddleware',)
FEATURES['CUSTOM_COURSES_EDX'] = True
# Set dummy values for profile image settings.
PROFILE_IMAGE_BACKEND = 'django.core.files.storage.FileSystemStorage'
PROFILE_IMAGE_DOMAIN = 'http://example-storage.com/'
PROFILE_IMAGE_URL_PATH = 'profile_images/'
PROFILE_IMAGE_BACKEND = {
'class': 'storages.backends.overwrite.OverwriteStorage',
'options': {
'location': MEDIA_ROOT,
'base_url': 'http://example-storage.com/profile-images/',
},
}
PROFILE_IMAGE_DEFAULT_FILENAME = 'default'
PROFILE_IMAGE_DEFAULT_FILE_EXTENSION = 'png'
PROFILE_IMAGE_SECRET_KEY = 'secret'
......
......@@ -639,7 +639,8 @@ urlpatterns = patterns(*urlpatterns)
if settings.DEBUG:
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
urlpatterns += static(
settings.PROFILE_IMAGE_DOMAIN + settings.PROFILE_IMAGE_URL_PATH, document_root=settings.MEDIA_ROOT
settings.PROFILE_IMAGE_BACKEND['options']['base_url'],
document_root=settings.PROFILE_IMAGE_BACKEND['options']['location']
)
# in debug mode, allow any template to be rendered (most useful for UX reference templates)
......
......@@ -27,8 +27,9 @@ def get_profile_image_storage():
Configures and returns a django Storage instance that can be used
to physically locate, read and write profile images.
"""
storage_class = get_storage_class(settings.PROFILE_IMAGE_BACKEND)
return storage_class(base_url=(settings.PROFILE_IMAGE_DOMAIN + settings.PROFILE_IMAGE_URL_PATH))
config = settings.PROFILE_IMAGE_BACKEND
storage_class = get_storage_class(config['class'])
return storage_class(**config['options'])
def _make_profile_image_name(username):
......@@ -75,7 +76,7 @@ def get_profile_image_urls_for_user(user):
callers will use `_get_default_profile_image_urls` instead to provide
a set of urls that point to placeholder images, when there are no user-
submitted images.
- based on the value of django.conf.settings.PROFILE_IMAGE_DOMAIN,
- based on the value of django.conf.settings.PROFILE_IMAGE_BACKEND,
the URL may be relative, and in that case the caller is responsible for
constructing the full URL if needed.
......
......@@ -34,7 +34,7 @@ class ProfileImageUrlTestCase(TestCase):
"""
self.assertEqual(
actual_url,
'http://example-storage.com/profile_images/{name}_{size}.jpg'.format(
'http://example-storage.com/profile-images/{name}_{size}.jpg'.format(
name=expected_name, size=expected_pixels
)
)
......
# -*- coding: utf-8 -*-
import datetime
from copy import deepcopy
import ddt
import hashlib
import json
......@@ -19,6 +20,12 @@ from openedx.core.djangoapps.user_api.preferences.api import set_user_preference
from .. import PRIVATE_VISIBILITY, ALL_USERS_VISIBILITY
# this is used in one test to check the behavior of profile image url
# generation with a relative url in the config.
TEST_PROFILE_IMAGE_BACKEND = deepcopy(settings.PROFILE_IMAGE_BACKEND)
TEST_PROFILE_IMAGE_BACKEND['options']['base_url'] = '/profile-images/'
class UserAPITestCase(APITestCase):
"""
The base class for all tests of the User API
......@@ -117,7 +124,7 @@ class TestAccountAPI(UserAPITestCase):
image.
"""
if has_profile_image:
url_root = 'http://example-storage.com/profile_images'
url_root = 'http://example-storage.com/profile-images'
filename = hashlib.md5('secret' + self.user.username).hexdigest()
file_extension = 'jpg'
else:
......@@ -593,12 +600,12 @@ class TestAccountAPI(UserAPITestCase):
)
self.assertIsNone(error_response.data["user_message"])
@override_settings(PROFILE_IMAGE_DOMAIN='/')
@override_settings(PROFILE_IMAGE_BACKEND=TEST_PROFILE_IMAGE_BACKEND)
def test_convert_relative_profile_url(self):
"""
Test that when PROFILE_IMAGE_DOMAIN is set to '/', the API
generates the full URL to profile images based on the URL
of the request.
Test that when TEST_PROFILE_IMAGE_BACKEND['base_url'] begins
with a '/', the API generates the full URL to profile images based on
the URL of the request.
"""
self.client.login(username=self.user.username, password=self.test_password)
response = self.send_get(self.client)
......
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