Sending preferences and accounts data to template.
Fixing the pencil icon issue. TNL-2047
This commit is contained in:
@@ -3,6 +3,7 @@
|
||||
import logging
|
||||
import json
|
||||
from ipware.ip import get_ip
|
||||
import pyuca
|
||||
|
||||
from django.conf import settings
|
||||
from django.contrib import messages
|
||||
@@ -338,10 +339,13 @@ def account_settings_context(request):
|
||||
"""
|
||||
user = request.user
|
||||
|
||||
collator = pyuca.Collator()
|
||||
sort_key = lambda item: collator.sort_key(unicode(item[1]))
|
||||
|
||||
country_options = [
|
||||
(country_code, _(country_name)) # pylint: disable=translation-of-non-string
|
||||
for country_code, country_name in sorted(
|
||||
countries.countries, key=lambda(__, name): unicode(name)
|
||||
countries.countries, key=sort_key
|
||||
)
|
||||
]
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.test import TestCase
|
||||
from django.test.client import RequestFactory
|
||||
|
||||
from util.testing import UrlResetMixin
|
||||
from student.tests.factories import UserFactory
|
||||
@@ -25,6 +26,8 @@ class LearnerProfileViewTest(UrlResetMixin, TestCase):
|
||||
'own_profile',
|
||||
'country_options',
|
||||
'language_options',
|
||||
'account_settings_data',
|
||||
'preferences_data',
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
@@ -36,7 +39,9 @@ class LearnerProfileViewTest(UrlResetMixin, TestCase):
|
||||
"""
|
||||
Verify learner profile page context data.
|
||||
"""
|
||||
context = learner_profile_context(self.user.username, self.USERNAME, self.user.is_staff)
|
||||
request = RequestFactory().get('/url')
|
||||
|
||||
context = learner_profile_context(self.user, self.USERNAME, self.user.is_staff, request.build_absolute_uri)
|
||||
|
||||
self.assertEqual(
|
||||
context['data']['default_public_account_fields'],
|
||||
|
||||
@@ -1,21 +1,22 @@
|
||||
""" Views for a student's profile information. """
|
||||
|
||||
import pyuca
|
||||
|
||||
from django.conf import settings
|
||||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django_countries import countries
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.http import HttpResponse
|
||||
from django.http import Http404
|
||||
from django.views.decorators.http import require_http_methods
|
||||
|
||||
from edxmako.shortcuts import render_to_response
|
||||
from openedx.core.djangoapps.user_api.preferences.api import get_user_preferences
|
||||
from openedx.core.djangoapps.user_api.accounts.api import get_account_settings
|
||||
from openedx.core.djangoapps.user_api.errors import UserNotFound, UserNotAuthorized
|
||||
from openedx.core.djangoapps.user_api.accounts.serializers import PROFILE_IMAGE_KEY_PREFIX
|
||||
from openedx.core.djangoapps.user_api.errors import UserNotFound, UserNotAuthorized
|
||||
from openedx.core.djangoapps.user_api.preferences.api import get_user_preferences
|
||||
from student.models import User
|
||||
|
||||
from microsite_configuration import microsite
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
@@ -33,8 +34,9 @@ def learner_profile(request, username):
|
||||
Returns:
|
||||
HttpResponse: 200 if the page was sent successfully
|
||||
HttpResponse: 302 if not logged in (redirect to login page)
|
||||
HttpResponse: 404 if the specified username does not exist
|
||||
HttpResponse: 405 if using an unsupported HTTP method
|
||||
Raises:
|
||||
Http404: 404 if the specified user is not authorized or does not exist
|
||||
|
||||
Example usage:
|
||||
GET /account/profile
|
||||
@@ -42,23 +44,20 @@ def learner_profile(request, username):
|
||||
try:
|
||||
return render_to_response(
|
||||
'student_profile/learner_profile.html',
|
||||
learner_profile_context(request, request.user.username, username, request.user.is_staff)
|
||||
learner_profile_context(request.user, username, request.user.is_staff, request.build_absolute_uri)
|
||||
)
|
||||
except UserNotAuthorized:
|
||||
return HttpResponse(status=403)
|
||||
except UserNotFound:
|
||||
return HttpResponse(status=404)
|
||||
except ObjectDoesNotExist:
|
||||
return HttpResponse(status=404)
|
||||
except (UserNotAuthorized, UserNotFound, ObjectDoesNotExist):
|
||||
raise Http404
|
||||
|
||||
|
||||
def learner_profile_context(request, logged_in_username, profile_username, user_is_staff):
|
||||
def learner_profile_context(logged_in_user, profile_username, user_is_staff, build_absolute_uri_func):
|
||||
"""Context for the learner profile page.
|
||||
|
||||
Args:
|
||||
logged_in_username (str): Username of user logged In user.
|
||||
logged_in_user (object): Logged In user.
|
||||
profile_username (str): username of user whose profile is requested.
|
||||
user_is_staff (bool): Logged In user has staff access.
|
||||
build_absolute_uri_func ():
|
||||
|
||||
Returns:
|
||||
dict
|
||||
@@ -68,20 +67,22 @@ def learner_profile_context(request, logged_in_username, profile_username, user_
|
||||
"""
|
||||
profile_user = User.objects.get(username=profile_username)
|
||||
|
||||
collator = pyuca.Collator()
|
||||
sort_key = lambda item: collator.sort_key(unicode(item[1]))
|
||||
|
||||
country_options = [
|
||||
(country_code, _(country_name)) # pylint: disable=translation-of-non-string
|
||||
for country_code, country_name in sorted(
|
||||
countries.countries, key=lambda(__, name): unicode(name)
|
||||
countries.countries, key=sort_key
|
||||
)
|
||||
]
|
||||
own_profile = (logged_in_user.username == profile_username)
|
||||
|
||||
own_profile = (logged_in_username == profile_username)
|
||||
|
||||
accounts_data = get_account_settings(request.user, profile_username)
|
||||
account_settings_data = get_account_settings(logged_in_user, profile_username)
|
||||
# Account for possibly relative URLs.
|
||||
for key, value in accounts_data['profile_image'].items():
|
||||
for key, value in account_settings_data['profile_image'].items():
|
||||
if key.startswith(PROFILE_IMAGE_KEY_PREFIX):
|
||||
accounts_data['profile_image'][key] = request.build_absolute_uri(value)
|
||||
account_settings_data['profile_image'][key] = build_absolute_uri_func(value)
|
||||
|
||||
preferences_data = get_user_preferences(profile_user, profile_username)
|
||||
|
||||
@@ -93,13 +94,13 @@ def learner_profile_context(request, logged_in_username, profile_username, user_
|
||||
'accounts_api_url': reverse("accounts_api", kwargs={'username': profile_username}),
|
||||
'preferences_api_url': reverse('preferences_api', kwargs={'username': profile_username}),
|
||||
'preferences_data': preferences_data,
|
||||
'accounts_data': accounts_data,
|
||||
'account_settings_data': account_settings_data,
|
||||
'profile_image_upload_url': reverse('profile_image_upload', kwargs={'username': profile_username}),
|
||||
'profile_image_remove_url': reverse('profile_image_remove', kwargs={'username': profile_username}),
|
||||
'profile_image_max_bytes': settings.PROFILE_IMAGE_MAX_BYTES,
|
||||
'profile_image_min_bytes': settings.PROFILE_IMAGE_MIN_BYTES,
|
||||
'account_settings_page_url': reverse('account_settings'),
|
||||
'has_preferences_access': (logged_in_username == profile_username or user_is_staff),
|
||||
'has_preferences_access': (logged_in_user.username == profile_username or user_is_staff),
|
||||
'own_profile': own_profile,
|
||||
'country_options': country_options,
|
||||
'language_options': settings.ALL_LANGUAGES,
|
||||
|
||||
@@ -27,7 +27,7 @@ define(['backbone', 'jquery', 'underscore', 'js/common_helpers/ajax_helpers', 'j
|
||||
TemplateHelpers.installTemplate('templates/student_profile/learner_profile');
|
||||
});
|
||||
|
||||
var createProfilePage = function(ownProfile) {
|
||||
var createProfilePage = function(ownProfile, options) {
|
||||
return new LearnerProfilePage({
|
||||
'accounts_api_url': Helpers.USER_ACCOUNTS_API_URL,
|
||||
'preferences_api_url': Helpers.USER_PREFERENCES_API_URL,
|
||||
@@ -41,55 +41,13 @@ define(['backbone', 'jquery', 'underscore', 'js/common_helpers/ajax_helpers', 'j
|
||||
'profile_image_upload_url': Helpers.IMAGE_UPLOAD_API_URL,
|
||||
'profile_image_remove_url': Helpers.IMAGE_REMOVE_API_URL,
|
||||
'default_visibility': 'all_users',
|
||||
'platform_name': 'edX'
|
||||
'platform_name': 'edX',
|
||||
'account_settings_data': Helpers.createAccountSettingsData(options),
|
||||
'preferences_data': Helpers.createUserPreferencesData()
|
||||
});
|
||||
};
|
||||
|
||||
it("show loading error when UserAccountModel fails to load", function() {
|
||||
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true),
|
||||
learnerProfileView = context.learnerProfileView;
|
||||
|
||||
var userAccountRequest = requests[0];
|
||||
expect(userAccountRequest.method).toBe('GET');
|
||||
expect(userAccountRequest.url).toBe(Helpers.USER_ACCOUNTS_API_URL);
|
||||
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, true);
|
||||
Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, false);
|
||||
LearnerProfileHelpers.expectProfileSectionsNotToBeRendered(learnerProfileView);
|
||||
});
|
||||
|
||||
it("shows loading error when UserPreferencesModel fails to load", function() {
|
||||
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true),
|
||||
learnerProfileView = context.learnerProfileView;
|
||||
|
||||
var userAccountRequest = requests[0];
|
||||
expect(userAccountRequest.method).toBe('GET');
|
||||
expect(userAccountRequest.url).toBe(Helpers.USER_ACCOUNTS_API_URL);
|
||||
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData());
|
||||
Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true);
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, false);
|
||||
LearnerProfileHelpers.expectProfileSectionsNotToBeRendered(learnerProfileView);
|
||||
|
||||
var userPreferencesRequest = requests[1];
|
||||
expect(userPreferencesRequest.method).toBe('GET');
|
||||
expect(userPreferencesRequest.url).toBe(Helpers.USER_PREFERENCES_API_URL);
|
||||
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, false);
|
||||
Helpers.expectLoadingErrorIsVisible(learnerProfileView, true);
|
||||
LearnerProfileHelpers.expectProfileSectionsNotToBeRendered(learnerProfileView);
|
||||
});
|
||||
|
||||
it("renders the full profile after models are successfully fetched", function() {
|
||||
it("renders the full profile after data is successfully fetched", function() {
|
||||
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
@@ -106,33 +64,19 @@ define(['backbone', 'jquery', 'underscore', 'js/common_helpers/ajax_helpers', 'j
|
||||
|
||||
it("renders the limited profile for undefined 'year_of_birth'", function() {
|
||||
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true),
|
||||
var context = createProfilePage(true, {year_of_birth: '', requires_parental_consent: true}),
|
||||
learnerProfileView = context.learnerProfileView;
|
||||
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData({
|
||||
year_of_birth: '',
|
||||
requires_parental_consent: true
|
||||
}));
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData());
|
||||
|
||||
LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView);
|
||||
});
|
||||
|
||||
it("renders the limited profile for under 13 users", function() {
|
||||
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
var context = createProfilePage(true),
|
||||
learnerProfileView = context.learnerProfileView;
|
||||
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData({
|
||||
year_of_birth: new Date().getFullYear() - 10,
|
||||
requires_parental_consent: true
|
||||
}));
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData());
|
||||
|
||||
var context = createProfilePage(
|
||||
true,
|
||||
{year_of_birth: new Date().getFullYear() - 10, requires_parental_consent: true}
|
||||
);
|
||||
var learnerProfileView = context.learnerProfileView;
|
||||
LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -34,8 +34,8 @@
|
||||
// Currently when a non-staff user A access user B's profile, the only way to tell whether user B's
|
||||
// profile is public is to check if the api has returned fields other than the default public fields
|
||||
// specified in settings.ACCOUNT_VISIBILITY_CONFIGURATION.
|
||||
var profileIsPublic = _.size(_.difference(_.keys(response), this.get('default_public_account_fields'))) > 0;
|
||||
this.set({'profile_is_public': profileIsPublic}, { silent: true });
|
||||
var responseKeys = _.filter(_.keys(response), function (key) {return key !== 'default_public_account_fields'});
|
||||
response.profile_is_public = _.size(_.difference(responseKeys, response.default_public_account_fields)) > 0;
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
@@ -15,18 +15,21 @@
|
||||
return function (options) {
|
||||
|
||||
var learnerProfileElement = $('.wrapper-profile');
|
||||
var defaultVisibility = options.default_visibility;
|
||||
var accountPreferencesModel, accountSettingsModel;
|
||||
|
||||
if (options.own_profile) {
|
||||
accountSettingsModel = new AccountSettingsModel({
|
||||
'default_public_account_fields': options.default_public_account_fields
|
||||
});
|
||||
accountPreferencesModel = new AccountPreferencesModel({account_privacy: defaultVisibility});
|
||||
} else {
|
||||
accountSettingsModel = new AccountSettingsModel(options.accounts_data, {parse: true});
|
||||
accountPreferencesModel = new AccountPreferencesModel(options.preferences_data);
|
||||
}
|
||||
var accountSettingsModel = new AccountSettingsModel(
|
||||
_.extend(
|
||||
options.account_settings_data,
|
||||
{'default_public_account_fields': options.default_public_account_fields}
|
||||
),
|
||||
{parse: true}
|
||||
);
|
||||
var AccountPreferencesModelWithDefaults = AccountPreferencesModel.extend({
|
||||
defaults: {
|
||||
account_privacy: options.default_visibility
|
||||
}
|
||||
});
|
||||
var accountPreferencesModel = new AccountPreferencesModelWithDefaults(options.preferences_data);
|
||||
|
||||
accountSettingsModel.url = options.accounts_api_url;
|
||||
accountPreferencesModel.url = options.preferences_api_url;
|
||||
|
||||
@@ -123,10 +126,6 @@
|
||||
sectionTwoFieldViews: sectionTwoFieldViews
|
||||
});
|
||||
|
||||
var showLoadingError = function () {
|
||||
learnerProfileView.showLoadingError();
|
||||
};
|
||||
|
||||
var getProfileVisibility = function() {
|
||||
if (options.has_preferences_access) {
|
||||
return accountPreferencesModel.get('account_privacy');
|
||||
@@ -147,34 +146,12 @@
|
||||
learnerProfileView.render();
|
||||
};
|
||||
|
||||
if (options.own_profile) {
|
||||
accountSettingsModel.fetch({
|
||||
success: function () {
|
||||
// Fetch the preferences model if the user has access
|
||||
if (options.has_preferences_access) {
|
||||
accountPreferencesModel.fetch({
|
||||
success: function () {
|
||||
if (accountSettingsModel.get('requires_parental_consent')) {
|
||||
accountPreferencesModel.set('account_privacy', 'private');
|
||||
}
|
||||
showLearnerProfileView();
|
||||
},
|
||||
error: showLoadingError
|
||||
});
|
||||
} else {
|
||||
showLearnerProfileView();
|
||||
}
|
||||
},
|
||||
error: showLoadingError
|
||||
});
|
||||
} else {
|
||||
if (options.has_preferences_access) {
|
||||
if (accountSettingsModel.get('requires_parental_consent')) {
|
||||
accountPreferencesModel.set('account_privacy', 'private');
|
||||
}
|
||||
if (options.has_preferences_access) {
|
||||
if (accountSettingsModel.get('requires_parental_consent')) {
|
||||
accountPreferencesModel.set('account_privacy', 'private');
|
||||
}
|
||||
showLearnerProfileView();
|
||||
}
|
||||
showLearnerProfileView();
|
||||
|
||||
return {
|
||||
accountSettingsModel: accountSettingsModel,
|
||||
|
||||
@@ -114,7 +114,11 @@
|
||||
|
||||
setTimeout(function () {
|
||||
if ((context === view.lastSuccessMessageContext) && (view.getNotificationMessage() === successMessage)) {
|
||||
view.showHelpMessage();
|
||||
if (view.editable === 'toggle') {
|
||||
view.showCanEditMessage(true);
|
||||
} else {
|
||||
view.showHelpMessage();
|
||||
}
|
||||
}
|
||||
}, messageRevertDelay);
|
||||
},
|
||||
@@ -201,15 +205,13 @@
|
||||
this.$el.addClass('mode-edit');
|
||||
},
|
||||
|
||||
startEditing: function (event) {
|
||||
event.preventDefault();
|
||||
startEditing: function () {
|
||||
if (this.editable === 'toggle' && this.mode !== 'edit') {
|
||||
this.showEditMode(true);
|
||||
}
|
||||
},
|
||||
|
||||
finishEditing: function(event) {
|
||||
event.preventDefault();
|
||||
finishEditing: function() {
|
||||
if (this.fieldValue() !== this.modelValue()) {
|
||||
this.saveValue();
|
||||
} else {
|
||||
|
||||
@@ -21,6 +21,8 @@
|
||||
git+https://github.com/mitocw/django-cas.git@60a5b8e5a62e63e0d5d224a87f0b489201a0c695#egg=django-cas
|
||||
-e git+https://github.com/dgrtwo/ParsePy.git@7949b9f754d1445eff8e8f20d0e967b9a6420639#egg=parse_rest
|
||||
git+https://github.com/mfogel/django-settings-context-processor.git@b758c3930862761216267715a70bbefd206ac03a#egg=django-settings-context-processor==0.2
|
||||
git+https://github.com/SmileyChris/pyuca.git@555292b39692e2c8a13fbba02a284fb89c9940e4#egg=pyuca==1.0
|
||||
|
||||
# Master pyfs has a bug working with VPC auth. This is a fix. We should switch
|
||||
# back to master when and if this fix is merged back.
|
||||
# fs==0.4.0
|
||||
|
||||
Reference in New Issue
Block a user