Commit c898e413 by Andy Armstrong

Add default profile images

TNL-1796
parent 20410a9c
......@@ -41,7 +41,7 @@ from lms.envs.common import (
# 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_SECRET_KEY
PROFILE_IMAGE_DEFAULT_FILE_EXTENSION, PROFILE_IMAGE_SECRET_KEY,
)
from path import path
from warnings import simplefilter
......
......@@ -2272,7 +2272,10 @@ PROFILE_IMAGE_BACKEND = 'storages.backends.overwrite.OverwriteStorage'
# i.e. 'http://www.example-image-server.com/'
PROFILE_IMAGE_DOMAIN = '/'
PROFILE_IMAGE_URL_PATH = 'media/profile_images/'
PROFILE_IMAGE_DEFAULT_FILENAME = 'default_profile_image' # TODO: determine final name
PROFILE_IMAGE_DEFAULT_FILENAME = (
'images/edx-theme/default-profile' if FEATURES['IS_EDX_DOMAIN'] else 'images/default-theme/default-profile'
)
PROFILE_IMAGE_DEFAULT_FILE_EXTENSION = 'png'
# This secret key is used in generating unguessable URLs to users'
# profile images. Once it has been set, changing it will make the
# platform unaware of current image URLs, resulting in reverting all
......
......@@ -482,6 +482,7 @@ PROFILE_IMAGE_BACKEND = 'django.core.files.storage.FileSystemStorage'
PROFILE_IMAGE_DOMAIN = 'http://example-storage.com/'
PROFILE_IMAGE_URL_PATH = 'profile_images/'
PROFILE_IMAGE_DEFAULT_FILENAME = 'default'
PROFILE_IMAGE_DEFAULT_FILE_EXTENSION = 'png'
PROFILE_IMAGE_SECRET_KEY = 'secret'
PROFILE_IMAGE_MAX_BYTES = 1024 * 1024
PROFILE_IMAGE_MIN_BYTES = 100
......@@ -6,11 +6,13 @@ import hashlib
from django.conf import settings
from django.core.exceptions import ObjectDoesNotExist
from django.core.files.storage import get_storage_class
from staticfiles.storage import staticfiles_storage
from student.models import UserProfile
from ..errors import UserNotFound
PROFILE_IMAGE_FILE_EXTENSION = 'jpg' # All processed profile images are converted to JPEGs
PROFILE_IMAGE_SIZES_MAP = {
'full': 500,
'large': 120,
......@@ -37,21 +39,20 @@ def _make_profile_image_name(username):
return hashlib.md5(settings.PROFILE_IMAGE_SECRET_KEY + username).hexdigest()
def _get_profile_image_filename(name, size):
def _get_profile_image_filename(name, size, file_extension=PROFILE_IMAGE_FILE_EXTENSION):
"""
Returns the full filename for a profile image, given the name and size.
"""
return '{name}_{size}.jpg'.format(name=name, size=size)
return '{name}_{size}.{file_extension}'.format(name=name, size=size, file_extension=file_extension)
def _get_profile_image_urls(name):
def _get_profile_image_urls(name, storage, file_extension=PROFILE_IMAGE_FILE_EXTENSION):
"""
Returns a dict containing the urls for a complete set of profile images,
keyed by "friendly" name (e.g. "full", "large", "medium", "small").
"""
storage = get_profile_image_storage()
return {
size_display_name: storage.url(_get_profile_image_filename(name, size))
size_display_name: storage.url(_get_profile_image_filename(name, size, file_extension=file_extension))
for size_display_name, size in PROFILE_IMAGE_SIZES_MAP.items()
}
......@@ -86,7 +87,7 @@ def get_profile_image_urls_for_user(user):
"""
if user.profile.has_profile_image:
return _get_profile_image_urls(_make_profile_image_name(user.username))
return _get_profile_image_urls(_make_profile_image_name(user.username), get_profile_image_storage())
else:
return _get_default_profile_image_urls()
......@@ -98,7 +99,11 @@ def _get_default_profile_image_urls():
TODO The result of this function should be memoized, but not in tests.
"""
return _get_profile_image_urls(settings.PROFILE_IMAGE_DEFAULT_FILENAME)
return _get_profile_image_urls(
settings.PROFILE_IMAGE_DEFAULT_FILENAME,
staticfiles_storage,
file_extension=settings.PROFILE_IMAGE_DEFAULT_FILE_EXTENSION
)
def set_has_profile_image(username, has_profile_image=True):
......
......@@ -231,8 +231,8 @@ class AccountSettingsOnCreationTest(TestCase):
'bio': None,
'profile_image': {
'has_image': False,
'image_url_full': 'http://example-storage.com/profile_images/default_50.jpg',
'image_url_small': 'http://example-storage.com/profile_images/default_10.jpg',
'image_url_full': '/static/default_50.png',
'image_url_small': '/static/default_10.png',
},
'requires_parental_consent': True,
'language_proficiencies': [],
......
......@@ -34,16 +34,30 @@ class ProfileImageUrlTestCase(TestCase):
"""
self.assertEqual(
actual_url,
'http://example-storage.com/profile_images/{0}_{1}.jpg'.format(expected_name, expected_pixels),
'http://example-storage.com/profile_images/{name}_{size}.jpg'.format(
name=expected_name, size=expected_pixels
)
)
def verify_urls(self, expected_name, actual_urls):
def verify_default_url(self, actual_url, expected_pixels):
"""
Verify correct url structure for a default profile image.
"""
self.assertEqual(
actual_url,
'/static/default_{size}.png'.format(size=expected_pixels)
)
def verify_urls(self, expected_name, actual_urls, is_default=False):
"""
Verify correct url dictionary structure.
"""
self.assertEqual(set(TEST_SIZES.keys()), set(actual_urls.keys()))
for size_display_name, url in actual_urls.items():
self.verify_url(url, expected_name, TEST_SIZES[size_display_name])
if is_default:
self.verify_default_url(url, TEST_SIZES[size_display_name])
else:
self.verify_url(url, expected_name, TEST_SIZES[size_display_name])
def test_get_profile_image_urls(self):
"""
......@@ -57,4 +71,4 @@ class ProfileImageUrlTestCase(TestCase):
self.user.profile.has_profile_image = False
self.user.profile.save()
self.verify_urls('default', get_profile_image_urls_for_user(self.user))
self.verify_urls('default', get_profile_image_urls_for_user(self.user), is_default=True)
......@@ -117,15 +117,23 @@ class TestAccountAPI(UserAPITestCase):
image.
"""
if has_profile_image:
url_root = 'http://example-storage.com/profile_images'
filename = hashlib.md5('secret' + self.user.username).hexdigest()
file_extension = 'jpg'
else:
url_root = 'http://testserver/static'
filename = 'default'
file_extension = 'png'
self.assertEqual(
data['profile_image'],
{
'has_image': has_profile_image,
'image_url_full': 'http://example-storage.com/profile_images/{}_50.jpg'.format(filename),
'image_url_small': 'http://example-storage.com/profile_images/{}_10.jpg'.format(filename)
'image_url_full': '{root}/{filename}_50.{extension}'.format(
root=url_root, filename=filename, extension=file_extension,
),
'image_url_small': '{root}/{filename}_10.{extension}'.format(
root=url_root, filename=filename, extension=file_extension,
)
}
)
......@@ -584,7 +592,7 @@ class TestAccountAPI(UserAPITestCase):
error_response.data["developer_message"]
)
self.assertIsNone(error_response.data["user_message"])
@override_settings(PROFILE_IMAGE_DOMAIN='/')
def test_convert_relative_profile_url(self):
"""
......@@ -599,8 +607,8 @@ class TestAccountAPI(UserAPITestCase):
response.data["profile_image"],
{
"has_image": False,
"image_url_full": "http://testserver/profile_images/default_50.jpg",
"image_url_small": "http://testserver/profile_images/default_10.jpg"
"image_url_full": "http://testserver/static/default_50.png",
"image_url_small": "http://testserver/static/default_10.png"
}
)
......@@ -647,7 +655,7 @@ class TestAccountAPI(UserAPITestCase):
response = self.send_get(client, query_parameters='view=shared')
self._verify_private_account_response(response, requires_parental_consent=True)
@unittest.skipUnless(settings.ROOT_URLCONF == 'lms.urls', 'Test only valid in lms')
class TestAccountAPITransactions(TransactionTestCase):
"""
......
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