Commit 378cf9bb by muzaffaryousaf

Sending preferences and accounts data to template.

Fixing the pencil icon issue.

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