From 46b63164a083e26b065d86cb45db4e31cb14392e Mon Sep 17 00:00:00 2001 From: jsa Date: Mon, 9 Mar 2015 11:29:42 -0400 Subject: [PATCH] Implement profile_image upload and remove endpoints TNL-1537 Co-Authored-By: Andy Armstrong Co-Authored-By: cahrens --- common/djangoapps/enrollment/views.py | 5 +- lms/djangoapps/commerce/views.py | 2 +- lms/djangoapps/mobile_api/utils.py | 8 +- lms/envs/common.py | 11 +- lms/envs/test.py | 2 + lms/urls.py | 4 + .../djangoapps/profile_images/__init__.py | 0 .../core/djangoapps/profile_images/images.py | 132 ++++++++ .../profile_images/tests/__init__.py | 0 .../profile_images/tests/helpers.py | 55 ++++ .../profile_images/tests/test_images.py | 182 +++++++++++ .../profile_images/tests/test_views.py | 304 ++++++++++++++++++ .../core/djangoapps/profile_images/urls.py | 22 ++ .../core/djangoapps/profile_images/views.py | 152 +++++++++ .../djangoapps/user_api/accounts/helpers.py | 57 ---- .../user_api/accounts/image_helpers.py | 115 +++++++ .../user_api/accounts/serializers.py | 9 +- .../user_api/accounts/tests/test_api.py | 5 +- .../user_api/accounts/tests/test_helpers.py | 69 ---- .../accounts/tests/test_image_helpers.py | 60 ++++ .../user_api/accounts/tests/test_views.py | 9 +- .../djangoapps/user_api/accounts/views.py | 17 +- .../djangoapps/user_api/preferences/api.py | 1 - .../user_api/preferences/tests/test_views.py | 10 +- .../djangoapps/user_api/preferences/views.py | 45 ++- .../djangoapps/user_api/tests/test_views.py | 1 + openedx/core/djangoapps/user_api/views.py | 11 +- .../core/lib/api}/authentication.py | 0 openedx/core/lib/api/permissions.py | 21 +- openedx/core/lib/api/tests/__init__.py | 0 .../lib/api}/tests/test_authentication.py | 0 31 files changed, 1135 insertions(+), 174 deletions(-) create mode 100644 openedx/core/djangoapps/profile_images/__init__.py create mode 100644 openedx/core/djangoapps/profile_images/images.py create mode 100644 openedx/core/djangoapps/profile_images/tests/__init__.py create mode 100644 openedx/core/djangoapps/profile_images/tests/helpers.py create mode 100644 openedx/core/djangoapps/profile_images/tests/test_images.py create mode 100644 openedx/core/djangoapps/profile_images/tests/test_views.py create mode 100644 openedx/core/djangoapps/profile_images/urls.py create mode 100644 openedx/core/djangoapps/profile_images/views.py delete mode 100644 openedx/core/djangoapps/user_api/accounts/helpers.py create mode 100644 openedx/core/djangoapps/user_api/accounts/image_helpers.py delete mode 100644 openedx/core/djangoapps/user_api/accounts/tests/test_helpers.py create mode 100644 openedx/core/djangoapps/user_api/accounts/tests/test_image_helpers.py rename {common/djangoapps/util => openedx/core/lib/api}/authentication.py (100%) create mode 100644 openedx/core/lib/api/tests/__init__.py rename {common/djangoapps/util => openedx/core/lib/api}/tests/test_authentication.py (100%) diff --git a/common/djangoapps/enrollment/views.py b/common/djangoapps/enrollment/views.py index 57301a9ec0..6ec535bae1 100644 --- a/common/djangoapps/enrollment/views.py +++ b/common/djangoapps/enrollment/views.py @@ -18,7 +18,10 @@ from opaque_keys.edx.keys import CourseKey from embargo import api as embargo_api from cors_csrf.authentication import SessionAuthenticationCrossDomainCsrf from cors_csrf.decorators import ensure_csrf_cookie_cross_domain -from util.authentication import SessionAuthenticationAllowInactiveUser, OAuth2AuthenticationAllowInactiveUser +from openedx.core.lib.api.authentication import ( + SessionAuthenticationAllowInactiveUser, + OAuth2AuthenticationAllowInactiveUser, +) from util.disable_rate_limit import can_disable_rate_limit from enrollment import api from enrollment.errors import ( diff --git a/lms/djangoapps/commerce/views.py b/lms/djangoapps/commerce/views.py index 46f0b92574..44b17efaa1 100644 --- a/lms/djangoapps/commerce/views.py +++ b/lms/djangoapps/commerce/views.py @@ -15,7 +15,7 @@ from course_modes.models import CourseMode from courseware import courses from enrollment.api import add_enrollment from student.models import CourseEnrollment -from util.authentication import SessionAuthenticationAllowInactiveUser +from openedx.core.lib.api.authentication import SessionAuthenticationAllowInactiveUser log = logging.getLogger(__name__) diff --git a/lms/djangoapps/mobile_api/utils.py b/lms/djangoapps/mobile_api/utils.py index 4d4eed8d26..57dc67c73c 100644 --- a/lms/djangoapps/mobile_api/utils.py +++ b/lms/djangoapps/mobile_api/utils.py @@ -2,14 +2,16 @@ Common utility methods and decorators for Mobile APIs. """ - import functools from rest_framework import permissions - -from util.authentication import SessionAuthenticationAllowInactiveUser, OAuth2AuthenticationAllowInactiveUser from opaque_keys.edx.keys import CourseKey from xmodule.modulestore.django import modulestore + from courseware.courses import get_course_with_access +from openedx.core.lib.api.authentication import ( + SessionAuthenticationAllowInactiveUser, + OAuth2AuthenticationAllowInactiveUser, +) from openedx.core.lib.api.permissions import IsUserInUrl diff --git a/lms/envs/common.py b/lms/envs/common.py index 6a27103021..14fe067eaf 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -2259,7 +2259,13 @@ FIELD_OVERRIDE_PROVIDERS = () # PROFILE IMAGE CONFIG # TODO: add these settings to aws.py as well -PROFILE_IMAGE_BACKEND = 'django.core.files.storage.FileSystemStorage' +# WARNING: Certain django storage backends do not support atomic +# file overwrites (including the default, specified below) - 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 @@ -2272,4 +2278,5 @@ PROFILE_IMAGE_DEFAULT_FILENAME = 'default_profile_image' # TODO: determine fina # platform unaware of current image URLs, resulting in reverting all # users' profile images to the default placeholder image. PROFILE_IMAGE_SECRET_KEY = 'placeholder secret key' - +PROFILE_IMAGE_MAX_BYTES = 1024 * 1024 +PROFILE_IMAGE_MIN_BYTES = 100 diff --git a/lms/envs/test.py b/lms/envs/test.py index 58e1e7fef4..fdcf919dbe 100644 --- a/lms/envs/test.py +++ b/lms/envs/test.py @@ -483,3 +483,5 @@ PROFILE_IMAGE_DOMAIN = 'http://example-storage.com/' PROFILE_IMAGE_URL_PATH = 'profile_images/' PROFILE_IMAGE_DEFAULT_FILENAME = 'default' PROFILE_IMAGE_SECRET_KEY = 'secret' +PROFILE_IMAGE_MAX_BYTES = 1024 * 1024 +PROFILE_IMAGE_MIN_BYTES = 100 diff --git a/lms/urls.py b/lms/urls.py index bb041f9876..7822ea7513 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -91,6 +91,7 @@ urlpatterns = ( if settings.FEATURES["ENABLE_USER_REST_API"]: urlpatterns += ( url(r'^api/user/', include('openedx.core.djangoapps.user_api.urls')), + url(r'^api/profile_images/', include('openedx.core.djangoapps.profile_images.urls')), ) if settings.FEATURES["ENABLE_COMBINED_LOGIN_REGISTRATION"]: @@ -637,6 +638,9 @@ 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 + ) # in debug mode, allow any template to be rendered (most useful for UX reference templates) urlpatterns += url(r'^template/(?P