Merge pull request #16703 from edx/zub/ENT-762-update-account-settings-page-for-enterprise-sso
Update account settings page for enterprise learners
This commit is contained in:
@@ -163,7 +163,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, AcceptanceTest):
|
||||
'fields': [
|
||||
'Username',
|
||||
'Full Name',
|
||||
'Email Address',
|
||||
'Email Address (Sign In)',
|
||||
'Password',
|
||||
'Language',
|
||||
'Country or Region of Residence',
|
||||
@@ -289,7 +289,7 @@ class AccountSettingsPageTest(AccountSettingsTestMixin, AcceptanceTest):
|
||||
self.visit_account_settings_page()
|
||||
self._test_text_field(
|
||||
u'email',
|
||||
u'Email Address',
|
||||
u'Email Address (Sign In)',
|
||||
email,
|
||||
u'test@example.com' + XSS_INJECTION,
|
||||
[u'me@here.com', u'you@there.com'],
|
||||
|
||||
@@ -35,6 +35,7 @@ from lms.djangoapps.commerce.tests import factories
|
||||
from lms.djangoapps.commerce.tests.mocks import mock_get_orders
|
||||
from openedx.core.djangoapps.oauth_dispatch.tests import factories as dot_factories
|
||||
from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin
|
||||
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory
|
||||
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
|
||||
from openedx.core.djangoapps.theming.tests.test_util import with_comprehensive_theme_context
|
||||
from openedx.core.djangoapps.user_api.accounts.api import activate_account, create_account
|
||||
@@ -727,8 +728,10 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
|
||||
MessageMiddleware().process_request(self.request)
|
||||
messages.error(self.request, 'Facebook is already in use.', extra_tags='Auth facebook')
|
||||
|
||||
def test_context(self):
|
||||
|
||||
@mock.patch('student_account.views.get_enterprise_learner_data')
|
||||
def test_context(self, mock_get_enterprise_learner_data):
|
||||
self.request.site = SiteFactory.create()
|
||||
mock_get_enterprise_learner_data.return_value = []
|
||||
context = account_settings_context(self.request)
|
||||
|
||||
user_accounts_api_url = reverse("accounts_api", kwargs={'username': self.user.username})
|
||||
@@ -751,6 +754,59 @@ class AccountSettingsViewTest(ThirdPartyAuthTestMixin, TestCase, ProgramsApiConf
|
||||
self.assertEqual(context['auth']['providers'][0]['name'], 'Facebook')
|
||||
self.assertEqual(context['auth']['providers'][1]['name'], 'Google')
|
||||
|
||||
self.assertEqual(context['sync_learner_profile_data'], False)
|
||||
self.assertEqual(context['edx_support_url'], settings.SUPPORT_SITE_LINK)
|
||||
self.assertEqual(context['enterprise_name'], None)
|
||||
self.assertEqual(
|
||||
context['enterprise_readonly_account_fields'], {'fields': settings.ENTERPRISE_READONLY_ACCOUNT_FIELDS}
|
||||
)
|
||||
|
||||
@mock.patch('student_account.views.get_enterprise_learner_data')
|
||||
@mock.patch('student_account.views.third_party_auth.provider.Registry.get')
|
||||
def test_context_for_enterprise_learner(
|
||||
self, mock_get_auth_provider, mock_get_enterprise_learner_data
|
||||
):
|
||||
dummy_enterprise_customer = {
|
||||
'uuid': 'real-ent-uuid',
|
||||
'name': 'Dummy Enterprise',
|
||||
'identity_provider': 'saml-ubc'
|
||||
}
|
||||
mock_get_enterprise_learner_data.return_value = [
|
||||
{'enterprise_customer': dummy_enterprise_customer}
|
||||
]
|
||||
self.request.site = SiteFactory.create()
|
||||
mock_get_auth_provider.return_value.sync_learner_profile_data = True
|
||||
context = account_settings_context(self.request)
|
||||
|
||||
user_accounts_api_url = reverse("accounts_api", kwargs={'username': self.user.username})
|
||||
self.assertEqual(context['user_accounts_api_url'], user_accounts_api_url)
|
||||
|
||||
user_preferences_api_url = reverse('preferences_api', kwargs={'username': self.user.username})
|
||||
self.assertEqual(context['user_preferences_api_url'], user_preferences_api_url)
|
||||
|
||||
for attribute in self.FIELDS:
|
||||
self.assertIn(attribute, context['fields'])
|
||||
|
||||
self.assertEqual(
|
||||
context['user_accounts_api_url'], reverse("accounts_api", kwargs={'username': self.user.username})
|
||||
)
|
||||
self.assertEqual(
|
||||
context['user_preferences_api_url'], reverse('preferences_api', kwargs={'username': self.user.username})
|
||||
)
|
||||
|
||||
self.assertEqual(context['duplicate_provider'], 'facebook')
|
||||
self.assertEqual(context['auth']['providers'][0]['name'], 'Facebook')
|
||||
self.assertEqual(context['auth']['providers'][1]['name'], 'Google')
|
||||
|
||||
self.assertEqual(
|
||||
context['sync_learner_profile_data'], mock_get_auth_provider.return_value.sync_learner_profile_data
|
||||
)
|
||||
self.assertEqual(context['edx_support_url'], settings.SUPPORT_SITE_LINK)
|
||||
self.assertEqual(context['enterprise_name'], dummy_enterprise_customer['name'])
|
||||
self.assertEqual(
|
||||
context['enterprise_readonly_account_fields'], {'fields': settings.ENTERPRISE_READONLY_ACCOUNT_FIELDS}
|
||||
)
|
||||
|
||||
def test_view(self):
|
||||
"""
|
||||
Test that all fields are visible
|
||||
|
||||
@@ -40,7 +40,7 @@ from openedx.core.djangoapps.user_api.errors import (
|
||||
)
|
||||
from openedx.core.lib.edx_api_utils import get_edx_api_data
|
||||
from openedx.core.lib.time_zone_utils import TIME_ZONE_CHOICES
|
||||
from openedx.features.enterprise_support.api import enterprise_customer_for_request
|
||||
from openedx.features.enterprise_support.api import enterprise_customer_for_request, get_enterprise_learner_data
|
||||
from student.helpers import destroy_oauth_tokens, get_next_url_for_login_page
|
||||
from student.models import UserProfile
|
||||
from student.views import register_user as old_register_view
|
||||
@@ -567,6 +567,22 @@ def account_settings_context(request):
|
||||
'order_history': user_orders
|
||||
}
|
||||
|
||||
enterprise_customer_name = None
|
||||
sync_learner_profile_data = False
|
||||
enterprise_learner_data = get_enterprise_learner_data(site=request.site, user=request.user)
|
||||
if enterprise_learner_data:
|
||||
enterprise_customer_name = enterprise_learner_data[0]['enterprise_customer']['name']
|
||||
enterprise_idp = enterprise_learner_data[0]['enterprise_customer']['identity_provider']
|
||||
identity_provider = third_party_auth.provider.Registry.get(provider_id=enterprise_idp)
|
||||
sync_learner_profile_data = identity_provider.sync_learner_profile_data if identity_provider else False
|
||||
|
||||
context['sync_learner_profile_data'] = sync_learner_profile_data
|
||||
context['edx_support_url'] = configuration_helpers.get_value('SUPPORT_SITE_LINK', settings.SUPPORT_SITE_LINK)
|
||||
context['enterprise_name'] = enterprise_customer_name
|
||||
context['enterprise_readonly_account_fields'] = {
|
||||
'fields': settings.ENTERPRISE_READONLY_ACCOUNT_FIELDS
|
||||
}
|
||||
|
||||
if third_party_auth.is_enabled():
|
||||
# If the account on the third party provider is already connected with another edX account,
|
||||
# we display a message to the user.
|
||||
|
||||
@@ -3395,6 +3395,12 @@ ENTERPRISE_EXCLUDED_REGISTRATION_FIELDS = {
|
||||
'year_of_birth',
|
||||
'mailing_address',
|
||||
}
|
||||
ENTERPRISE_READONLY_ACCOUNT_FIELDS = [
|
||||
'username',
|
||||
'name',
|
||||
'email',
|
||||
'country',
|
||||
]
|
||||
ENTERPRISE_CUSTOMER_COOKIE_NAME = 'enterprise_customer_uuid'
|
||||
BASE_COOKIE_DOMAIN = 'localhost'
|
||||
|
||||
|
||||
@@ -148,4 +148,183 @@ define(['backbone',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('edx.user.AccountSettingsFactory', function() {
|
||||
var createEnterpriseLearnerAccountSettingsPage = function() {
|
||||
var context = AccountSettingsPage(
|
||||
Helpers.FIELDS_DATA,
|
||||
[],
|
||||
Helpers.AUTH_DATA,
|
||||
Helpers.PASSWORD_RESET_SUPPORT_LINK,
|
||||
Helpers.USER_ACCOUNTS_API_URL,
|
||||
Helpers.USER_PREFERENCES_API_URL,
|
||||
1,
|
||||
Helpers.PLATFORM_NAME,
|
||||
Helpers.CONTACT_EMAIL,
|
||||
true,
|
||||
'',
|
||||
|
||||
Helpers.SYNC_LEARNER_PROFILE_DATA,
|
||||
Helpers.ENTERPRISE_NAME,
|
||||
Helpers.ENTERPRISE_READ_ONLY_ACCOUNT_FIELDS,
|
||||
Helpers.EDX_SUPPORT_URL
|
||||
);
|
||||
return context.accountSettingsView;
|
||||
};
|
||||
|
||||
var requests;
|
||||
var accountInfoTab = {
|
||||
BASIC_ACCOUNT_INFORMATION: 0,
|
||||
ADDITIONAL_INFORMATION: 1
|
||||
};
|
||||
var basicAccountInfoFields = {
|
||||
USERNAME: 0,
|
||||
FULL_NAME: 1,
|
||||
EMAIL_ADDRESS: 2,
|
||||
PASSWORD: 3,
|
||||
LANGUAGE: 4,
|
||||
COUNTRY: 5,
|
||||
TIMEZONE: 6
|
||||
};
|
||||
var additionalInfoFields = {
|
||||
EDUCATION: 0,
|
||||
GENDER: 1,
|
||||
YEAR_OF_BIRTH: 2,
|
||||
PREFERRED_LANGUAGE: 3
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures('<div class="wrapper-account-settings"></div>');
|
||||
});
|
||||
|
||||
it('shows loading error when UserAccountModel fails to load for enterprise learners', function() {
|
||||
var accountSettingsView, request;
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
accountSettingsView = createEnterpriseLearnerAccountSettingsPage();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);
|
||||
|
||||
request = requests[0];
|
||||
expect(request.method).toBe('GET');
|
||||
expect(request.url).toBe(Helpers.USER_ACCOUNTS_API_URL);
|
||||
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
Helpers.expectLoadingErrorIsVisible(accountSettingsView, true);
|
||||
});
|
||||
|
||||
it('shows loading error when UserPreferencesModel fails to load for enterprise learners', function() {
|
||||
var accountSettingsView, request;
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
accountSettingsView = createEnterpriseLearnerAccountSettingsPage();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);
|
||||
|
||||
request = requests[0];
|
||||
expect(request.method).toBe('GET');
|
||||
expect(request.url).toBe(Helpers.USER_ACCOUNTS_API_URL);
|
||||
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData());
|
||||
Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);
|
||||
|
||||
request = requests[1];
|
||||
expect(request.method).toBe('GET');
|
||||
expect(request.url).toBe('/user_api/v1/preferences/time_zones/?country_code=1');
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.TIME_ZONE_RESPONSE);
|
||||
|
||||
request = requests[2];
|
||||
expect(request.method).toBe('GET');
|
||||
expect(request.url).toBe(Helpers.USER_PREFERENCES_API_URL);
|
||||
|
||||
AjaxHelpers.respondWithError(requests, 500);
|
||||
Helpers.expectLoadingErrorIsVisible(accountSettingsView, true);
|
||||
});
|
||||
|
||||
it('renders fields after the models are successfully fetched for enterprise learners', function() {
|
||||
var accountSettingsView;
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
accountSettingsView = createEnterpriseLearnerAccountSettingsPage();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);
|
||||
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData());
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.TIME_ZONE_RESPONSE);
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData());
|
||||
|
||||
accountSettingsView.render();
|
||||
|
||||
Helpers.expectLoadingErrorIsVisible(accountSettingsView, false);
|
||||
Helpers.expectSettingsSectionsAndFieldsToBeRenderedWithMessage(accountSettingsView);
|
||||
});
|
||||
|
||||
it('expects all fields to behave correctly for enterprise learners', function() {
|
||||
var accountSettingsView, i, view, sectionsData, textFields, dropdownFields;
|
||||
requests = AjaxHelpers.requests(this);
|
||||
|
||||
accountSettingsView = createEnterpriseLearnerAccountSettingsPage();
|
||||
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData());
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.TIME_ZONE_RESPONSE);
|
||||
AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData());
|
||||
AjaxHelpers.respondWithJson(requests, {}); // Page viewed analytics event
|
||||
|
||||
sectionsData = accountSettingsView.options.tabSections.aboutTabSections;
|
||||
|
||||
expect(sectionsData[accountInfoTab.BASIC_ACCOUNT_INFORMATION].fields.length).toBe(7);
|
||||
|
||||
// Verify that username, name and email fields are readonly
|
||||
textFields = [
|
||||
sectionsData[accountInfoTab.BASIC_ACCOUNT_INFORMATION].fields[basicAccountInfoFields.USERNAME],
|
||||
sectionsData[accountInfoTab.BASIC_ACCOUNT_INFORMATION].fields[basicAccountInfoFields.FULL_NAME],
|
||||
sectionsData[accountInfoTab.BASIC_ACCOUNT_INFORMATION].fields[basicAccountInfoFields.EMAIL_ADDRESS]
|
||||
];
|
||||
for (i = 0; i < textFields.length; i++) {
|
||||
view = textFields[i].view;
|
||||
|
||||
FieldViewsSpecHelpers.verifyReadonlyTextField(view, {
|
||||
title: view.options.title,
|
||||
valueAttribute: view.options.valueAttribute,
|
||||
helpMessage: view.options.helpMessage,
|
||||
validValue: 'My Name',
|
||||
defaultValue: ''
|
||||
}, requests);
|
||||
}
|
||||
|
||||
// Verify un-editable country dropdown field
|
||||
view = sectionsData[
|
||||
accountInfoTab.BASIC_ACCOUNT_INFORMATION
|
||||
].fields[basicAccountInfoFields.COUNTRY].view;
|
||||
|
||||
FieldViewsSpecHelpers.verifyReadonlyDropDownField(view, {
|
||||
title: view.options.title,
|
||||
valueAttribute: view.options.valueAttribute,
|
||||
helpMessage: '',
|
||||
validValue: Helpers.FIELD_OPTIONS[1][0],
|
||||
editable: 'never',
|
||||
defaultValue: null
|
||||
});
|
||||
|
||||
expect(sectionsData[accountInfoTab.ADDITIONAL_INFORMATION].fields.length).toBe(4);
|
||||
dropdownFields = [
|
||||
sectionsData[accountInfoTab.ADDITIONAL_INFORMATION].fields[additionalInfoFields.EDUCATION],
|
||||
sectionsData[accountInfoTab.ADDITIONAL_INFORMATION].fields[additionalInfoFields.GENDER],
|
||||
sectionsData[accountInfoTab.ADDITIONAL_INFORMATION].fields[additionalInfoFields.YEAR_OF_BIRTH]
|
||||
];
|
||||
_.each(dropdownFields, function(field) {
|
||||
view = field.view;
|
||||
FieldViewsSpecHelpers.verifyDropDownField(view, {
|
||||
title: view.options.title,
|
||||
valueAttribute: view.options.valueAttribute,
|
||||
helpMessage: '',
|
||||
validValue: Helpers.FIELD_OPTIONS[1][0], // dummy option for dropdown field
|
||||
invalidValue1: Helpers.FIELD_OPTIONS[2][0], // dummy option for dropdown field
|
||||
invalidValue2: Helpers.FIELD_OPTIONS[3][0], // dummy option for dropdown field
|
||||
validationError: 'Nope, this will not do!',
|
||||
defaultValue: null
|
||||
}, requests);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -20,6 +20,9 @@ define(['backbone',
|
||||
var aboutSectionsData = [
|
||||
{
|
||||
title: 'Basic Account Information',
|
||||
messageType: 'info',
|
||||
message: 'Your profile settings are managed by Test Enterprise. ' +
|
||||
'Contact your administrator or <a href="https://support.edx.org/">edX Support</a> for help.',
|
||||
fields: [
|
||||
{
|
||||
view: new FieldViews.ReadonlyFieldView({
|
||||
|
||||
@@ -10,6 +10,14 @@ define(['underscore'], function(_) {
|
||||
var PASSWORD_RESET_SUPPORT_LINK = 'https://support.edx.org/hc/en-us/articles/206212088-What-if-I-did-not-receive-a-password-reset-message-'; // eslint-disable-line max-len
|
||||
var PLATFORM_NAME = 'edX';
|
||||
var CONTACT_EMAIL = 'info@example.com';
|
||||
|
||||
var SYNC_LEARNER_PROFILE_DATA = true;
|
||||
var ENTERPRISE_NAME = 'Test Enterprise';
|
||||
var ENTERPRISE_READ_ONLY_ACCOUNT_FIELDS = {
|
||||
fields: ['username', 'name', 'email', 'country']
|
||||
};
|
||||
var EDX_SUPPORT_URL = 'https://support.edx.org/';
|
||||
|
||||
var PROFILE_IMAGE = {
|
||||
image_url_large: '/media/profile-images/image.jpg',
|
||||
has_image: true
|
||||
@@ -167,6 +175,36 @@ define(['underscore'], function(_) {
|
||||
});
|
||||
};
|
||||
|
||||
var expectSettingsSectionsAndFieldsToBeRenderedWithMessage = function(accountSettingsView, fieldsAreRendered) {
|
||||
var sectionFieldElements;
|
||||
var sectionsData = accountSettingsView.options.tabSections.aboutTabSections;
|
||||
|
||||
var sectionElements = accountSettingsView.$('#aboutTabSections-tabpanel .section');
|
||||
expect(sectionElements.length).toBe(sectionsData.length);
|
||||
|
||||
_.each(sectionElements, function(sectionElement, sectionIndex) {
|
||||
expect($(sectionElement).find('.section-header').text()
|
||||
.trim()).toBe(sectionsData[sectionIndex].title);
|
||||
|
||||
if (!_.isUndefined(sectionsData[sectionIndex].message)) {
|
||||
expect($(sectionElement).find('.account-settings-section-message span').html()
|
||||
.trim()).toBe(String(sectionsData[sectionIndex].message));
|
||||
}
|
||||
|
||||
sectionFieldElements = $(sectionElement).find('.u-field');
|
||||
|
||||
if (fieldsAreRendered === false) {
|
||||
expect(sectionFieldElements.length).toBe(0);
|
||||
} else {
|
||||
expect(sectionFieldElements.length).toBe(sectionsData[sectionIndex].fields.length);
|
||||
|
||||
_.each(sectionFieldElements, function(sectionFieldElement, fieldIndex) {
|
||||
expectElementContainsField(sectionFieldElement, sectionsData[sectionIndex].fields[fieldIndex]);
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
var expectSettingsSectionsButNotFieldsToBeRendered = function(accountSettingsView) {
|
||||
expectSettingsSectionsAndFieldsToBeRendered(accountSettingsView, false);
|
||||
};
|
||||
@@ -181,6 +219,12 @@ define(['underscore'], function(_) {
|
||||
PASSWORD_RESET_SUPPORT_LINK: PASSWORD_RESET_SUPPORT_LINK,
|
||||
PLATFORM_NAME: PLATFORM_NAME,
|
||||
CONTACT_EMAIL: CONTACT_EMAIL,
|
||||
|
||||
SYNC_LEARNER_PROFILE_DATA: SYNC_LEARNER_PROFILE_DATA,
|
||||
ENTERPRISE_NAME: ENTERPRISE_NAME,
|
||||
ENTERPRISE_READ_ONLY_ACCOUNT_FIELDS: ENTERPRISE_READ_ONLY_ACCOUNT_FIELDS,
|
||||
EDX_SUPPORT_URL: EDX_SUPPORT_URL,
|
||||
|
||||
PROFILE_IMAGE: PROFILE_IMAGE,
|
||||
FIELD_OPTIONS: FIELD_OPTIONS,
|
||||
TIME_ZONE_RESPONSE: TIME_ZONE_RESPONSE,
|
||||
@@ -195,6 +239,7 @@ define(['underscore'], function(_) {
|
||||
expectLoadingErrorIsVisible: expectLoadingErrorIsVisible,
|
||||
expectElementContainsField: expectElementContainsField,
|
||||
expectSettingsSectionsButNotFieldsToBeRendered: expectSettingsSectionsButNotFieldsToBeRendered,
|
||||
expectSettingsSectionsAndFieldsToBeRendered: expectSettingsSectionsAndFieldsToBeRendered
|
||||
expectSettingsSectionsAndFieldsToBeRendered: expectSettingsSectionsAndFieldsToBeRendered,
|
||||
expectSettingsSectionsAndFieldsToBeRenderedWithMessage: expectSettingsSectionsAndFieldsToBeRenderedWithMessage
|
||||
};
|
||||
});
|
||||
|
||||
@@ -244,6 +244,28 @@ define(['backbone',
|
||||
}
|
||||
};
|
||||
|
||||
var verifyReadonlyField = function(view, data) {
|
||||
if (data.editable === 'toggle') {
|
||||
expect(view.el).toHaveClass('mode-placeholder');
|
||||
expectTitleToContain(view, data.title);
|
||||
expectMessageContains(view, view.indicators.canEdit);
|
||||
view.$el.click();
|
||||
} else {
|
||||
expectTitleAndMessageToContain(view, data.title, data.helpMessage);
|
||||
}
|
||||
expect(view.el).toHaveClass('u-field-readonly');
|
||||
|
||||
if (view.fieldValue() !== null) {
|
||||
expect(view.fieldValue()).not.toContain(data.validValue);
|
||||
}
|
||||
};
|
||||
|
||||
var verifyUneditableDropdownField = function(view, data) {
|
||||
expectTitleAndMessageToContain(view, data.title, data.helpMessage);
|
||||
expect(view.el).toHaveClass('u-field-dropdown');
|
||||
expect(view.el).toHaveClass('editable-never');
|
||||
};
|
||||
|
||||
var verifyTextField = function(view, data, requests) {
|
||||
verifyEditableField(view, _.extend({
|
||||
valueSelector: '.u-field-value',
|
||||
@@ -252,6 +274,12 @@ define(['backbone',
|
||||
requests);
|
||||
};
|
||||
|
||||
var verifyReadonlyTextField = function(view, data) {
|
||||
verifyReadonlyField(view, _.extend({
|
||||
valueSelector: '.u-field-value'
|
||||
}, data));
|
||||
};
|
||||
|
||||
var verifyDropDownField = function(view, data, requests) {
|
||||
verifyEditableField(view, _.extend({
|
||||
valueSelector: '.u-field-value',
|
||||
@@ -260,6 +288,12 @@ define(['backbone',
|
||||
), requests);
|
||||
};
|
||||
|
||||
var verifyReadonlyDropDownField = function(view, data) {
|
||||
verifyUneditableDropdownField(view, _.extend({
|
||||
valueSelector: '.editable-never'
|
||||
}, data));
|
||||
};
|
||||
|
||||
return {
|
||||
SELECT_OPTIONS: SELECT_OPTIONS,
|
||||
UserAccountModel: UserAccountModel,
|
||||
@@ -274,7 +308,9 @@ define(['backbone',
|
||||
verifySuccessMessageReset: verifySuccessMessageReset,
|
||||
verifyEditableField: verifyEditableField,
|
||||
verifyTextField: verifyTextField,
|
||||
verifyReadonlyTextField: verifyReadonlyTextField,
|
||||
verifyDropDownField: verifyDropDownField,
|
||||
verifyReadonlyDropDownField: verifyReadonlyDropDownField,
|
||||
verifyPersistence: verifyPersistence
|
||||
};
|
||||
});
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'edx-ui-toolkit/js/utils/html-utils',
|
||||
'text!templates/student_account/account_settings_section.underscore'
|
||||
], function(gettext, $, _, Backbone, sectionTemplate) {
|
||||
], function(gettext, $, _, Backbone, HtmlUtils, sectionTemplate) {
|
||||
var AccountSectionView = Backbone.View.extend({
|
||||
|
||||
initialize: function(options) {
|
||||
@@ -16,6 +17,7 @@
|
||||
|
||||
render: function() {
|
||||
this.$el.html(_.template(sectionTemplate)({
|
||||
HtmlUtils: HtmlUtils,
|
||||
sections: this.options.sections,
|
||||
tabName: this.options.tabName,
|
||||
tabLabel: this.options.tabLabel
|
||||
|
||||
@@ -21,12 +21,19 @@
|
||||
platformName,
|
||||
contactEmail,
|
||||
allowEmailChange,
|
||||
socialPlatforms
|
||||
socialPlatforms,
|
||||
|
||||
syncLearnerProfileData,
|
||||
enterpriseName,
|
||||
enterpriseReadonlyAccountFields,
|
||||
edxSupportUrl
|
||||
) {
|
||||
var $accountSettingsElement, userAccountModel, userPreferencesModel, aboutSectionsData,
|
||||
accountsSectionData, ordersSectionData, accountSettingsView, showAccountSettingsPage,
|
||||
showLoadingError, orderNumber, getUserField, userFields, timeZoneDropdownField, countryDropdownField,
|
||||
emailFieldView, socialFields, platformData;
|
||||
emailFieldView, socialFields, platformData,
|
||||
aboutSectionMessageType, aboutSectionMessage, fullnameFieldView, countryFieldView,
|
||||
fullNameFieldData, emailFieldData, countryFieldData;
|
||||
|
||||
$accountSettingsElement = $('.wrapper-account-settings');
|
||||
|
||||
@@ -36,30 +43,80 @@
|
||||
userPreferencesModel = new UserPreferencesModel();
|
||||
userPreferencesModel.url = userPreferencesApiUrl;
|
||||
|
||||
if (allowEmailChange) {
|
||||
emailFieldView = {
|
||||
view: new AccountSettingsFieldViews.EmailFieldView({
|
||||
model: userAccountModel,
|
||||
title: gettext('Email Address'),
|
||||
valueAttribute: 'email',
|
||||
helpMessage: StringUtils.interpolate(
|
||||
gettext('The email address you use to sign in. Communications from {platform_name} and your courses are sent to this address.'), // eslint-disable-line max-len
|
||||
{platform_name: platformName}
|
||||
if (syncLearnerProfileData && enterpriseName) {
|
||||
aboutSectionMessageType = 'info';
|
||||
aboutSectionMessage = HtmlUtils.interpolateHtml(
|
||||
gettext('Your profile settings are managed by {enterprise_name}. Contact your administrator or {link_start}edX Support{link_end} for help.'), // eslint-disable-line max-len
|
||||
{
|
||||
enterprise_name: enterpriseName,
|
||||
link_start: HtmlUtils.HTML(
|
||||
StringUtils.interpolate(
|
||||
'<a href="{edx_support_url}">', {
|
||||
edx_support_url: edxSupportUrl
|
||||
}
|
||||
)
|
||||
),
|
||||
persistChanges: true
|
||||
})
|
||||
link_end: HtmlUtils.HTML('</a>')
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
emailFieldData = {
|
||||
model: userAccountModel,
|
||||
title: gettext('Email Address (Sign In)'),
|
||||
valueAttribute: 'email',
|
||||
helpMessage: StringUtils.interpolate(
|
||||
gettext('You receive messages from {platform_name} and course teams at this address.'), // eslint-disable-line max-len
|
||||
{platform_name: platformName}
|
||||
),
|
||||
persistChanges: true
|
||||
};
|
||||
if (!allowEmailChange || (syncLearnerProfileData && enterpriseReadonlyAccountFields.fields.indexOf('email') !== -1)) { // eslint-disable-line max-len
|
||||
emailFieldView = {
|
||||
view: new AccountSettingsFieldViews.ReadonlyFieldView(emailFieldData)
|
||||
};
|
||||
} else {
|
||||
emailFieldView = {
|
||||
view: new AccountSettingsFieldViews.ReadonlyFieldView({
|
||||
model: userAccountModel,
|
||||
title: gettext('Email Address'),
|
||||
valueAttribute: 'email',
|
||||
helpMessage: StringUtils.interpolate(
|
||||
gettext('The email address you use to sign in. Communications from {platform_name} and your courses are sent to this address. To change the email address, please contact {contact_email}.'), // eslint-disable-line max-len
|
||||
{platform_name: platformName, contact_email: contactEmail}
|
||||
)
|
||||
})
|
||||
view: new AccountSettingsFieldViews.EmailFieldView(emailFieldData)
|
||||
};
|
||||
}
|
||||
|
||||
fullNameFieldData = {
|
||||
model: userAccountModel,
|
||||
title: gettext('Full Name'),
|
||||
valueAttribute: 'name',
|
||||
helpMessage: gettext('The name that is used for ID verification and that appears on your certificates.'), // eslint-disable-line max-len,
|
||||
persistChanges: true
|
||||
};
|
||||
if (syncLearnerProfileData && enterpriseReadonlyAccountFields.fields.indexOf('name') !== -1) {
|
||||
fullnameFieldView = {
|
||||
view: new AccountSettingsFieldViews.ReadonlyFieldView(fullNameFieldData)
|
||||
};
|
||||
} else {
|
||||
fullnameFieldView = {
|
||||
view: new AccountSettingsFieldViews.TextFieldView(fullNameFieldData)
|
||||
};
|
||||
}
|
||||
|
||||
countryFieldData = {
|
||||
model: userAccountModel,
|
||||
required: true,
|
||||
title: gettext('Country or Region of Residence'),
|
||||
valueAttribute: 'country',
|
||||
options: fieldsData.country.options,
|
||||
persistChanges: true,
|
||||
helpMessage: gettext('The country or region where you live.')
|
||||
};
|
||||
if (syncLearnerProfileData && enterpriseReadonlyAccountFields.fields.indexOf('country') !== -1) {
|
||||
countryFieldData.editable = 'never';
|
||||
countryFieldView = {
|
||||
view: new AccountSettingsFieldViews.DropdownFieldView(
|
||||
countryFieldData
|
||||
)
|
||||
};
|
||||
} else {
|
||||
countryFieldView = {
|
||||
view: new AccountSettingsFieldViews.DropdownFieldView(countryFieldData)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -67,6 +124,10 @@
|
||||
{
|
||||
title: gettext('Basic Account Information'),
|
||||
subtitle: gettext('These settings include basic information about your account.'),
|
||||
|
||||
messageType: aboutSectionMessageType,
|
||||
message: aboutSectionMessage,
|
||||
|
||||
fields: [
|
||||
{
|
||||
view: new AccountSettingsFieldViews.ReadonlyFieldView({
|
||||
@@ -74,26 +135,12 @@
|
||||
title: gettext('Username'),
|
||||
valueAttribute: 'username',
|
||||
helpMessage: StringUtils.interpolate(
|
||||
gettext('The name that identifies you throughout {platform_name}. You cannot change your username.'), // eslint-disable-line max-len
|
||||
gettext('The name that identifies you on {platform_name}. You cannot change your username.'), // eslint-disable-line max-len
|
||||
{platform_name: platformName}
|
||||
)
|
||||
})
|
||||
},
|
||||
{
|
||||
view: new AccountSettingsFieldViews.TextFieldView({
|
||||
model: userAccountModel,
|
||||
title: gettext('Full Name'),
|
||||
valueAttribute: 'name',
|
||||
helpMessage: HtmlUtils.interpolateHtml(
|
||||
gettext('The name that is used for ID verification and that appears on your certificates. Other learners see your full name if you have selected {bold_start}Full Profile{bold_end} for profile visibility. Make sure to enter your name exactly as it appears on your photo ID, including any non-Roman characters.'), // eslint-disable-line max-len
|
||||
{
|
||||
bold_start: HtmlUtils.HTML('<b>'),
|
||||
bold_end: HtmlUtils.HTML('</b>')
|
||||
}
|
||||
),
|
||||
persistChanges: true
|
||||
})
|
||||
},
|
||||
fullnameFieldView,
|
||||
emailFieldView,
|
||||
{
|
||||
view: new AccountSettingsFieldViews.PasswordFieldView({
|
||||
@@ -105,10 +152,7 @@
|
||||
passwordResetSupportUrl: passwordResetSupportUrl,
|
||||
linkTitle: gettext('Reset Your Password'),
|
||||
linkHref: fieldsData.password.url,
|
||||
helpMessage: StringUtils.interpolate(
|
||||
gettext('When you select "Reset Your Password", a message will be sent to the email address for your {platform_name} account. Click the link in the message to reset your password.'), // eslint-disable-line max-len
|
||||
{platform_name: platformName}
|
||||
)
|
||||
helpMessage: gettext('Check your email account for instructions to reset your password.') // eslint-disable-line max-len
|
||||
})
|
||||
},
|
||||
{
|
||||
@@ -126,17 +170,7 @@
|
||||
persistChanges: true
|
||||
})
|
||||
},
|
||||
{
|
||||
view: new AccountSettingsFieldViews.DropdownFieldView({
|
||||
model: userAccountModel,
|
||||
required: true,
|
||||
title: gettext('Country or Region of Residence'),
|
||||
valueAttribute: 'country',
|
||||
options: fieldsData.country.options,
|
||||
persistChanges: true,
|
||||
helpMessage: gettext('The country or region where you live.')
|
||||
})
|
||||
},
|
||||
countryFieldView,
|
||||
{
|
||||
view: new AccountSettingsFieldViews.TimeZoneFieldView({
|
||||
model: userPreferencesModel,
|
||||
|
||||
@@ -78,7 +78,7 @@
|
||||
|
||||
@include appearance(none);
|
||||
|
||||
display:block;
|
||||
display: block;
|
||||
padding: ($baseline/4);
|
||||
|
||||
&:hover,
|
||||
@@ -101,7 +101,7 @@
|
||||
border-bottom: none;
|
||||
|
||||
.account-nav-link {
|
||||
border-bottom: 4px solid theme-color("light");
|
||||
border-bottom: 4px solid theme-color("light");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -130,6 +130,59 @@
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
|
||||
.account-settings-section-message {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
margin-top: 15px;
|
||||
margin-bottom: 30px;
|
||||
|
||||
.alert-message {
|
||||
color: #292b2c;
|
||||
font-family: $font-family-sans-serif;
|
||||
position: relative;
|
||||
padding: 10px 10px 10px 35px;
|
||||
border: 1px solid transparent;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
margin-bottom: 8px;
|
||||
|
||||
& > .fa {
|
||||
position: absolute;
|
||||
left: 11px;
|
||||
top: 13px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
span {
|
||||
display: block;
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.success {
|
||||
background-color: #ecfaec;
|
||||
border-color: #b9edb9;
|
||||
}
|
||||
|
||||
.info {
|
||||
background-color: #d8edf8;
|
||||
border-color: #bbdff2;
|
||||
}
|
||||
|
||||
.warning {
|
||||
background-color: #fcf8e3;
|
||||
border-color: #faebcc;
|
||||
}
|
||||
|
||||
.error {
|
||||
background-color: #f2dede;
|
||||
border-color: #ebccd1;
|
||||
}
|
||||
}
|
||||
|
||||
.account-settings-section-body {
|
||||
.u-field {
|
||||
border-bottom: 2px solid $m-gray-l4;
|
||||
@@ -176,6 +229,7 @@
|
||||
font-size: 1rem;
|
||||
line-height: 1;
|
||||
color: $dark-gray;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.field-input {
|
||||
@@ -219,8 +273,7 @@
|
||||
padding-top: $baseline;
|
||||
padding-bottom: $baseline;
|
||||
line-height: normal;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
flex-flow: row wrap;
|
||||
|
||||
span {
|
||||
padding: $baseline;
|
||||
@@ -301,7 +354,9 @@
|
||||
font-weight: $font-semibold;
|
||||
padding: 0;
|
||||
|
||||
&:focus, &:hover, &:active {
|
||||
&:focus,
|
||||
&:hover,
|
||||
&:active {
|
||||
background-color: transparent;
|
||||
color: $m-blue-d3;
|
||||
border: none;
|
||||
@@ -372,10 +427,10 @@
|
||||
display: flex;
|
||||
flex-wrap: nowrap;
|
||||
|
||||
u-field-order-number,
|
||||
u-field-order-date,
|
||||
u-field-order-price,
|
||||
u-field-order-link, {
|
||||
.u-field-order-number,
|
||||
.u-field-order-date,
|
||||
.u-field-order-price,
|
||||
.u-field-order-link {
|
||||
width: auto;
|
||||
float: none;
|
||||
flex-grow: 1;
|
||||
@@ -388,8 +443,43 @@
|
||||
}
|
||||
}
|
||||
|
||||
.u-field-readonly .u-field-value {
|
||||
.u-field {
|
||||
&.u-field-dropdown, &.editable-never &.mode-display {
|
||||
.u-field-value {
|
||||
margin-bottom: ($baseline);
|
||||
|
||||
.u-field-title {
|
||||
font-size: 16px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 18px;
|
||||
}
|
||||
|
||||
.u-field-value-readonly {
|
||||
font-size: 22px;
|
||||
color: #636c72;
|
||||
line-height: 30px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.u-field-readonly .u-field-title {
|
||||
font-size: 16px;
|
||||
color: #636c72;
|
||||
line-height: 22px;
|
||||
padding-top: ($baseline/2);
|
||||
padding-bottom: 0;
|
||||
margin-bottom: 8px !important;
|
||||
}
|
||||
|
||||
.u-field-readonly .u-field-value {
|
||||
font-size: 22px;
|
||||
color: #636c72;
|
||||
line-height: 30px;
|
||||
padding-top: 8px;
|
||||
padding-bottom: ($baseline);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.u-field-orderHistory {
|
||||
@@ -402,7 +492,8 @@
|
||||
border-bottom: 1px solid $m-gray-l4;
|
||||
}
|
||||
|
||||
&:hover, &:focus {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: $light-gray4;
|
||||
}
|
||||
}
|
||||
@@ -413,7 +504,8 @@
|
||||
margin-bottom: 0;
|
||||
padding-bottom: 0;
|
||||
|
||||
&:hover, &:focus {
|
||||
&:hover,
|
||||
&:focus {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
@@ -38,7 +38,12 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
|
||||
platformName = '${ static.get_platform_name() | n, js_escaped_string }',
|
||||
contactEmail = '${ static.get_contact_email_address() | n, js_escaped_string }',
|
||||
allowEmailChange = ${ bool(settings.FEATURES['ALLOW_EMAIL_ADDRESS_CHANGE']) | n, dump_js_escaped_json },
|
||||
socialPlatforms = ${ settings.SOCIAL_PLATFORMS | n, dump_js_escaped_json };
|
||||
socialPlatforms = ${ settings.SOCIAL_PLATFORMS | n, dump_js_escaped_json },
|
||||
|
||||
syncLearnerProfileData = ${ bool(sync_learner_profile_data) | n, dump_js_escaped_json },
|
||||
enterpriseName = '${ enterprise_name | n, js_escaped_string }',
|
||||
enterpriseReadonlyAccountFields = ${ enterprise_readonly_account_fields | n, dump_js_escaped_json },
|
||||
edxSupportUrl = '${ edx_support_url | n, js_escaped_string }';
|
||||
|
||||
AccountSettingsFactory(
|
||||
fieldsData,
|
||||
@@ -51,7 +56,12 @@ from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_str
|
||||
platformName,
|
||||
contactEmail,
|
||||
allowEmailChange,
|
||||
socialPlatforms
|
||||
socialPlatforms,
|
||||
|
||||
syncLearnerProfileData,
|
||||
enterpriseName,
|
||||
enterpriseReadonlyAccountFields,
|
||||
edxSupportUrl
|
||||
);
|
||||
</%static:require_module>
|
||||
</%block>
|
||||
|
||||
@@ -4,9 +4,19 @@
|
||||
<% _.each(sections, function(section) { %>
|
||||
<div class="section">
|
||||
<h3 class="section-header"><%- gettext(section.title) %></h3>
|
||||
<% if (section.subtitle) { %>
|
||||
<% if (section.subtitle && _.isUndefined(section.message)) { %>
|
||||
<p class="account-settings-header-subtitle"><%- section.subtitle %></p>
|
||||
<% } %>
|
||||
|
||||
<% if (section.message) { %>
|
||||
<div class="account-settings-section-message">
|
||||
<div class="alert-message <%- section.messageType%>" aria-live="polite">
|
||||
<i class="fa fa-info-circle message-icon <%- section.messageType %>" aria-hidden="true"></i>
|
||||
<span><%= HtmlUtils.ensureHtml(section.message) %></span>
|
||||
</div>
|
||||
</div>
|
||||
<% } %>
|
||||
|
||||
<div class="account-settings-section-body <%- tabName %>-section-body">
|
||||
<div class="ui-loading-error is-hidden">
|
||||
<span class="fa fa-exclamation-triangle message-error" aria-hidden="true"></span>
|
||||
|
||||
Reference in New Issue
Block a user