diff --git a/cms/envs/common.py b/cms/envs/common.py index b3d6bf1687006ee73784842f4354f9bfcc160afb..e2a3b6fe783c58467a9b6a86dc94ffeef1f2b998 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -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 diff --git a/lms/envs/common.py b/lms/envs/common.py index 14fe067eaf0081b6027ffd50a3503f9d1fe43e19..89e261d7b60a0cf3e26ddb19fdfbc82bad91c902 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -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 diff --git a/lms/envs/test.py b/lms/envs/test.py index fdcf919dbe14f04755f710d2a92553d4ca3d0290..4b6ad246ccd20eab476365baaf5674f75176cc00 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -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 diff --git a/lms/static/images/default-theme/default-profile_120.png b/lms/static/images/default-theme/default-profile_120.png new file mode 100644 index 0000000000000000000000000000000000000000..b3261d08ffba732d58181f593100011e6cb11f83 Binary files /dev/null and b/lms/static/images/default-theme/default-profile_120.png differ diff --git a/lms/static/images/default-theme/default-profile_30.png b/lms/static/images/default-theme/default-profile_30.png new file mode 100644 index 0000000000000000000000000000000000000000..2ce94a348d13a95eccbb95627f4b566db36401da Binary files /dev/null and b/lms/static/images/default-theme/default-profile_30.png differ diff --git a/lms/static/images/default-theme/default-profile_50.png b/lms/static/images/default-theme/default-profile_50.png new file mode 100644 index 0000000000000000000000000000000000000000..cef57edebd9ed9576636c4c3d106b19df05322d0 Binary files /dev/null and b/lms/static/images/default-theme/default-profile_50.png differ diff --git a/lms/static/images/default-theme/default-profile_500.png b/lms/static/images/default-theme/default-profile_500.png new file mode 100644 index 0000000000000000000000000000000000000000..ce3172373a99a742cff153927f786f1082a8c943 Binary files /dev/null and b/lms/static/images/default-theme/default-profile_500.png differ diff --git a/lms/static/images/edx-theme/default-profile_120.png b/lms/static/images/edx-theme/default-profile_120.png new file mode 100644 index 0000000000000000000000000000000000000000..8294f6c97e6161b2f27a09931210a0d07f73b130 Binary files /dev/null and b/lms/static/images/edx-theme/default-profile_120.png differ diff --git a/lms/static/images/edx-theme/default-profile_30.png b/lms/static/images/edx-theme/default-profile_30.png new file mode 100644 index 0000000000000000000000000000000000000000..8e7fb135c13fb57a185454ab083e0511f9941d8d Binary files /dev/null and b/lms/static/images/edx-theme/default-profile_30.png differ diff --git a/lms/static/images/edx-theme/default-profile_50.png b/lms/static/images/edx-theme/default-profile_50.png new file mode 100644 index 0000000000000000000000000000000000000000..b921c482bfc9ba54f154fa10fe8a6f42d86baacc Binary files /dev/null and b/lms/static/images/edx-theme/default-profile_50.png differ diff --git a/lms/static/images/edx-theme/default-profile_500.png b/lms/static/images/edx-theme/default-profile_500.png new file mode 100644 index 0000000000000000000000000000000000000000..69dcb956c7e14224e639930166ca4552cb16cce5 Binary files /dev/null and b/lms/static/images/edx-theme/default-profile_500.png differ diff --git a/openedx/core/djangoapps/user_api/accounts/image_helpers.py b/openedx/core/djangoapps/user_api/accounts/image_helpers.py index 64514db4c3b406f71d0611c0ffd5630b39420f10..9ab6c86cff33af4cda583cb4242d45c7a1356dcc 100644 --- a/openedx/core/djangoapps/user_api/accounts/image_helpers.py +++ b/openedx/core/djangoapps/user_api/accounts/image_helpers.py @@ -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): diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py index 729553706c9addd6a613de3d2edec37bced9b230..6f820f88ca6e8f56cf1e7d7954c9515e660488d5 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_api.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_api.py @@ -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': [], diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py b/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py index 8fda6808a48eb88096116c06d461149f71e7c3c0..4d1ced4411d8284f2720890adaa5ce2e9c85acd7 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py @@ -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) diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py index 76ead6b47a9401c1358c910e7abafb90fa6ad6b6..6a6d2bce3bf2cdd4fbe820d5048c2e71154e3a37 100644 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_views.py +++ b/openedx/core/djangoapps/user_api/accounts/tests/test_views.py @@ -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): """