From 5daaae0c8c7b0305b2b9b71c6fe52c2032e3fd73 Mon Sep 17 00:00:00 2001 From: Deborah Kaplan Date: Fri, 31 Jan 2025 21:49:09 +0000 Subject: [PATCH 1/3] feat!: remove legacy accounts, profile, order history **DRAFT** This work is in progress, tests are failing, in no way is this ready to go. FIXES: APER-3884 --- .../third_party_auth/tests/specs/base.py | 44 +-- .../tests/specs/test_testshib.py | 4 - lms/static/learner_profile | 1 - lms/templates/header/user_dropdown.html | 3 +- .../user_api/accounts/settings_views.py | 300 ------------------ .../user_api/accounts/tests/test_filters.py | 3 +- .../accounts/tests/test_settings_views.py | 287 ----------------- .../djangoapps/user_api/accounts/toggles.py | 44 --- .../core/djangoapps/user_api/legacy_urls.py | 2 - .../tests/views/test_learner_profile.py | 36 +-- openedx/features/learner_profile/toggles.py | 29 -- .../learner_profile/views/learner_profile.py | 20 +- 12 files changed, 25 insertions(+), 748 deletions(-) delete mode 120000 lms/static/learner_profile delete mode 100644 openedx/core/djangoapps/user_api/accounts/settings_views.py delete mode 100644 openedx/core/djangoapps/user_api/accounts/tests/test_settings_views.py delete mode 100644 openedx/core/djangoapps/user_api/accounts/toggles.py delete mode 100644 openedx/features/learner_profile/toggles.py diff --git a/common/djangoapps/third_party_auth/tests/specs/base.py b/common/djangoapps/third_party_auth/tests/specs/base.py index 8f96235017da..323dc38e87b6 100644 --- a/common/djangoapps/third_party_auth/tests/specs/base.py +++ b/common/djangoapps/third_party_auth/tests/specs/base.py @@ -28,7 +28,6 @@ from openedx.core.djangoapps.user_authn.views.register import RegistrationView from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory -from openedx.core.djangoapps.user_api.accounts.settings_views import account_settings_context from common.djangoapps.student import models as student_models from common.djangoapps.student.tests.factories import UserFactory @@ -611,7 +610,6 @@ def test_full_pipeline_succeeds_for_linking_account(self, _mock_segment_track): # First we expect that we're in the unlinked state, and that there # really is no association in the backend. - self.assert_account_settings_context_looks_correct(account_settings_context(get_request), linked=False) self.assert_social_auth_does_not_exist_for_user(get_request.user, strategy) # We should be redirected back to the complete page, setting @@ -630,7 +628,6 @@ def test_full_pipeline_succeeds_for_linking_account(self, _mock_segment_track): # Now we expect to be in the linked state, with a backend entry. self.assert_social_auth_exists_for_user(get_request.user, strategy) - self.assert_account_settings_context_looks_correct(account_settings_context(get_request), linked=True) def test_full_pipeline_succeeds_for_unlinking_account(self): # First, create, the GET request and strategy that store pipeline state, @@ -662,7 +659,6 @@ def test_full_pipeline_succeeds_for_unlinking_account(self): get_request.user = post_request.user # First we expect that we're in the linked state, with a backend entry. - self.assert_account_settings_context_looks_correct(account_settings_context(get_request), linked=True) self.assert_social_auth_exists_for_user(get_request.user, strategy) # Fire off the disconnect pipeline to unlink. @@ -676,7 +672,6 @@ def test_full_pipeline_succeeds_for_unlinking_account(self): ) # Now we expect to be in the unlinked state, with no backend entry. - self.assert_account_settings_context_looks_correct(account_settings_context(get_request), linked=False) self.assert_social_auth_does_not_exist_for_user(user, strategy) def test_linking_already_associated_account_raises_auth_already_associated(self): @@ -702,41 +697,6 @@ def test_linking_already_associated_account_raises_auth_already_associated(self) # pylint: disable=protected-access actions.do_complete(backend, social_views._do_login, user=unlinked_user, request=strategy.request) - def test_already_associated_exception_populates_dashboard_with_error(self): - # Instrument the pipeline with an exception. We test that the - # exception is raised correctly separately, so it's ok that we're - # raising it artificially here. This makes the linked=True artificial - # in the final assert because in practice the account would be - # unlinked, but getting that behavior is cumbersome here and already - # covered in other tests. Using linked=True does, however, let us test - # that the duplicate error has no effect on the state of the controls. - get_request, strategy = self.get_request_and_strategy( - auth_entry=pipeline.AUTH_ENTRY_LOGIN, redirect_uri='social:complete') - strategy.request.backend.auth_complete = mock.MagicMock(return_value=self.fake_auth_complete(strategy)) - user = self.create_user_models_for_existing_account( - strategy, 'user@example.com', 'password', self.get_username()) - self.assert_social_auth_exists_for_user(user, strategy) - - self.client.get('/login') - self.client.get(pipeline.get_login_url(self.provider.provider_id, pipeline.AUTH_ENTRY_LOGIN)) - actions.do_complete(get_request.backend, social_views._do_login, # pylint: disable=protected-access - request=get_request) - - post_request = self._get_login_post_request(strategy) - with self._patch_edxmako_current_request(post_request): - login_user(post_request) - actions.do_complete(post_request.backend, social_views._do_login, # pylint: disable=protected-access, no-member - user=user, request=post_request) - - # Monkey-patch storage for messaging; pylint: disable=protected-access - post_request._messages = fallback.FallbackStorage(post_request) - middleware.ExceptionMiddleware(get_response=lambda request: None).process_exception( - post_request, - exceptions.AuthAlreadyAssociated(self.provider.backend_name, 'account is already in use.')) - - self.assert_account_settings_context_looks_correct( - account_settings_context(post_request), duplicate=True, linked=True) - @mock.patch('common.djangoapps.third_party_auth.pipeline.segment.track') def test_full_pipeline_succeeds_for_signing_in_to_existing_active_account(self, _mock_segment_track): # First, create, the GET request and strategy that store pipeline state, @@ -795,7 +755,6 @@ def test_full_pipeline_succeeds_for_signing_in_to_existing_active_account(self, self.assert_redirect_after_pipeline_completes( self.do_complete(strategy, get_request, partial_pipeline_token, partial_data, user) ) - self.assert_account_settings_context_looks_correct(account_settings_context(get_request)) def test_signin_fails_if_account_not_active(self): _, strategy = self.get_request_and_strategy( @@ -935,9 +894,8 @@ def test_full_pipeline_succeeds_registering_new_account(self): self.assert_redirect_after_pipeline_completes( self.do_complete(strategy, request, partial_pipeline_token, partial_data, created_user) ) - # Now the user has been redirected to the dashboard. Their third party account should now be linked. + # Their third party account should now be linked. self.assert_social_auth_exists_for_user(created_user, strategy) - self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=True) def test_new_account_registration_assigns_distinct_username_on_collision(self): original_username = self.get_username() diff --git a/common/djangoapps/third_party_auth/tests/specs/test_testshib.py b/common/djangoapps/third_party_auth/tests/specs/test_testshib.py index ec3efd8286e7..ca03b6fae97a 100644 --- a/common/djangoapps/third_party_auth/tests/specs/test_testshib.py +++ b/common/djangoapps/third_party_auth/tests/specs/test_testshib.py @@ -27,7 +27,6 @@ from common.djangoapps.third_party_auth.saml import log as saml_log from common.djangoapps.third_party_auth.tasks import fetch_saml_metadata from common.djangoapps.third_party_auth.tests import testutil, utils -from openedx.core.djangoapps.user_api.accounts.settings_views import account_settings_context from openedx.core.djangoapps.user_authn.views.login import login_user from openedx.features.enterprise_support.tests.factories import EnterpriseCustomerFactory @@ -239,7 +238,6 @@ class TestShibIntegrationTest(SamlIntegrationTestUtilities, IntegrationTestMixin } @patch('openedx.features.enterprise_support.api.enterprise_customer_for_request') - @patch('openedx.core.djangoapps.user_api.accounts.settings_views.enterprise_customer_for_request') @patch('openedx.features.enterprise_support.utils.third_party_auth.provider.Registry.get') def test_full_pipeline_succeeds_for_unlinking_testshib_account( self, @@ -299,7 +297,6 @@ def test_full_pipeline_succeeds_for_unlinking_testshib_account( request=request) # First we expect that we're in the linked state, with a backend entry. - self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=True) self.assert_social_auth_exists_for_user(request.user, strategy) FEATURES_WITH_ENTERPRISE_ENABLED = settings.FEATURES.copy() @@ -327,7 +324,6 @@ def test_full_pipeline_succeeds_for_unlinking_testshib_account( ) ) # Now we expect to be in the unlinked state, with no backend entry. - self.assert_account_settings_context_looks_correct(account_settings_context(request), linked=False) self.assert_social_auth_does_not_exist_for_user(user, strategy) assert EnterpriseCustomerUser.objects\ .filter(enterprise_customer=enterprise_customer, user_id=user.id).count() == 0 diff --git a/lms/static/learner_profile b/lms/static/learner_profile deleted file mode 120000 index ca7ce1f79785..000000000000 --- a/lms/static/learner_profile +++ /dev/null @@ -1 +0,0 @@ -../../openedx/features/learner_profile/static/learner_profile \ No newline at end of file diff --git a/lms/templates/header/user_dropdown.html b/lms/templates/header/user_dropdown.html index 2e7e168a6937..0e4a0271e17b 100644 --- a/lms/templates/header/user_dropdown.html +++ b/lms/templates/header/user_dropdown.html @@ -10,7 +10,6 @@ from django.utils.translation import gettext as _ from openedx.core.djangoapps.user_api.accounts.image_helpers import get_profile_image_urls_for_user -from openedx.core.djangoapps.user_api.accounts.toggles import should_redirect_to_order_history_microfrontend from openedx.features.enterprise_support.utils import get_enterprise_learner_generic_name, get_enterprise_learner_portal %> @@ -23,7 +22,7 @@ enterprise_customer_portal = get_enterprise_learner_portal(request) ## Enterprises with the learner portal enabled should not show order history, as it does ## not apply to the learner's method of purchasing content. -should_show_order_history = should_redirect_to_order_history_microfrontend() and not enterprise_customer_portal +should_show_order_history = not enterprise_customer_portal %> '); - }); - - it('shows loading error when UserAccountModel fails to load', function() { - requests = AjaxHelpers.requests(this); - - var accountSettingsView = createAccountSettingsPage(); - - Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - - var 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', function() { - requests = AjaxHelpers.requests(this); - - var accountSettingsView = createAccountSettingsPage(); - - Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - - var 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('/api/user/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', function() { - requests = AjaxHelpers.requests(this); - - var accountSettingsView = createAccountSettingsPage(); - - 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.expectSettingsSectionsAndFieldsToBeRendered(accountSettingsView); - }); - - it('expects all fields to behave correctly', function() { - var i, view; - - requests = AjaxHelpers.requests(this); - - var accountSettingsView = createAccountSettingsPage(); - - AjaxHelpers.respondWithJson(requests, Helpers.createAccountSettingsData()); - AjaxHelpers.respondWithJson(requests, Helpers.TIME_ZONE_RESPONSE); - AjaxHelpers.respondWithJson(requests, Helpers.createUserPreferencesData()); - AjaxHelpers.respondWithJson(requests, {}); // Page viewed analytics event - - var sectionsData = accountSettingsView.options.tabSections.aboutTabSections; - - expect(sectionsData[0].fields.length).toBe(7); - - var textFields = [sectionsData[0].fields[1], sectionsData[0].fields[2]]; - for (i = 0; i < textFields.length; i++) { - view = textFields[i].view; - FieldViewsSpecHelpers.verifyTextField(view, { - title: view.options.title, - valueAttribute: view.options.valueAttribute, - helpMessage: view.options.helpMessage, - validValue: 'My Name', - invalidValue1: '', - invalidValue2: '@', - validationError: 'Think again!', - defaultValue: '' - }, requests); - } - - expect(sectionsData[1].fields.length).toBe(4); - var dropdownFields = [ - sectionsData[1].fields[0], - sectionsData[1].fields[1], - sectionsData[1].fields[2] - ]; - _.each(dropdownFields, function(field) { - // eslint-disable-next-line no-shadow - var view = field.view; - FieldViewsSpecHelpers.verifyDropDownField(view, { - title: view.options.title, - valueAttribute: view.options.valueAttribute, - helpMessage: '', - validValue: Helpers.FIELD_OPTIONS[1][0], - invalidValue1: Helpers.FIELD_OPTIONS[2][0], - invalidValue2: Helpers.FIELD_OPTIONS[3][0], - validationError: 'Nope, this will not do!', - defaultValue: null - }, requests); - }); - }); - }); - - describe('edx.user.AccountSettingsFactory', function() { - var createEnterpriseLearnerAccountSettingsPage = function() { - var context = AccountSettingsPage( - Helpers.FIELDS_DATA, - false, - [], - 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.ENABLE_COPPA_COMPLIANCE, - '', - - 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('
'); - }); - - 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('/api/user/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); - }); - }); - }); -}); diff --git a/lms/static/js/spec/student_account/account_settings_fields_helpers.js b/lms/static/js/spec/student_account/account_settings_fields_helpers.js deleted file mode 100644 index 4aea86b235a3..000000000000 --- a/lms/static/js/spec/student_account/account_settings_fields_helpers.js +++ /dev/null @@ -1,34 +0,0 @@ -define(['backbone', - 'jquery', - 'underscore', - 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', - 'common/js/spec_helpers/template_helpers', - 'js/spec/views/fields_helpers', - 'string_utils'], -function(Backbone, $, _, AjaxHelpers, TemplateHelpers, FieldViewsSpecHelpers) { - 'use strict'; - - var verifyAuthField = function(view, data, requests) { - var selector = '.u-field-value .u-field-link-title-' + view.options.valueAttribute; - - spyOn(view, 'redirect_to'); - - FieldViewsSpecHelpers.expectTitleAndMessageToContain(view, data.title, data.helpMessage); - expect(view.$(selector).text().trim()).toBe('Unlink This Account'); - view.$(selector).click(); - FieldViewsSpecHelpers.expectMessageContains(view, 'Unlinking'); - AjaxHelpers.expectRequest(requests, 'POST', data.disconnectUrl); - AjaxHelpers.respondWithNoContent(requests); - - expect(view.$(selector).text().trim()).toBe('Link Your Account'); - FieldViewsSpecHelpers.expectMessageContains(view, 'Successfully unlinked.'); - - view.$(selector).click(); - FieldViewsSpecHelpers.expectMessageContains(view, 'Linking'); - expect(view.redirect_to).toHaveBeenCalledWith(data.connectUrl); - }; - - return { - verifyAuthField: verifyAuthField - }; -}); diff --git a/lms/static/js/spec/student_account/account_settings_fields_spec.js b/lms/static/js/spec/student_account/account_settings_fields_spec.js deleted file mode 100644 index 76ea7c512b7f..000000000000 --- a/lms/static/js/spec/student_account/account_settings_fields_spec.js +++ /dev/null @@ -1,216 +0,0 @@ -define(['backbone', - 'jquery', - 'underscore', - 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', - 'common/js/spec_helpers/template_helpers', - 'js/student_account/models/user_account_model', - 'js/views/fields', - 'js/spec/views/fields_helpers', - 'js/spec/student_account/account_settings_fields_helpers', - 'js/student_account/views/account_settings_fields', - 'js/student_account/models/user_account_model', - 'string_utils'], -function(Backbone, $, _, AjaxHelpers, TemplateHelpers, UserAccountModel, FieldViews, FieldViewsSpecHelpers, - AccountSettingsFieldViewSpecHelpers, AccountSettingsFieldViews) { - 'use strict'; - - describe('edx.AccountSettingsFieldViews', function() { - var requests, - timerCallback, // eslint-disable-line no-unused-vars - data; - - beforeEach(function() { - timerCallback = jasmine.createSpy('timerCallback'); - jasmine.clock().install(); - }); - - afterEach(function() { - jasmine.clock().uninstall(); - }); - - it('sends request to reset password on clicking link in PasswordFieldView', function() { - requests = AjaxHelpers.requests(this); - - var fieldData = FieldViewsSpecHelpers.createFieldData(AccountSettingsFieldViews.PasswordFieldView, { - linkHref: '/password_reset', - emailAttribute: 'email', - valueAttribute: 'password' - }); - - var view = new AccountSettingsFieldViews.PasswordFieldView(fieldData).render(); - expect(view.$('.u-field-value > button').is(':disabled')).toBe(false); - view.$('.u-field-value > button').click(); - expect(view.$('.u-field-value > button').is(':disabled')).toBe(true); - AjaxHelpers.expectRequest(requests, 'POST', '/password_reset', 'email=legolas%40woodland.middlearth'); - AjaxHelpers.respondWithJson(requests, {success: 'true'}); - FieldViewsSpecHelpers.expectMessageContains( - view, - "We've sent a message to legolas@woodland.middlearth. " - + 'Click the link in the message to reset your password.' - ); - }); - - it('update time zone dropdown after country dropdown changes', function() { - var baseSelector = '.u-field-value > select'; - var groupsSelector = baseSelector + '> optgroup'; - var groupOptionsSelector = groupsSelector + '> option'; - - var timeZoneData = FieldViewsSpecHelpers.createFieldData(AccountSettingsFieldViews.TimeZoneFieldView, { - valueAttribute: 'time_zone', - groupOptions: [{ - groupTitle: gettext('All Time Zones'), - selectOptions: FieldViewsSpecHelpers.SELECT_OPTIONS, - nullValueOptionLabel: 'Default (Local Time Zone)' - }], - persistChanges: true, - required: true - }); - var countryData = FieldViewsSpecHelpers.createFieldData(AccountSettingsFieldViews.DropdownFieldView, { - valueAttribute: 'country', - options: [['KY', 'Cayman Islands'], ['CA', 'Canada'], ['GY', 'Guyana']], - persistChanges: true - }); - - var countryChange = {country: 'GY'}; - var timeZoneChange = {time_zone: 'Pacific/Kosrae'}; - - var timeZoneView = new AccountSettingsFieldViews.TimeZoneFieldView(timeZoneData).render(); - var countryView = new AccountSettingsFieldViews.DropdownFieldView(countryData).render(); - - requests = AjaxHelpers.requests(this); - - timeZoneView.listenToCountryView(countryView); - - // expect time zone dropdown to have single subheader ('All Time Zones') - expect(timeZoneView.$(groupsSelector).length).toBe(1); - expect(timeZoneView.$(groupOptionsSelector).length).toBe(3); - expect(timeZoneView.$(groupOptionsSelector)[0].value).toBe(FieldViewsSpecHelpers.SELECT_OPTIONS[0][0]); - - // change country - countryView.$(baseSelector).val(countryChange[countryData.valueAttribute]).change(); - countryView.$(baseSelector).focusout(); - FieldViewsSpecHelpers.expectAjaxRequestWithData(requests, countryChange); - AjaxHelpers.respondWithJson(requests, {success: 'true'}); - - AjaxHelpers.expectRequest( - requests, - 'GET', - '/api/user/v1/preferences/time_zones/?country_code=GY' - ); - AjaxHelpers.respondWithJson(requests, [ - {time_zone: 'America/Guyana', description: 'America/Guyana (ECT, UTC-0500)'}, - {time_zone: 'Pacific/Kosrae', description: 'Pacific/Kosrae (KOST, UTC+1100)'} - ]); - - // expect time zone dropdown to have two subheaders (country/all time zone sub-headers) with new values - expect(timeZoneView.$(groupsSelector).length).toBe(2); - expect(timeZoneView.$(groupOptionsSelector).length).toBe(6); - expect(timeZoneView.$(groupOptionsSelector)[0].value).toBe('America/Guyana'); - - // select time zone option from option - timeZoneView.$(baseSelector).val(timeZoneChange[timeZoneData.valueAttribute]).change(); - timeZoneView.$(baseSelector).focusout(); - FieldViewsSpecHelpers.expectAjaxRequestWithData(requests, timeZoneChange); - AjaxHelpers.respondWithJson(requests, {success: 'true'}); - timeZoneView.render(); - - // expect time zone dropdown to have three subheaders (currently selected/country/all time zones) - expect(timeZoneView.$(groupsSelector).length).toBe(3); - expect(timeZoneView.$(groupOptionsSelector).length).toBe(6); - expect(timeZoneView.$(groupOptionsSelector)[0].value).toBe('Pacific/Kosrae'); - }); - - it('sends request to /i18n/setlang/ after changing language in LanguagePreferenceFieldView', function() { - requests = AjaxHelpers.requests(this); - - var selector = '.u-field-value > select'; - var fieldData = FieldViewsSpecHelpers.createFieldData(AccountSettingsFieldViews.DropdownFieldView, { - valueAttribute: 'language', - options: FieldViewsSpecHelpers.SELECT_OPTIONS, - persistChanges: true - }); - - var view = new AccountSettingsFieldViews.LanguagePreferenceFieldView(fieldData).render(); - - data = {language: FieldViewsSpecHelpers.SELECT_OPTIONS[2][0]}; - view.$(selector).val(data[fieldData.valueAttribute]).change(); - view.$(selector).focusout(); - FieldViewsSpecHelpers.expectAjaxRequestWithData(requests, data); - AjaxHelpers.respondWithNoContent(requests); - - AjaxHelpers.expectRequest( - requests, - 'POST', - '/i18n/setlang/', - $.param({ - language: data[fieldData.valueAttribute], - next: window.location.href - }) - ); - // Django will actually respond with a 302 redirect, but that would cause a page load during these - // unittests. 204 should work fine for testing. - AjaxHelpers.respondWithNoContent(requests); - FieldViewsSpecHelpers.expectMessageContains(view, 'Your changes have been saved.'); - - data = {language: FieldViewsSpecHelpers.SELECT_OPTIONS[1][0]}; - view.$(selector).val(data[fieldData.valueAttribute]).change(); - view.$(selector).focusout(); - FieldViewsSpecHelpers.expectAjaxRequestWithData(requests, data); - AjaxHelpers.respondWithNoContent(requests); - - AjaxHelpers.expectRequest( - requests, - 'POST', - '/i18n/setlang/', - $.param({ - language: data[fieldData.valueAttribute], - next: window.location.href - }) - ); - AjaxHelpers.respondWithError(requests, 500); - FieldViewsSpecHelpers.expectMessageContains( - view, - 'You must sign out and sign back in before your language changes take effect.' - ); - }); - - it('reads and saves the value correctly for LanguageProficienciesFieldView', function() { - requests = AjaxHelpers.requests(this); - - var selector = '.u-field-value > select'; - var fieldData = FieldViewsSpecHelpers.createFieldData(AccountSettingsFieldViews.DropdownFieldView, { - valueAttribute: 'language_proficiencies', - options: FieldViewsSpecHelpers.SELECT_OPTIONS, - persistChanges: true - }); - fieldData.model.set({language_proficiencies: [{code: FieldViewsSpecHelpers.SELECT_OPTIONS[0][0]}]}); - - var view = new AccountSettingsFieldViews.LanguageProficienciesFieldView(fieldData).render(); - - expect(view.modelValue()).toBe(FieldViewsSpecHelpers.SELECT_OPTIONS[0][0]); - - data = {language_proficiencies: [{code: FieldViewsSpecHelpers.SELECT_OPTIONS[1][0]}]}; - view.$(selector).val(FieldViewsSpecHelpers.SELECT_OPTIONS[1][0]).change(); - view.$(selector).focusout(); - FieldViewsSpecHelpers.expectAjaxRequestWithData(requests, data); - AjaxHelpers.respondWithNoContent(requests); - }); - - it('correctly links and unlinks from AuthFieldView', function() { - requests = AjaxHelpers.requests(this); - - var fieldData = FieldViewsSpecHelpers.createFieldData(FieldViews.LinkFieldView, { - title: 'Yet another social network', - helpMessage: '', - valueAttribute: 'auth-yet-another', - connected: true, - acceptsLogins: 'true', - connectUrl: 'yetanother.com/auth/connect', - disconnectUrl: 'yetanother.com/auth/disconnect' - }); - var view = new AccountSettingsFieldViews.AuthFieldView(fieldData).render(); - - AccountSettingsFieldViewSpecHelpers.verifyAuthField(view, fieldData, requests); - }); - }); -}); diff --git a/lms/static/js/spec/student_account/account_settings_view_spec.js b/lms/static/js/spec/student_account/account_settings_view_spec.js deleted file mode 100644 index c0c213cf3c5c..000000000000 --- a/lms/static/js/spec/student_account/account_settings_view_spec.js +++ /dev/null @@ -1,91 +0,0 @@ -define(['backbone', - 'jquery', - 'underscore', - 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', - 'common/js/spec_helpers/template_helpers', - 'js/spec/student_account/helpers', - 'js/views/fields', - 'js/student_account/models/user_account_model', - 'js/student_account/views/account_settings_view' -], -function(Backbone, $, _, AjaxHelpers, TemplateHelpers, Helpers, FieldViews, UserAccountModel, - AccountSettingsView) { - 'use strict'; - - describe('edx.user.AccountSettingsView', function() { - var createAccountSettingsView = function() { - var model = new UserAccountModel(); - model.set(Helpers.createAccountSettingsData()); - - var aboutSectionsData = [ - { - title: 'Basic Account Information', - messageType: 'info', - message: 'Your profile settings are managed by Test Enterprise. ' - + 'Contact your administrator or edX Support for help.', - fields: [ - { - view: new FieldViews.ReadonlyFieldView({ - model: model, - title: 'Username', - valueAttribute: 'username' - }) - }, - { - view: new FieldViews.TextFieldView({ - model: model, - title: 'Full Name', - valueAttribute: 'name' - }) - } - ] - }, - { - title: 'Additional Information', - fields: [ - { - view: new FieldViews.DropdownFieldView({ - model: model, - title: 'Education Completed', - valueAttribute: 'level_of_education', - options: Helpers.FIELD_OPTIONS - }) - } - ] - } - ]; - - var accountSettingsView = new AccountSettingsView({ - el: $('.wrapper-account-settings'), - model: model, - tabSections: { - aboutTabSections: aboutSectionsData - } - }); - - return accountSettingsView; - }; - - beforeEach(function() { - setFixtures('
'); - }); - - it('shows loading error correctly', function() { - var accountSettingsView = createAccountSettingsView(); - - accountSettingsView.render(); - Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - - accountSettingsView.showLoadingError(); - Helpers.expectLoadingErrorIsVisible(accountSettingsView, true); - }); - - it('renders all fields as expected', function() { - var accountSettingsView = createAccountSettingsView(); - - accountSettingsView.render(); - Helpers.expectLoadingErrorIsVisible(accountSettingsView, false); - Helpers.expectSettingsSectionsAndFieldsToBeRendered(accountSettingsView); - }); - }); -}); diff --git a/lms/static/js/student_account/views/account_section_view.js b/lms/static/js/student_account/views/account_section_view.js deleted file mode 100644 index 70cd217477a4..000000000000 --- a/lms/static/js/student_account/views/account_section_view.js +++ /dev/null @@ -1,48 +0,0 @@ -// eslint-disable-next-line no-shadow-restricted-names -(function(define, undefined) { - 'use strict'; - - define([ - 'gettext', - 'jquery', - 'underscore', - 'backbone', - 'edx-ui-toolkit/js/utils/html-utils', - 'text!templates/student_account/account_settings_section.underscore' - ], function(gettext, $, _, Backbone, HtmlUtils, sectionTemplate) { - var AccountSectionView = Backbone.View.extend({ - - initialize: function(options) { - this.options = options; - _.bindAll(this, 'render', 'renderFields'); - }, - - render: function() { - HtmlUtils.setHtml( - this.$el, - HtmlUtils.template(sectionTemplate)({ - HtmlUtils: HtmlUtils, - sections: this.options.sections, - tabName: this.options.tabName, - tabLabel: this.options.tabLabel - }) - ); - - this.renderFields(); - }, - - renderFields: function() { - var view = this; - - _.each(view.$('.' + view.options.tabName + '-section-body'), function(sectionEl, index) { - _.each(view.options.sections[index].fields, function(field) { - $(sectionEl).append(field.view.render().el); - }); - }); - return this; - } - }); - - return AccountSectionView; - }); -}).call(this, define || RequireJS.define); diff --git a/lms/static/js/student_account/views/account_settings_factory.js b/lms/static/js/student_account/views/account_settings_factory.js deleted file mode 100644 index 70d3ad205c10..000000000000 --- a/lms/static/js/student_account/views/account_settings_factory.js +++ /dev/null @@ -1,495 +0,0 @@ -// eslint-disable-next-line no-shadow-restricted-names -(function(define, undefined) { - 'use strict'; - - define([ - 'gettext', 'jquery', 'underscore', 'backbone', 'logger', - 'js/student_account/models/user_account_model', - 'js/student_account/models/user_preferences_model', - 'js/student_account/views/account_settings_fields', - 'js/student_account/views/account_settings_view', - 'edx-ui-toolkit/js/utils/string-utils', - 'edx-ui-toolkit/js/utils/html-utils' - ], function(gettext, $, _, Backbone, Logger, UserAccountModel, UserPreferencesModel, - AccountSettingsFieldViews, AccountSettingsView, StringUtils, HtmlUtils) { - return function( - fieldsData, - disableOrderHistoryTab, - ordersHistoryData, - authData, - passwordResetSupportUrl, - userAccountsApiUrl, - userPreferencesApiUrl, - accountUserId, - platformName, - contactEmail, - allowEmailChange, - enableCoppaCompliance, - socialPlatforms, - syncLearnerProfileData, - enterpriseName, - enterpriseReadonlyAccountFields, - edxSupportUrl, - extendedProfileFields, - displayAccountDeletion, - isSecondaryEmailFeatureEnabled, - betaLanguage - ) { - var $accountSettingsElement, userAccountModel, userPreferencesModel, aboutSectionsData, - accountsSectionData, ordersSectionData, accountSettingsView, showAccountSettingsPage, - showLoadingError, orderNumber, getUserField, userFields, timeZoneDropdownField, countryDropdownField, - emailFieldView, secondaryEmailFieldView, socialFields, accountDeletionFields, platformData, - aboutSectionMessageType, aboutSectionMessage, fullnameFieldView, countryFieldView, - fullNameFieldData, emailFieldData, secondaryEmailFieldData, countryFieldData, additionalFields, - fieldItem, emailFieldViewIndex, focusId, yearOfBirthViewIndex, levelOfEducationFieldData, - tabIndex = 0; - - $accountSettingsElement = $('.wrapper-account-settings'); - - userAccountModel = new UserAccountModel(); - userAccountModel.url = userAccountsApiUrl; - - userPreferencesModel = new UserPreferencesModel(); - userPreferencesModel.url = userPreferencesApiUrl; - - 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( - '', { - edx_support_url: edxSupportUrl - } - ) - ), - link_end: HtmlUtils.HTML('') - } - ); - } - - 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.EmailFieldView(emailFieldData) - }; - } - - secondaryEmailFieldData = { - model: userAccountModel, - title: gettext('Recovery Email Address'), - valueAttribute: 'secondary_email', - helpMessage: gettext('You may access your account with this address if single-sign on or access to your primary email is not available.'), // eslint-disable-line max-len - persistChanges: true - }; - - 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) - }; - } - - levelOfEducationFieldData = fieldsData.level_of_education.options; - if (enableCoppaCompliance) { - levelOfEducationFieldData = levelOfEducationFieldData.filter(option => option[0] !== 'el'); - } - - aboutSectionsData = [ - { - title: gettext('Basic Account Information'), - subtitle: gettext('These settings include basic information about your account.'), - - messageType: aboutSectionMessageType, - message: aboutSectionMessage, - - fields: [ - { - view: new AccountSettingsFieldViews.ReadonlyFieldView({ - model: userAccountModel, - title: gettext('Username'), - valueAttribute: 'username', - helpMessage: StringUtils.interpolate( - gettext('The name that identifies you on {platform_name}. You cannot change your username.'), // eslint-disable-line max-len - {platform_name: platformName} - ) - }) - }, - fullnameFieldView, - emailFieldView, - { - view: new AccountSettingsFieldViews.PasswordFieldView({ - model: userAccountModel, - title: gettext('Password'), - screenReaderTitle: gettext('Reset Your Password'), - valueAttribute: 'password', - emailAttribute: 'email', - passwordResetSupportUrl: passwordResetSupportUrl, - linkTitle: gettext('Reset Your Password'), - linkHref: fieldsData.password.url, - helpMessage: gettext('Check your email account for instructions to reset your password.') // eslint-disable-line max-len - }) - }, - { - view: new AccountSettingsFieldViews.LanguagePreferenceFieldView({ - model: userPreferencesModel, - title: gettext('Language'), - valueAttribute: 'pref-lang', - required: true, - refreshPageOnSave: true, - helpMessage: StringUtils.interpolate( - gettext('The language used throughout this site. This site is currently available in a limited number of languages. Changing the value of this field will cause the page to refresh.'), // eslint-disable-line max-len - {platform_name: platformName} - ), - options: fieldsData.language.options, - persistChanges: true, - focusNextID: '#u-field-select-country' - }) - }, - countryFieldView, - { - view: new AccountSettingsFieldViews.TimeZoneFieldView({ - model: userPreferencesModel, - required: true, - title: gettext('Time Zone'), - valueAttribute: 'time_zone', - helpMessage: gettext('Select the time zone for displaying course dates. If you do not specify a time zone, course dates, including assignment deadlines, will be displayed in your browser\'s local time zone.'), // eslint-disable-line max-len - groupOptions: [{ - groupTitle: gettext('All Time Zones'), - selectOptions: fieldsData.time_zone.options, - nullValueOptionLabel: gettext('Default (Local Time Zone)') - }], - persistChanges: true - }) - } - ] - }, - { - title: gettext('Additional Information'), - fields: [ - { - view: new AccountSettingsFieldViews.DropdownFieldView({ - model: userAccountModel, - title: gettext('Education Completed'), - valueAttribute: 'level_of_education', - options: levelOfEducationFieldData, - persistChanges: true - }) - }, - { - view: new AccountSettingsFieldViews.DropdownFieldView({ - model: userAccountModel, - title: gettext('Gender'), - valueAttribute: 'gender', - options: fieldsData.gender.options, - persistChanges: true - }) - }, - { - view: new AccountSettingsFieldViews.DropdownFieldView({ - model: userAccountModel, - title: gettext('Year of Birth'), - valueAttribute: 'year_of_birth', - options: fieldsData.year_of_birth.options, - persistChanges: true - }) - }, - { - view: new AccountSettingsFieldViews.LanguageProficienciesFieldView({ - model: userAccountModel, - title: gettext('Preferred Language'), - valueAttribute: 'language_proficiencies', - options: fieldsData.preferred_language.options, - persistChanges: true - }) - } - ] - } - ]; - - if (enableCoppaCompliance) { - yearOfBirthViewIndex = aboutSectionsData[1].fields.findIndex(function(field) { - return field.view.options.valueAttribute === 'year_of_birth'; - }); - aboutSectionsData[1].fields.splice(yearOfBirthViewIndex, 1); - } - - // Secondary email address - if (isSecondaryEmailFeatureEnabled) { - secondaryEmailFieldView = { - view: new AccountSettingsFieldViews.EmailFieldView(secondaryEmailFieldData), - successMessage: function() { - return HtmlUtils.joinHtml( - this.indicators.success, - StringUtils.interpolate( - gettext('We\'ve sent a confirmation message to {new_secondary_email_address}. Click the link in the message to update your secondary email address.'), // eslint-disable-line max-len - { - new_secondary_email_address: this.fieldValue() - } - ) - ); - } - }; - emailFieldViewIndex = aboutSectionsData[0].fields.indexOf(emailFieldView); - - // Insert secondary email address after email address field. - aboutSectionsData[0].fields.splice( - emailFieldViewIndex + 1, 0, secondaryEmailFieldView - ); - } - - // Add the extended profile fields - additionalFields = aboutSectionsData[1]; - for (var field in extendedProfileFields) { // eslint-disable-line guard-for-in, no-restricted-syntax, vars-on-top, max-len - fieldItem = extendedProfileFields[field]; - if (fieldItem.field_type === 'TextField') { - additionalFields.fields.push({ - view: new AccountSettingsFieldViews.ExtendedFieldTextFieldView({ - model: userAccountModel, - title: fieldItem.field_label, - fieldName: fieldItem.field_name, - valueAttribute: 'extended_profile', - persistChanges: true - }) - }); - } else { - if (fieldItem.field_type === 'ListField') { - additionalFields.fields.push({ - view: new AccountSettingsFieldViews.ExtendedFieldListFieldView({ - model: userAccountModel, - title: fieldItem.field_label, - fieldName: fieldItem.field_name, - options: fieldItem.field_options, - valueAttribute: 'extended_profile', - persistChanges: true - }) - }); - } - } - } - - // Add the social link fields - socialFields = { - title: gettext('Social Media Links'), - subtitle: gettext('Optionally, link your personal accounts to the social media icons on your edX profile.'), // eslint-disable-line max-len - fields: [] - }; - - for (var socialPlatform in socialPlatforms) { // eslint-disable-line guard-for-in, no-restricted-syntax, vars-on-top, max-len - platformData = socialPlatforms[socialPlatform]; - socialFields.fields.push( - { - view: new AccountSettingsFieldViews.SocialLinkTextFieldView({ - model: userAccountModel, - title: StringUtils.interpolate( - gettext('{platform_display_name} Link'), - {platform_display_name: platformData.display_name} - ), - valueAttribute: 'social_links', - helpMessage: StringUtils.interpolate( - gettext('Enter your {platform_display_name} username or the URL to your {platform_display_name} page. Delete the URL to remove the link.'), // eslint-disable-line max-len - {platform_display_name: platformData.display_name} - ), - platform: socialPlatform, - persistChanges: true, - placeholder: platformData.example - }) - } - ); - } - aboutSectionsData.push(socialFields); - - // Add account deletion fields - if (displayAccountDeletion) { - accountDeletionFields = { - title: gettext('Delete My Account'), - fields: [], - // Used so content can be rendered external to Backbone - domHookId: 'account-deletion-container' - }; - aboutSectionsData.push(accountDeletionFields); - } - - // set TimeZoneField to listen to CountryField - - getUserField = function(list, search) { - // eslint-disable-next-line no-shadow - return _.find(list, function(field) { - return field.view.options.valueAttribute === search; - }).view; - }; - userFields = _.find(aboutSectionsData, function(section) { - return section.title === gettext('Basic Account Information'); - }).fields; - timeZoneDropdownField = getUserField(userFields, 'time_zone'); - countryDropdownField = getUserField(userFields, 'country'); - timeZoneDropdownField.listenToCountryView(countryDropdownField); - - accountsSectionData = [ - { - title: gettext('Linked Accounts'), - subtitle: StringUtils.interpolate( - gettext('You can link your social media accounts to simplify signing in to {platform_name}.'), - {platform_name: platformName} - ), - fields: _.map(authData.providers, function(provider) { - return { - view: new AccountSettingsFieldViews.AuthFieldView({ - title: provider.name, - valueAttribute: 'auth-' + provider.id, - helpMessage: '', - connected: provider.connected, - connectUrl: provider.connect_url, - acceptsLogins: provider.accepts_logins, - disconnectUrl: provider.disconnect_url, - platformName: platformName - }) - }; - }) - } - ]; - - ordersHistoryData.unshift( - { - title: gettext('ORDER NAME'), - order_date: gettext('ORDER PLACED'), - price: gettext('TOTAL'), - number: gettext('ORDER NUMBER') - } - ); - - ordersSectionData = [ - { - title: gettext('My Orders'), - subtitle: StringUtils.interpolate( - gettext('This page contains information about orders that you have placed with {platform_name}.'), // eslint-disable-line max-len - {platform_name: platformName} - ), - fields: _.map(ordersHistoryData, function(order) { - orderNumber = order.number; - if (orderNumber === 'ORDER NUMBER') { - orderNumber = 'orderId'; - } - return { - view: new AccountSettingsFieldViews.OrderHistoryFieldView({ - totalPrice: order.price, - orderId: order.number, - orderDate: order.order_date, - receiptUrl: order.receipt_url, - valueAttribute: 'order-' + orderNumber, - lines: order.lines - }) - }; - }) - } - ]; - - accountSettingsView = new AccountSettingsView({ - model: userAccountModel, - accountUserId: accountUserId, - el: $accountSettingsElement, - tabSections: { - aboutTabSections: aboutSectionsData, - accountsTabSections: accountsSectionData, - ordersTabSections: ordersSectionData - }, - userPreferencesModel: userPreferencesModel, - disableOrderHistoryTab: disableOrderHistoryTab, - betaLanguage: betaLanguage - }); - - accountSettingsView.render(); - focusId = $.cookie('focus_id'); - if (focusId) { - // eslint-disable-next-line no-bitwise - if (~focusId.indexOf('beta-language')) { - tabIndex = -1; - - // Scroll to top of selected element - $('html, body').animate({ - scrollTop: $(focusId).offset().top - }, 'slow'); - } - $(focusId).attr({tabindex: tabIndex}).focus(); - // Deleting the cookie - document.cookie = 'focus_id=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/account;'; - } - showAccountSettingsPage = function() { - // Record that the account settings page was viewed. - Logger.log('edx.user.settings.viewed', { - page: 'account', - visibility: null, - user_id: accountUserId - }); - }; - - showLoadingError = function() { - accountSettingsView.showLoadingError(); - }; - - userAccountModel.fetch({ - success: function() { - // Fetch the user preferences model - userPreferencesModel.fetch({ - success: showAccountSettingsPage, - error: showLoadingError - }); - }, - error: showLoadingError - }); - - return { - userAccountModel: userAccountModel, - userPreferencesModel: userPreferencesModel, - accountSettingsView: accountSettingsView - }; - }; - }); -}).call(this, define || RequireJS.define); diff --git a/lms/static/js/student_account/views/account_settings_fields.js b/lms/static/js/student_account/views/account_settings_fields.js deleted file mode 100644 index 1fc174f93588..000000000000 --- a/lms/static/js/student_account/views/account_settings_fields.js +++ /dev/null @@ -1,466 +0,0 @@ -// eslint-disable-next-line no-shadow-restricted-names -(function(define, undefined) { - 'use strict'; - - define([ - 'gettext', - 'jquery', - 'underscore', - 'backbone', - 'js/views/fields', - 'text!templates/fields/field_text_account.underscore', - 'text!templates/fields/field_readonly_account.underscore', - 'text!templates/fields/field_link_account.underscore', - 'text!templates/fields/field_dropdown_account.underscore', - 'text!templates/fields/field_social_link_account.underscore', - 'text!templates/fields/field_order_history.underscore', - 'edx-ui-toolkit/js/utils/string-utils', - 'edx-ui-toolkit/js/utils/html-utils' - ], function( - gettext, $, _, Backbone, - FieldViews, - field_text_account_template, - field_readonly_account_template, - field_link_account_template, - field_dropdown_account_template, - field_social_link_template, - field_order_history_template, - StringUtils, - HtmlUtils - ) { - var AccountSettingsFieldViews = { - ReadonlyFieldView: FieldViews.ReadonlyFieldView.extend({ - fieldTemplate: field_readonly_account_template - }), - TextFieldView: FieldViews.TextFieldView.extend({ - fieldTemplate: field_text_account_template - }), - DropdownFieldView: FieldViews.DropdownFieldView.extend({ - fieldTemplate: field_dropdown_account_template - }), - EmailFieldView: FieldViews.TextFieldView.extend({ - fieldTemplate: field_text_account_template, - successMessage: function() { - return HtmlUtils.joinHtml( - this.indicators.success, - StringUtils.interpolate( - gettext('We\'ve sent a confirmation message to {new_email_address}. Click the link in the message to update your email address.'), // eslint-disable-line max-len - {new_email_address: this.fieldValue()} - ) - ); - } - }), - LanguagePreferenceFieldView: FieldViews.DropdownFieldView.extend({ - fieldTemplate: field_dropdown_account_template, - - initialize: function(options) { - this._super(options); // eslint-disable-line no-underscore-dangle - this.listenTo(this.model, 'revertValue', this.revertValue); - }, - - revertValue: function(event) { - var attributes = {}, - oldPrefLang = $(event.target).data('old-lang-code'); - - if (oldPrefLang) { - attributes['pref-lang'] = oldPrefLang; - this.saveAttributes(attributes); - } - }, - - saveSucceeded: function() { - var data = { - language: this.modelValue(), - next: window.location.href - }; - - var view = this; - $.ajax({ - type: 'POST', - url: '/i18n/setlang/', - data: data, - dataType: 'html', - success: function() { - view.showSuccessMessage(); - }, - error: function() { - view.showNotificationMessage( - HtmlUtils.joinHtml( - view.indicators.error, - gettext('You must sign out and sign back in before your language changes take effect.') // eslint-disable-line max-len - ) - ); - } - }); - } - - }), - TimeZoneFieldView: FieldViews.DropdownFieldView.extend({ - fieldTemplate: field_dropdown_account_template, - - initialize: function(options) { - this.options = _.extend({}, options); - _.bindAll(this, 'listenToCountryView', 'updateCountrySubheader', 'replaceOrAddGroupOption'); - this._super(options); // eslint-disable-line no-underscore-dangle - }, - - listenToCountryView: function(view) { - this.listenTo(view.model, 'change:country', this.updateCountrySubheader); - }, - - updateCountrySubheader: function(user) { - var view = this; - $.ajax({ - type: 'GET', - url: '/api/user/v1/preferences/time_zones/', - data: {country_code: user.attributes.country}, - success: function(data) { - var countryTimeZones = $.map(data, function(timeZoneInfo) { - return [[timeZoneInfo.time_zone, timeZoneInfo.description]]; - }); - view.replaceOrAddGroupOption( - 'Country Time Zones', - countryTimeZones - ); - view.render(); - } - }); - }, - - updateValueInField: function() { - var options; - if (this.modelValue()) { - options = [[this.modelValue(), this.displayValue(this.modelValue())]]; - this.replaceOrAddGroupOption( - 'Currently Selected Time Zone', - options - ); - } - this._super(); // eslint-disable-line no-underscore-dangle - }, - - replaceOrAddGroupOption: function(title, options) { - var groupOption = { - groupTitle: gettext(title), - selectOptions: options - }; - - var index = _.findIndex(this.options.groupOptions, function(group) { - return group.groupTitle === gettext(title); - }); - if (index >= 0) { - this.options.groupOptions[index] = groupOption; - } else { - this.options.groupOptions.unshift(groupOption); - } - } - - }), - PasswordFieldView: FieldViews.LinkFieldView.extend({ - fieldType: 'button', - fieldTemplate: field_link_account_template, - events: { - 'click button': 'linkClicked' - }, - initialize: function(options) { - this.options = _.extend({}, options); - this._super(options); - _.bindAll(this, 'resetPassword'); - }, - linkClicked: function(event) { - event.preventDefault(); - this.toggleDisableButton(true); - this.resetPassword(event); - }, - resetPassword: function() { - var data = {}; - data[this.options.emailAttribute] = this.model.get(this.options.emailAttribute); - - var view = this; - $.ajax({ - type: 'POST', - url: view.options.linkHref, - data: data, - success: function() { - view.showSuccessMessage(); - view.setMessageTimeout(); - }, - error: function(xhr) { - view.showErrorMessage(xhr); - view.setMessageTimeout(); - view.toggleDisableButton(false); - } - }); - }, - toggleDisableButton: function(disabled) { - var button = this.$('#u-field-link-' + this.options.valueAttribute); - if (button) { - button.prop('disabled', disabled); - } - }, - setMessageTimeout: function() { - var view = this; - setTimeout(function() { - view.showHelpMessage(); - }, 6000); - }, - successMessage: function() { - return HtmlUtils.joinHtml( - this.indicators.success, - HtmlUtils.interpolateHtml( - gettext('We\'ve sent a message to {email}. Click the link in the message to reset your password. Didn\'t receive the message? Contact {anchorStart}technical support{anchorEnd}.'), // eslint-disable-line max-len - { - email: this.model.get(this.options.emailAttribute), - anchorStart: HtmlUtils.HTML( - StringUtils.interpolate( - '', { - passwordResetSupportUrl: this.options.passwordResetSupportUrl - } - ) - ), - anchorEnd: HtmlUtils.HTML('') - } - ) - ); - } - }), - LanguageProficienciesFieldView: FieldViews.DropdownFieldView.extend({ - fieldTemplate: field_dropdown_account_template, - modelValue: function() { - var modelValue = this.model.get(this.options.valueAttribute); - if (_.isArray(modelValue) && modelValue.length > 0) { - return modelValue[0].code; - } else { - return null; - } - }, - saveValue: function() { - var attributes = {}, - value = ''; - if (this.persistChanges === true) { - value = this.fieldValue() ? [{code: this.fieldValue()}] : []; - attributes[this.options.valueAttribute] = value; - this.saveAttributes(attributes); - } - } - }), - SocialLinkTextFieldView: FieldViews.TextFieldView.extend({ - render: function() { - HtmlUtils.setHtml(this.$el, HtmlUtils.template(field_text_account_template)({ - id: this.options.valueAttribute + '_' + this.options.platform, - title: this.options.title, - value: this.modelValue(), - message: this.options.helpMessage, - placeholder: this.options.placeholder || '' - })); - this.delegateEvents(); - return this; - }, - - modelValue: function() { - var socialLinks = this.model.get(this.options.valueAttribute); - for (var i = 0; i < socialLinks.length; i++) { // eslint-disable-line vars-on-top - if (socialLinks[i].platform === this.options.platform) { - return socialLinks[i].social_link; - } - } - return null; - }, - saveValue: function() { - var attributes, value; - if (this.persistChanges === true) { - attributes = {}; - value = this.fieldValue() != null ? [{ - platform: this.options.platform, - social_link: this.fieldValue() - }] : []; - attributes[this.options.valueAttribute] = value; - this.saveAttributes(attributes); - } - } - }), - ExtendedFieldTextFieldView: FieldViews.TextFieldView.extend({ - render: function() { - HtmlUtils.setHtml(this.$el, HtmlUtils.template(field_text_account_template)({ - id: this.options.valueAttribute + '_' + this.options.field_name, - title: this.options.title, - value: this.modelValue(), - message: this.options.helpMessage, - placeholder: this.options.placeholder || '' - })); - this.delegateEvents(); - return this; - }, - - modelValue: function() { - var extendedProfileFields = this.model.get(this.options.valueAttribute); - for (var i = 0; i < extendedProfileFields.length; i++) { // eslint-disable-line vars-on-top - if (extendedProfileFields[i].field_name === this.options.fieldName) { - return extendedProfileFields[i].field_value; - } - } - return null; - }, - saveValue: function() { - var attributes, value; - if (this.persistChanges === true) { - attributes = {}; - value = this.fieldValue() != null ? [{ - field_name: this.options.fieldName, - field_value: this.fieldValue() - }] : []; - attributes[this.options.valueAttribute] = value; - this.saveAttributes(attributes); - } - } - }), - ExtendedFieldListFieldView: FieldViews.DropdownFieldView.extend({ - fieldTemplate: field_dropdown_account_template, - modelValue: function() { - var extendedProfileFields = this.model.get(this.options.valueAttribute); - for (var i = 0; i < extendedProfileFields.length; i++) { // eslint-disable-line vars-on-top - if (extendedProfileFields[i].field_name === this.options.fieldName) { - return extendedProfileFields[i].field_value; - } - } - return null; - }, - saveValue: function() { - var attributes = {}, - value; - if (this.persistChanges === true) { - value = this.fieldValue() ? [{ - field_name: this.options.fieldName, - field_value: this.fieldValue() - }] : []; - attributes[this.options.valueAttribute] = value; - this.saveAttributes(attributes); - } - } - }), - AuthFieldView: FieldViews.LinkFieldView.extend({ - fieldTemplate: field_social_link_template, - className: function() { - return 'u-field u-field-social u-field-' + this.options.valueAttribute; - }, - initialize: function(options) { - this.options = _.extend({}, options); - this._super(options); - _.bindAll(this, 'redirect_to', 'disconnect', 'successMessage', 'inProgressMessage'); - }, - render: function() { - var linkTitle = '', - linkClass = '', - subTitle = '', - screenReaderTitle = StringUtils.interpolate( - gettext('Link your {accountName} account'), - {accountName: this.options.title} - ); - if (this.options.connected) { - linkTitle = gettext('Unlink This Account'); - linkClass = 'social-field-linked'; - subTitle = StringUtils.interpolate( - gettext('You can use your {accountName} account to sign in to your {platformName} account.'), // eslint-disable-line max-len - {accountName: this.options.title, platformName: this.options.platformName} - ); - screenReaderTitle = StringUtils.interpolate( - gettext('Unlink your {accountName} account'), - {accountName: this.options.title} - ); - } else if (this.options.acceptsLogins) { - linkTitle = gettext('Link Your Account'); - linkClass = 'social-field-unlinked'; - subTitle = StringUtils.interpolate( - gettext('Link your {accountName} account to your {platformName} account and use {accountName} to sign in to {platformName}.'), // eslint-disable-line max-len - {accountName: this.options.title, platformName: this.options.platformName} - ); - } - - HtmlUtils.setHtml(this.$el, HtmlUtils.template(this.fieldTemplate)({ - id: this.options.valueAttribute, - title: this.options.title, - screenReaderTitle: screenReaderTitle, - linkTitle: linkTitle, - subTitle: subTitle, - linkClass: linkClass, - linkHref: '#', - message: this.helpMessage - })); - this.delegateEvents(); - return this; - }, - linkClicked: function(event) { - event.preventDefault(); - - this.showInProgressMessage(); - - if (this.options.connected) { - this.disconnect(); - } else { - // Direct the user to the providers site to start the authentication process. - // See python-social-auth docs for more information. - this.redirect_to(this.options.connectUrl); - } - }, - redirect_to: function(url) { - window.location.href = url; - }, - disconnect: function() { - var data = {}; - - // Disconnects the provider from the user's edX account. - // See python-social-auth docs for more information. - var view = this; - $.ajax({ - type: 'POST', - url: this.options.disconnectUrl, - data: data, - dataType: 'html', - success: function() { - view.options.connected = false; - view.render(); - view.showSuccessMessage(); - }, - error: function(xhr) { - view.showErrorMessage(xhr); - } - }); - }, - inProgressMessage: function() { - return HtmlUtils.joinHtml(this.indicators.inProgress, ( - this.options.connected ? gettext('Unlinking') : gettext('Linking') - )); - }, - successMessage: function() { - return HtmlUtils.joinHtml(this.indicators.success, gettext('Successfully unlinked.')); - } - }), - - OrderHistoryFieldView: FieldViews.ReadonlyFieldView.extend({ - fieldType: 'orderHistory', - fieldTemplate: field_order_history_template, - - initialize: function(options) { - this.options = options; - this._super(options); - this.template = HtmlUtils.template(this.fieldTemplate); - }, - - render: function() { - HtmlUtils.setHtml(this.$el, this.template({ - totalPrice: this.options.totalPrice, - orderId: this.options.orderId, - orderDate: this.options.orderDate, - receiptUrl: this.options.receiptUrl, - valueAttribute: this.options.valueAttribute, - lines: this.options.lines - })); - this.delegateEvents(); - return this; - } - }) - }; - - return AccountSettingsFieldViews; - }); -}).call(this, define || RequireJS.define); diff --git a/lms/static/js/student_account/views/account_settings_view.js b/lms/static/js/student_account/views/account_settings_view.js deleted file mode 100644 index 6ee9c9101d6c..000000000000 --- a/lms/static/js/student_account/views/account_settings_view.js +++ /dev/null @@ -1,157 +0,0 @@ -// eslint-disable-next-line no-shadow-restricted-names -(function(define, undefined) { - 'use strict'; - - define([ - 'gettext', - 'jquery', - 'underscore', - 'common/js/components/views/tabbed_view', - 'edx-ui-toolkit/js/utils/html-utils', - 'js/student_account/views/account_section_view', - 'text!templates/student_account/account_settings.underscore' - ], function(gettext, $, _, TabbedView, HtmlUtils, AccountSectionView, accountSettingsTemplate) { - var AccountSettingsView = TabbedView.extend({ - - navLink: '.account-nav-link', - activeTab: 'aboutTabSections', - events: { - 'click .account-nav-link': 'switchTab', - 'keydown .account-nav-link': 'keydownHandler', - 'click .btn-alert-primary': 'revertValue' - }, - - initialize: function(options) { - this.options = options; - _.bindAll(this, 'render', 'switchTab', 'setActiveTab', 'showLoadingError'); - }, - - render: function() { - var tabName, betaLangMessage, helpTranslateText, helpTranslateLink, betaLangCode, oldLangCode, - view = this; - var accountSettingsTabs = [ - { - name: 'aboutTabSections', - id: 'about-tab', - label: gettext('Account Information'), - class: 'active', - tabindex: 0, - selected: true, - expanded: true - }, - { - name: 'accountsTabSections', - id: 'accounts-tab', - label: gettext('Linked Accounts'), - tabindex: -1, - selected: false, - expanded: false - } - ]; - if (!view.options.disableOrderHistoryTab) { - accountSettingsTabs.push({ - name: 'ordersTabSections', - id: 'orders-tab', - label: gettext('Order History'), - tabindex: -1, - selected: false, - expanded: false - }); - } - - if (!_.isEmpty(view.options.betaLanguage) && $.cookie('old-pref-lang')) { - betaLangMessage = HtmlUtils.interpolateHtml( - gettext('You have set your language to {beta_language}, which is currently not fully translated. You can help us translate this language fully by joining the Transifex community and adding translations from English for learners that speak {beta_language}.'), // eslint-disable-line max-len - { - beta_language: view.options.betaLanguage.name - } - ); - helpTranslateText = HtmlUtils.interpolateHtml( - gettext('Help Translate into {beta_language}'), - { - beta_language: view.options.betaLanguage.name - } - ); - betaLangCode = this.options.betaLanguage.code.split('-'); - if (betaLangCode.length > 1) { - betaLangCode = betaLangCode[0] + '_' + betaLangCode[1].toUpperCase(); - } else { - betaLangCode = betaLangCode[0]; - } - helpTranslateLink = 'https://www.transifex.com/open-edx/edx-platform/translate/#' + betaLangCode; - oldLangCode = $.cookie('old-pref-lang'); - // Deleting the cookie - document.cookie = 'old-pref-lang=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/account;'; - - $.cookie('focus_id', '#beta-language-message'); - } - HtmlUtils.setHtml(this.$el, HtmlUtils.template(accountSettingsTemplate)({ - accountSettingsTabs: accountSettingsTabs, - HtmlUtils: HtmlUtils, - message: betaLangMessage, - helpTranslateText: helpTranslateText, - helpTranslateLink: helpTranslateLink, - oldLangCode: oldLangCode - })); - _.each(accountSettingsTabs, function(tab) { - tabName = tab.name; - view.renderSection(view.options.tabSections[tabName], tabName, tab.label); - }); - return this; - }, - - switchTab: function(e) { - var $currentTab, - $accountNavLink = $('.account-nav-link'); - - if (e) { - e.preventDefault(); - $currentTab = $(e.target); - this.activeTab = $currentTab.data('name'); - - _.each(this.$('.account-settings-tabpanels'), function(tabPanel) { - $(tabPanel).addClass('hidden'); - }); - - $('#' + this.activeTab + '-tabpanel').removeClass('hidden'); - - $accountNavLink.attr('tabindex', -1); - $accountNavLink.attr('aria-selected', false); - $accountNavLink.attr('aria-expanded', false); - - $currentTab.attr('tabindex', 0); - $currentTab.attr('aria-selected', true); - $currentTab.attr('aria-expanded', true); - - $(this.navLink).removeClass('active'); - $currentTab.addClass('active'); - } - }, - - setActiveTab: function() { - this.switchTab(); - }, - - renderSection: function(tabSections, tabName, tabLabel) { - var accountSectionView = new AccountSectionView({ - tabName: tabName, - tabLabel: tabLabel, - sections: tabSections, - el: '#' + tabName + '-tabpanel' - }); - - accountSectionView.render(); - }, - - showLoadingError: function() { - this.$('.ui-loading-error').removeClass('is-hidden'); - }, - - revertValue: function(event) { - this.options.userPreferencesModel.trigger('revertValue', event); - } - }); - - return AccountSettingsView; - }); -}).call(this, define || RequireJS.define); diff --git a/lms/static/lms/js/build.js b/lms/static/lms/js/build.js index c22f366c5d2b..1d5e1a983be0 100644 --- a/lms/static/lms/js/build.js +++ b/lms/static/lms/js/build.js @@ -33,10 +33,8 @@ 'js/discussions_management/views/discussions_dashboard_factory', 'js/header_factory', 'js/student_account/logistration_factory', - 'js/student_account/views/account_settings_factory', 'js/student_account/views/finish_auth_factory', 'js/views/message_banner', - 'learner_profile/js/learner_profile_factory', 'lms/js/preview/preview_factory', 'support/js/certificates_factory', 'support/js/enrollment_factory', diff --git a/lms/static/lms/js/spec/main.js b/lms/static/lms/js/spec/main.js index cd8668ddc443..4b6b0d9ec64a 100644 --- a/lms/static/lms/js/spec/main.js +++ b/lms/static/lms/js/spec/main.js @@ -761,9 +761,6 @@ 'js/spec/shoppingcart/shoppingcart_spec.js', 'js/spec/staff_debug_actions_spec.js', 'js/spec/student_account/access_spec.js', - 'js/spec/student_account/account_settings_factory_spec.js', - 'js/spec/student_account/account_settings_fields_spec.js', - 'js/spec/student_account/account_settings_view_spec.js', 'js/spec/student_account/emailoptin_spec.js', 'js/spec/student_account/enrollment_spec.js', 'js/spec/student_account/finish_auth_spec.js', @@ -787,10 +784,6 @@ 'js/spec/views/file_uploader_spec.js', 'js/spec/views/message_banner_spec.js', 'js/spec/views/notification_spec.js', - 'learner_profile/js/spec/learner_profile_factory_spec.js', - 'learner_profile/js/spec/views/learner_profile_fields_spec.js', - 'learner_profile/js/spec/views/learner_profile_view_spec.js', - 'learner_profile/js/spec/views/section_two_tab_spec.js', 'support/js/spec/collections/enrollment_spec.js', 'support/js/spec/models/enrollment_spec.js', 'support/js/spec/views/certificates_spec.js', diff --git a/lms/static/sass/_build-lms-v1.scss b/lms/static/sass/_build-lms-v1.scss index 7a77eb34ca07..90c5077c1f38 100644 --- a/lms/static/sass/_build-lms-v1.scss +++ b/lms/static/sass/_build-lms-v1.scss @@ -52,7 +52,6 @@ @import 'multicourse/survey-page'; // base - specific views -@import 'views/account-settings'; @import 'views/course-entitlements'; @import 'views/login-register'; @import 'views/verification'; @@ -68,7 +67,6 @@ // features @import 'features/bookmarks-v1'; -@import 'features/learner-profile'; @import 'features/_unsupported-browser-alert'; @import 'features/content-type-gating'; @import 'features/course-duration-limits'; diff --git a/lms/static/sass/features/_learner-profile.scss b/lms/static/sass/features/_learner-profile.scss deleted file mode 100644 index 8d35a7eccc67..000000000000 --- a/lms/static/sass/features/_learner-profile.scss +++ /dev/null @@ -1,875 +0,0 @@ -// lms - application - learner profile -// ==================== - -.learner-achievements { - .learner-message { - @extend %no-content; - - margin: $baseline*0.75 0; - - .message-header, - .message-actions { - text-align: center; - } - - .message-actions { - margin-top: $baseline/2; - - .btn-brand { - color: $white; - } - } - } -} - -.certificate-card { - display: flex; - flex-direction: row; - margin-bottom: $baseline; - padding: $baseline/2; - border: 1px; - border-style: solid; - background-color: $white; - cursor: pointer; - - &:hover { - box-shadow: 0 0 1px 1px $gray-l2; - } - - .card-logo { - @include margin-right($baseline); - - width: 100px; - height: 100px; - - @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap - display: none; - } - } - - .card-content { - color: $body-color; - margin-top: $baseline/2; - } - - .card-supertitle { - @extend %t-title6; - - color: $lightest-base-font-color; - } - - .card-title { - @extend %t-title5; - @extend %t-strong; - - margin-bottom: $baseline/2; - } - - .card-text { - @extend %t-title8; - - color: $lightest-base-font-color; - } - - &.mode-audit { - border-color: $audit-mode-color; - - .card-logo { - background-image: url('#{$static-path}/images/certificates/audit.png'); - } - } - - &.mode-honor { - border-color: $honor-mode-color; - - .card-logo { - background-image: url('#{$static-path}/images/certificates/honor.png'); - } - } - - &.mode-verified { - border-color: $verified-mode-color; - - .card-logo { - background-image: url('#{$static-path}/images/certificates/verified.png'); - } - } - - &.mode-professional { - border-color: $professional-certificate-color; - - .card-logo { - background-image: url('#{$static-path}/images/certificates/professional.png'); - } - } -} - -.view-profile { - $profile-image-dimension: 120px; - - .window-wrap, - .content-wrapper { - background-color: $body-bg; - padding: 0; - margin-top: 0; - } - - .page-banner { - background-color: $gray-l4; - max-width: none; - - .user-messages { - max-width: map-get($container-max-widths, xl); - margin: auto; - padding: $baseline/2; - } - } - - .ui-loading-indicator { - @extend .ui-loading-base; - - padding-bottom: $baseline; - - // center horizontally - @include margin-left(auto); - @include margin-right(auto); - - width: ($baseline*5); - } - - .profile-image-field { - button { - background: transparent !important; - border: none !important; - padding: 0; - } - - .u-field-image { - padding-top: 0; - padding-bottom: ($baseline/4); - } - - .image-wrapper { - width: $profile-image-dimension; - position: relative; - margin: auto; - - .image-frame { - display: block; - position: relative; - width: $profile-image-dimension; - height: $profile-image-dimension; - border-radius: ($profile-image-dimension/2); - overflow: hidden; - border: 3px solid $gray-l6; - margin-top: $baseline*-0.75; - background: $white; - } - - .u-field-upload-button { - position: absolute; - top: 0; - opacity: 0; - width: $profile-image-dimension; - height: $profile-image-dimension; - border-radius: ($profile-image-dimension/2); - border: 2px dashed transparent; - background: rgba(229, 241, 247, 0.8); - color: $link-color; - text-shadow: none; - - @include transition(all $tmg-f1 ease-in-out 0s); - - z-index: 6; - - i { - color: $link-color; - } - - &:focus, - &:hover { - @include show-hover-state(); - - border-color: $link-color; - } - - &.in-progress { - opacity: 1; - } - } - - .button-visible { - @include show-hover-state(); - } - - .upload-button-icon, - .upload-button-title { - display: block; - margin-bottom: ($baseline/4); - - @include transform(translateY(35px)); - - line-height: 1.3em; - text-align: center; - z-index: 7; - color: $body-color; - } - - .upload-button-input { - position: absolute; - top: 0; - - @include left(0); - - width: $profile-image-dimension; - border-radius: ($profile-image-dimension/2); - height: 100%; - cursor: pointer; - z-index: 5; - outline: 0; - opacity: 0; - } - - .u-field-remove-button { - position: relative; - display: block; - width: $profile-image-dimension; - margin-top: ($baseline / 4); - padding: ($baseline / 5) 0 0; - text-align: center; - opacity: 0; - transition: opacity 0.5s; - } - - &:hover, - &:active { - .u-field-remove-button { - opacity: 1; - } - } - } - } - - .wrapper-profile { - min-height: 200px; - background-color: $gray-l6; - - .ui-loading-indicator { - margin-top: 100px; - } - } - - .profile-self { - .wrapper-profile-field-account-privacy { - @include clearfix(); - - box-sizing: border-box; - width: 100%; - margin: 0 auto; - border-bottom: 1px solid $gray-l3; - background-color: $gray-l4; - padding: ($baseline*0.75) 5%; - display: table; - - .wrapper-profile-records { - display: table-row; - - button { - @extend %btn-secondary-blue-outline; - - margin-top: 1em; - background: $blue; - color: #fff; - } - } - - @include media-breakpoint-up(sm) { - .wrapper-profile-records { - display: table-cell; - vertical-align: middle; - white-space: nowrap; - - button { - margin-top: 0; - } - } - } - - .u-field-account_privacy { - @extend .container; - - display: table-cell; - border: none; - box-shadow: none; - padding: 0; - margin: 0; - vertical-align: middle; - - @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap - max-width: calc(100% - 40px); - min-width: auto; - } - - .btn-change-privacy { - @extend %btn-primary-blue; - - padding-top: 4px; - padding-bottom: 5px; - background-image: none; - box-shadow: none; - } - } - - .u-field-title { - @extend %t-strong; - - width: auto; - color: $body-color; - cursor: text; - text-shadow: none; // override bad lms styles on labels - } - - .u-field-value { - width: auto; - - @include margin-left($baseline/2); - } - - .u-field-message { - @include float(left); - - width: 100%; - padding: 0; - color: $body-color; - - .u-field-message-notification { - color: $gray-d2; - } - } - } - } - - .wrapper-profile-sections { - @extend .container; - - @include padding($baseline*1.5, 5%, $baseline*1.5, 5%); - - display: flex; - min-width: 0; - max-width: 100%; - - @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap - @include margin-left(0); - - flex-wrap: wrap; - } - } - - .profile-header { - max-width: map-get($container-max-widths, xl); - margin: auto; - padding: $baseline 5% 0; - - .header { - @extend %t-title4; - @extend %t-ultrastrong; - - display: inline-block; - color: #222; - } - - .subheader { - @extend %t-title6; - } - } - - .wrapper-profile-section-container-one { - @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap - width: 100%; - } - - .wrapper-profile-section-one { - width: 300px; - background-color: $white; - border-top: 5px solid $blue; - padding-bottom: $baseline; - - @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap - @include margin-left(0); - - width: 100%; - } - - .profile-section-one-fields { - margin: 0 $baseline/2; - - .social-links { - @include padding($baseline/4, 0, 0, $baseline/4); - - font-size: 2rem; - - & > span { - color: $gray-l4; - } - - a { - .fa-facebook-square { - color: $facebook-blue; - } - - .fa-twitter-square { - color: $twitter-blue; - } - - .fa-linkedin-square { - color: $linkedin-blue; - } - } - } - - .u-field { - font-weight: $font-semibold; - - @include padding(0, 0, 0, 3px); - - color: $body-color; - margin-top: $baseline/5; - - .u-field-value, - .u-field-title { - font-weight: 500; - width: calc(100% - 40px); - color: $lightest-base-font-color; - } - - .u-field-value-readonly { - font-family: $font-family-sans-serif; - color: $darkest-base-font-color; - } - - &.u-field-dropdown { - position: relative; - - &:not(.editable-never) { - cursor: pointer; - } - } - - &:not(.u-field-readonly) { - &.u-field-value { - @extend %t-weight3; - } - - &:not(:last-child) { - padding-bottom: $baseline/4; - border-bottom: 1px solid $border-color; - - &:hover.mode-placeholder { - padding-bottom: $baseline/5; - border-bottom: 2px dashed $link-color; - } - } - } - } - - & > .u-field { - &:not(:first-child) { - font-size: $body-font-size; - color: $body-color; - font-weight: $font-light; - margin-bottom: 0; - } - - &:first-child { - @extend %t-title4; - @extend %t-weight4; - - font-size: em(24); - } - } - - select { - width: 85%; - } - - .u-field-message { - @include right(0); - - position: absolute; - top: 0; - width: 20px; - - .icon { - vertical-align: baseline; - } - } - } - } - } - - - .wrapper-profile-section-container-two { - @include float(left); - @include padding-left($baseline); - - font-family: $font-family-sans-serif; - flex-grow: 1; - - @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap - width: 90%; - margin-top: $baseline; - padding: 0; - } - - .u-field-textarea { - @include padding(0, ($baseline*0.75), ($baseline*0.75), 0); - - margin-bottom: ($baseline/2); - - @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap - @include padding-left($baseline/4); - } - - .u-field-header { - position: relative; - - .u-field-message { - @include right(0); - - top: $baseline/4; - position: absolute; - } - } - - &.editable-toggle { - cursor: pointer; - } - } - - .u-field-title { - @extend %t-title6; - - display: inline-block; - margin-top: 0; - margin-bottom: ($baseline/4); - color: $gray-d3; - width: 100%; - font: $font-semibold 1.4em/1.4em $font-family-sans-serif; - } - - .u-field-value { - @extend %t-copy-base; - - width: 100%; - overflow: auto; - - textarea { - width: 100%; - background-color: transparent; - border-radius: 5px; - border-color: $gray-d1; - resize: none; - white-space: pre-line; - outline: 0; - box-shadow: none; - -webkit-appearance: none; - } - - a { - color: inherit; - } - } - - .u-field-message { - @include float(right); - - width: auto; - - .message-can-edit { - position: absolute; - } - } - - .u-field.mode-placeholder { - padding: $baseline; - margin: $baseline*0.75 0; - border: 2px dashed $gray-l3; - - i { - font-size: 12px; - - @include padding-right(5px); - - vertical-align: middle; - color: $body-color; - } - - .u-field-title { - width: 100%; - text-align: center; - } - - .u-field-value { - text-align: center; - line-height: 1.5em; - - @extend %t-copy-sub1; - - color: $body-color; - } - - &:hover { - border: 2px dashed $link-color; - - .u-field-title, - i { - color: $link-color; - } - } - } - - .wrapper-u-field { - font-size: $body-font-size; - color: $body-color; - - .u-field-header .u-field-title { - color: $body-color; - } - - .u-field-footer { - .field-textarea-character-count { - @extend %t-weight1; - - @include float(right); - - margin-top: $baseline/4; - } - } - } - - .profile-private-message { - @include padding-left($baseline*0.75); - - line-height: 3em; - } - } - - .badge-paging-header { - padding-top: $baseline; - } - - .page-content-nav { - @extend %page-content-nav; - } - - .badge-set-display { - @extend .container; - - padding: 0; - - .badge-list { - // We're using a div instead of ul for accessibility, so we have to match the style - // used by ul. - margin: 1em 0; - padding: 0 0 0 40px; - } - - .badge-display { - width: 50%; - display: inline-block; - vertical-align: top; - padding: 2em 0; - - .badge-image-container { - padding-right: $baseline; - margin-left: 1em; - width: 20%; - vertical-align: top; - display: inline-block; - - img.badge { - width: 100%; - } - - .accomplishment-placeholder { - border: 4px dotted $gray-l4; - border-radius: 50%; - display: block; - width: 100%; - padding-bottom: 100%; - } - } - - .badge-details { - @extend %t-copy-sub1; - @extend %t-regular; - - max-width: 70%; - display: inline-block; - color: $gray-d1; - - .badge-name { - @extend %t-strong; - @extend %t-copy-base; - - color: $gray-d3; - } - - .badge-description { - padding-bottom: $baseline; - line-height: 1.5em; - } - - .badge-date-stamp { - @extend %t-copy-sub1; - } - - .find-button-container { - border: 1px solid $blue-l1; - padding: ($baseline / 2) $baseline ($baseline / 2) $baseline; - display: inline-block; - border-radius: 5px; - font-weight: bold; - color: $blue-s3; - } - - .share-button { - @extend %t-action3; - @extend %button-reset; - - background: $gray-l6; - color: $gray-d1; - padding: ($baseline / 4) ($baseline / 2); - margin-bottom: ($baseline / 2); - display: inline-block; - border-radius: 5px; - border: 2px solid $gray-d1; - cursor: pointer; - transition: background 0.5s; - - .share-prefix { - display: inline-block; - vertical-align: middle; - } - - .share-icon-container { - display: inline-block; - - img.icon-mozillaopenbadges { - max-width: 1.5em; - margin-right: 0.25em; - } - } - - &:hover { - background: $gray-l4; - } - - &:active { - box-shadow: inset 0 4px 15px 0 $black-t2; - transition: none; - } - } - } - } - - .badge-placeholder { - background-color: $gray-l7; - box-shadow: inset 0 0 4px 0 $gray-l4; - } - } - - // ------------------------------ - // #BADGES MODAL - // ------------------------------ - .badges-overlay { - @extend %ui-depth1; - - position: fixed; - top: 0; - left: 0; - background-color: $dark-trans-bg; /* dim the background */ - width: 100%; - height: 100%; - vertical-align: middle; - - .badges-modal { - @extend %t-copy-lead1; - @extend %ui-depth2; - - color: $lighter-base-font-color; - box-sizing: content-box; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - width: 80%; - max-width: 700px; - max-height: calc(100% - 100px); - margin-right: auto; - margin-left: auto; - border-top: rem(10) solid $blue-l2; - background: $light-gray3; - padding-right: ($baseline * 2); - padding-left: ($baseline * 2); - padding-bottom: ($baseline); - overflow-x: hidden; - - .modal-header { - margin-top: ($baseline / 2); - margin-bottom: ($baseline / 2); - } - - .close { - @extend %button-reset; - @extend %t-strong; - - color: $lighter-base-font-color; - position: absolute; - right: ($baseline); - top: $baseline; - cursor: pointer; - padding: ($baseline / 4) ($baseline / 2); - - @include transition(all $tmg-f2 ease-in-out 0s); - - &:focus, - &:hover { - background-color: $blue-d2; - border-radius: 3px; - color: $white; - } - } - - .badges-steps { - display: table; - } - - .image-container { - // Lines the image up with the content of the above list. - @include ltr { - @include padding-left(2em); - } - - @include rtl { - @include padding-right(1em); - - float: right; - } - } - - .backpack-logo { - @include float(right); - @include margin-left($baseline); - } - } - } - - .modal-hr { - display: block; - border: none; - background-color: $light-gray; - height: rem(2); - width: 100%; - } -} diff --git a/lms/static/sass/partials/lms/theme/_variables-v1.scss b/lms/static/sass/partials/lms/theme/_variables-v1.scss index 1cff0168aced..5dca9b849534 100644 --- a/lms/static/sass/partials/lms/theme/_variables-v1.scss +++ b/lms/static/sass/partials/lms/theme/_variables-v1.scss @@ -527,9 +527,6 @@ $palette-success-border: #b9edb9; $palette-success-back: #ecfaec; $palette-success-text: #008100; -// learner profile elements -$learner-profile-container-flex: 768px; - // course elements $course-bg-color: $uxpl-grayscale-x-back !default; $account-content-wrapper-bg: shade($body-bg, 2%) !default; diff --git a/lms/static/sass/views/_account-settings.scss b/lms/static/sass/views/_account-settings.scss deleted file mode 100644 index a4e5ff76eab6..000000000000 --- a/lms/static/sass/views/_account-settings.scss +++ /dev/null @@ -1,683 +0,0 @@ -// lms - application - account settings -// ==================== - -// Table of Contents -// * +Container - Account Settings -// * +Main - Header -// * +Settings Section -// * +Alert Messages - - -// +Container - Account Settings -.wrapper-account-settings { - background: $white; - width: 100%; - - .account-settings-container { - max-width: grid-width(12); - padding: 10px; - margin: 0 auto; - } - - .ui-loading-indicator, - .ui-loading-error { - @extend .ui-loading-base; - // center horizontally - @include margin-left(auto); - @include margin-right(auto); - - padding: ($baseline*3); - text-align: center; - - .message-error { - color: $alert-color; - } - } -} - -// +Main - Header -.wrapper-account-settings { - .wrapper-header { - max-width: grid-width(12); - height: 139px; - border-bottom: 4px solid $m-gray-l4; - - .header-title { - @extend %t-title4; - - margin-bottom: ($baseline/2); - padding-top: ($baseline*2); - } - - .header-subtitle { - color: $gray-l2; - } - - .account-nav { - @include float(left); - - margin: ($baseline/2) 0; - padding: 0; - list-style: none; - - .account-nav-link { - @include float(left); - - font-size: em(14); - color: $gray; - padding: $baseline/4 $baseline*1.25 $baseline; - display: inline-block; - box-shadow: none; - border-bottom: 4px solid transparent; - border-radius: 0; - background: transparent none; - } - - button { - @extend %ui-clear-button; - @extend %btn-no-style; - - @include appearance(none); - - display: block; - padding: ($baseline/4); - - &:hover, - &:focus { - text-decoration: none; - border-bottom-color: $courseware-border-bottom-color; - } - - &.active { - border-bottom-color: theme-color("dark"); - } - } - } - - @include media-breakpoint-down(md) { - border-bottom-color: transparent; - - .account-nav { - display: flex; - border-bottom: none; - - .account-nav-link { - border-bottom: 4px solid theme-color("light"); - } - } - } - } -} - -// +Settings Section -.account-settings-sections { - .section-header { - @extend %t-title5; - @extend %t-strong; - - padding-top: ($baseline/2)*3; - color: $dark-gray1; - } - - .section { - background-color: $white; - margin: $baseline 5% 0; - border-bottom: 4px solid $m-gray-l4; - - .account-settings-header-subtitle { - font-size: em(14); - line-height: normal; - color: $dark-gray; - padding-bottom: 10px; - } - - .account-settings-header-subtitle-warning { - @extend .account-settings-header-subtitle; - - color: $alert-color; - } - - .account-settings-section-body { - .u-field { - border-bottom: 2px solid $m-gray-l4; - padding: $baseline*0.75 0; - - .field { - width: 30%; - vertical-align: top; - display: inline-block; - position: relative; - - select { - @include appearance(none); - - padding: 14px 30px 14px 15px; - border: 1px solid $gray58-border; - background-color: transparent; - border-radius: 2px; - position: relative; - z-index: 10; - - &::-ms-expand { - display: none; - } - - ~ .icon-caret-down { - &::after { - content: ""; - border-left: 6px solid transparent; - border-right: 6px solid transparent; - border-top: 7px solid $blue; - position: absolute; - right: 10px; - bottom: 20px; - z-index: 0; - } - } - } - - .field-label { - display: block; - width: auto; - margin-bottom: 0.625rem; - font-size: 1rem; - line-height: 1; - color: $dark-gray; - white-space: nowrap; - } - - .field-input { - @include transition(all 0.125s ease-in-out 0s); - - display: inline-block; - padding: 0.625rem; - border: 1px solid $gray58-border; - border-radius: 2px; - background: $white; - font-size: $body-font-size; - color: $dark-gray; - width: 100%; - height: 48px; - box-shadow: none; - } - - .u-field-link { - @extend %ui-clear-button; - - // set styles - @extend %btn-pl-default-base; - - @include font-size(18); - - width: 100%; - border: 1px solid $blue; - color: $blue; - padding: 11px 14px; - line-height: normal; - } - } - - .u-field-order { - display: flex; - align-items: center; - font-size: em(16); - font-weight: 600; - color: $dark-gray; - width: 100%; - padding-top: $baseline; - padding-bottom: $baseline; - line-height: normal; - flex-flow: row wrap; - - span { - padding: $baseline; - } - - .u-field-order-number { - @include float(left); - - width: 30%; - } - - .u-field-order-date { - @include float(left); - - padding-left: 30px; - width: 20%; - } - - .u-field-order-price { - @include float(left); - - width: 15%; - } - - .u-field-order-link { - width: 10%; - padding: 0; - - .u-field-link { - @extend %ui-clear-button; - @extend %btn-pl-default-base; - - @include font-size(14); - - border: 1px solid $blue; - color: $blue; - line-height: normal; - padding: 10px; - width: 110px; - } - } - } - - .u-field-order-lines { - @extend .u-field-order; - - padding: 5px 0 0; - font-weight: 100; - - .u-field-order-number { - padding: 20px 10px 20px 30px; - } - } - - .social-field-linked { - background: $m-gray-l4; - box-shadow: 0 1px 2px 1px $shadow-l2; - padding: 1.25rem; - box-sizing: border-box; - margin: 10px; - width: 100%; - - .field-label { - @include font-size(24); - } - - .u-field-social-help { - display: inline-block; - padding: 20px 0 6px; - } - - .u-field-link { - @include font-size(14); - @include text-align(left); - - border: none; - margin-top: $baseline; - font-weight: $font-semibold; - padding: 0; - - &:focus, - &:hover, - &:active { - background-color: transparent; - color: $m-blue-d3; - border: none; - } - } - } - - .social-field-unlinked { - background: $m-gray-l4; - box-shadow: 0 1px 2px 1px $shadow-l2; - padding: 1.25rem; - box-sizing: border-box; - text-align: center; - margin: 10px; - width: 100%; - - .field-label { - @include font-size(24); - - text-align: center; - } - - .u-field-link { - @include font-size(14); - - margin-top: $baseline; - font-weight: $font-semibold; - } - } - - .u-field-message { - position: relative; - padding: $baseline*0.75 0 0 ($baseline*4); - width: 60%; - - .u-field-message-notification { - position: absolute; - left: 0; - top: 0; - bottom: 0; - margin: auto; - padding: 38px 0 0 ($baseline*5); - } - } - - &:last-child { - border-bottom: none; - margin-bottom: ($baseline*2); - } - - // Responsive behavior - @include media-breakpoint-down(md) { - .u-field-value { - width: 100%; - } - - .u-field-message { - width: 100%; - padding: $baseline/2 0; - - .u-field-message-notification { - position: relative; - padding: 0; - } - } - - .u-field-order { - display: flex; - flex-wrap: nowrap; - - .u-field-order-number, - .u-field-order-date, - .u-field-order-price, - .u-field-order-link { - width: auto; - float: none; - flex-grow: 1; - - &:first-of-type { - flex-grow: 2; - } - } - } - } - } - - .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 { - border-bottom: none; - border: 1px solid $m-gray-l4; - margin-bottom: $baseline; - padding: 0; - - &:last-child { - border-bottom: 1px solid $m-gray-l4; - } - - &:hover, - &:focus { - background-color: $light-gray4; - } - } - - .u-field-order-orderId { - border: none; - margin-top: $baseline; - margin-bottom: 0; - padding-bottom: 0; - - &:hover, - &:focus { - background-color: transparent; - } - - .u-field-order { - font-weight: $font-semibold; - padding-top: 0; - padding-bottom: 0; - - .u-field-order-title { - font-size: em(16); - } - } - } - - .u-field-social { - border-bottom: none; - margin-right: 20px; - width: 30%; - display: inline-block; - vertical-align: top; - - .u-field-social-help { - @include font-size(12); - - color: $m-gray-d1; - } - } - } - - .account-deletion-details { - .btn-outline-primary { - @extend %ui-clear-button; - - // set styles - @extend %btn-pl-default-base; - - @include font-size(18); - - border: 1px solid $blue; - color: $blue; - padding: 11px 14px; - line-height: normal; - margin: 20px 0; - } - - .paragon__modal-open { - overflow-y: scroll; - color: $dark-gray; - - .paragon__modal-title { - font-weight: $font-semibold; - } - - .paragon__modal-body { - line-height: 1.5; - - .alert-title { - line-height: 1.5; - } - } - - .paragon__alert-warning { - color: $dark-gray; - } - - .next-steps { - margin-bottom: 10px; - font-weight: $font-semibold; - } - - .confirm-password-input { - width: 50%; - } - - .paragon__btn:not(.cancel-btn) { - @extend %btn-primary-blue; - } - } - - .modal-alert { - display: flex; - - .icon-wrapper { - padding-right: 15px; - } - - .alert-content { - .alert-title { - color: $dark-gray; - margin-bottom: 10px; - font: { - size: 1rem; - weight: $font-semibold; - } - } - - a { - color: $blue-u1; - } - } - } - - .delete-confirmation-wrapper { - .paragon__modal-footer { - .paragon__btn-outline-primary { - @extend %ui-clear-button; - - // set styles - @extend %btn-pl-default-base; - - @include margin-left(25px); - - border-color: $blue; - color: $blue; - padding: 11px 14px; - line-height: normal; - } - } - } - } - - &:last-child { - border-bottom: none; - } - } -} - -// * +Alert Messages -.account-settings-message, -.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-message { - margin-bottom: 0; - - .alert-message { - padding: 10px; - - .alert-actions { - margin-top: 10px; - - .btn-alert-primary { - @extend %btn-primary-blue; - - @include font-size(18); - - border: 1px solid $m-blue-d3; - border-radius: 3px; - box-shadow: none; - padding: 11px 14px; - line-height: normal; - } - - .btn-alert-secondary { - @extend %ui-clear-button; - - // set styles - @extend %btn-pl-default-base; - - @include font-size(18); - - background-color: white; - border: 1px solid $blue; - color: $blue; - padding: 11px 14px; - line-height: normal; - } - } - } -} diff --git a/lms/templates/dashboard/_dashboard_third_party_error.html b/lms/templates/dashboard/_dashboard_third_party_error.html deleted file mode 100644 index 5b9efe0bbddd..000000000000 --- a/lms/templates/dashboard/_dashboard_third_party_error.html +++ /dev/null @@ -1,14 +0,0 @@ -<%page expression_filter="h"/> - -<%! from django.utils.translation import gettext as _ %> -
-
-
-

${_("Could Not Link Accounts")}

-
- ## Translators: this message is displayed when a user tries to link their account with a third-party authentication provider (for example, Google or LinkedIn) with a given edX account, but their third-party account is already associated with another edX account. provider_name is the name of the third-party authentication provider, and platform_name is the name of the edX deployment. -

${_("The {provider_name} account you selected is already linked to another {platform_name} account.").format(provider_name=duplicate_provider, platform_name=platform_name)}

-
-
-
-
diff --git a/lms/templates/header/user_dropdown.html b/lms/templates/header/user_dropdown.html index 0e4a0271e17b..b4b22e0e32be 100644 --- a/lms/templates/header/user_dropdown.html +++ b/lms/templates/header/user_dropdown.html @@ -4,6 +4,7 @@ <%! import json +from urllib.parse import urljoin from django.conf import settings from django.urls import reverse @@ -43,8 +44,8 @@ % endif - - + + % if should_show_order_history: % endif diff --git a/lms/templates/student_account/account_settings.html b/lms/templates/student_account/account_settings.html deleted file mode 100644 index 2748dc03792d..000000000000 --- a/lms/templates/student_account/account_settings.html +++ /dev/null @@ -1,91 +0,0 @@ -<%page expression_filter="h"/> - -<%! -import json - -from django.urls import reverse -from django.conf import settings -from django.utils.translation import gettext as _ - -from openedx.core.djangolib.js_utils import dump_js_escaped_json, js_escaped_string -from openedx.core.djangolib.markup import HTML -from webpack_loader.templatetags.webpack_loader import render_bundle -from openedx.core.djangoapps.user_api.accounts.utils import is_secondary_email_feature_enabled -%> - -<%inherit file="/main.html" /> -<%def name="online_help_token()"><% return "learneraccountsettings" %> -<%namespace name='static' file='/static_content.html'/> - -<%block name="pagetitle">${_("Account Settings")} - -% if duplicate_provider: -
- <%include file='/dashboard/_dashboard_third_party_error.html' /> -
-% endif - -
-<%block name="headextra"> - <%static:css group='style-course'/> - - - -<%block name="js_extra"> -<%static:require_module module_name="js/student_account/views/account_settings_factory" class_name="AccountSettingsFactory"> - var fieldsData = ${ fields | n, dump_js_escaped_json }, - ordersHistoryData = ${ order_history | n, dump_js_escaped_json }, - authData = ${ auth | n, dump_js_escaped_json }, - 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 }, - - 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 }', - extendedProfileFields = ${ extended_profile_fields | n, dump_js_escaped_json }, - displayAccountDeletion = ${ enable_account_deletion | n, dump_js_escaped_json}; - isSecondaryEmailFeatureEnabled = ${ bool(is_secondary_email_feature_enabled()) | n, dump_js_escaped_json }, - enableCoppaCompliance = ${ bool(enable_coppa_compliance) | n, dump_js_escaped_json }, - - AccountSettingsFactory( - fieldsData, - ${ disable_order_history_tab | n, dump_js_escaped_json }, - ordersHistoryData, - authData, - '${ password_reset_support_link | n, js_escaped_string }', - '${ user_accounts_api_url | n, js_escaped_string }', - '${ user_preferences_api_url | n, js_escaped_string }', - ${ user.id | n, dump_js_escaped_json }, - platformName, - contactEmail, - allowEmailChange, - enableCoppaCompliance, - socialPlatforms, - - syncLearnerProfileData, - enterpriseName, - enterpriseReadonlyAccountFields, - edxSupportUrl, - extendedProfileFields, - displayAccountDeletion, - isSecondaryEmailFeatureEnabled, - ${ beta_language | n, dump_js_escaped_json }, - ); - - - -<%static:webpack entry="StudentAccountDeletionInitializer"> - - diff --git a/lms/templates/student_account/account_settings.underscore b/lms/templates/student_account/account_settings.underscore deleted file mode 100644 index d58a805ec7a4..000000000000 --- a/lms/templates/student_account/account_settings.underscore +++ /dev/null @@ -1,30 +0,0 @@ -
- -
diff --git a/lms/templates/student_account/account_settings_section.underscore b/lms/templates/student_account/account_settings_section.underscore deleted file mode 100644 index 487c2fe0de83..000000000000 --- a/lms/templates/student_account/account_settings_section.underscore +++ /dev/null @@ -1,31 +0,0 @@ -

- <%- tabLabel %> -

-<% _.each(sections, function(section) { %> -
-

<%- gettext(section.title) %>

- <% if (section.subtitle && _.isUndefined(section.message)) { %> - - <% } %> - - <% if (section.message) { %> - - <% } %> - - <% if (section.domHookId) { %> -
- <% } %> - - -
-<% }); %> diff --git a/lms/templates/user_dropdown.html b/lms/templates/user_dropdown.html index 454a898433aa..5423c7c4c892 100644 --- a/lms/templates/user_dropdown.html +++ b/lms/templates/user_dropdown.html @@ -28,7 +28,7 @@ @@ -36,7 +36,7 @@ % else: diff --git a/lms/urls.py b/lms/urls.py index f97bc7f0d04e..9c7dd433a6ff 100644 --- a/lms/urls.py +++ b/lms/urls.py @@ -658,12 +658,6 @@ include('openedx.features.calendar_sync.urls'), ), - # Learner profile - path( - 'u/', - include('openedx.features.learner_profile.urls'), - ), - # Survey Report re_path( fr'^survey_report/', diff --git a/openedx/core/djangoapps/theming/tests/test_theme_style_overrides.py b/openedx/core/djangoapps/theming/tests/test_theme_style_overrides.py index d7578f25eb87..41f91c7d1eb5 100644 --- a/openedx/core/djangoapps/theming/tests/test_theme_style_overrides.py +++ b/openedx/core/djangoapps/theming/tests/test_theme_style_overrides.py @@ -44,21 +44,6 @@ def test_footer(self): # This string comes from header.html of test-theme self.assertContains(resp, "This is a footer for test-theme.") - @with_comprehensive_theme("edx.org") - def test_account_settings_hide_nav(self): - """ - Test that theme header doesn't show marketing site links for Account Settings page. - """ - self._login() - - account_settings_url = reverse('account_settings') - response = self.client.get(account_settings_url) - - # Verify that the header navigation links are hidden for the edx.org version - self.assertNotContains(response, "How it Works") - self.assertNotContains(response, "Find courses") - self.assertNotContains(response, "Schools & Partners") - @with_comprehensive_theme("test-theme") def test_logo_image(self): """ diff --git a/openedx/core/djangoapps/user_api/accounts/tests/test_filters.py b/openedx/core/djangoapps/user_api/accounts/tests/test_filters.py deleted file mode 100644 index 100bd8bff604..000000000000 --- a/openedx/core/djangoapps/user_api/accounts/tests/test_filters.py +++ /dev/null @@ -1,242 +0,0 @@ -""" -Test that various filters are fired for views in the certificates app. -""" -from django.conf import settings -from django.http import HttpResponse -from django.test import override_settings -from django.urls import reverse -from openedx_filters import PipelineStep -from openedx_filters.learning.filters import AccountSettingsRenderStarted -from rest_framework import status -from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase - -from openedx.core.djangolib.testing.utils import skip_unless_lms -from common.djangoapps.student.tests.factories import UserFactory - - -class TestRenderInvalidAccountSettings(PipelineStep): - """ - Utility class used when getting steps for pipeline. - """ - - def run_filter(self, context, template_name): # pylint: disable=arguments-differ - """ - Pipeline step that stops the course about render process. - """ - raise AccountSettingsRenderStarted.RenderInvalidAccountSettings( - "You can't access the account settings page.", - account_settings_template="static_templates/server-error.html", - ) - - -class TestRedirectToPage(PipelineStep): - """ - Utility class used when getting steps for pipeline. - """ - - def run_filter(self, context, template_name): # pylint: disable=arguments-differ - """ - Pipeline step that redirects to dashboard before rendering the account settings page. - - When raising RedirectToPage, this filter uses a redirect_to field handled by - the course about view that redirects to that URL. - """ - raise AccountSettingsRenderStarted.RedirectToPage( - "You can't access this page, redirecting to dashboard.", - redirect_to="/courses", - ) - - -class TestRedirectToDefaultPage(PipelineStep): - """ - Utility class used when getting steps for pipeline. - """ - - def run_filter(self, context, template_name): # pylint: disable=arguments-differ - """ - Pipeline step that redirects to dashboard before rendering the account settings page. - - When raising RedirectToPage, this filter uses a redirect_to field handled by - the course about view that redirects to that URL. - """ - raise AccountSettingsRenderStarted.RedirectToPage( - "You can't access this page, redirecting to dashboard." - ) - - -class TestRenderCustomResponse(PipelineStep): - """ - Utility class used when getting steps for pipeline. - """ - - def run_filter(self, context, template_name): # pylint: disable=arguments-differ - """Pipeline step that returns a custom response when rendering the account settings page.""" - response = HttpResponse("Here's the text of the web page.") - raise AccountSettingsRenderStarted.RenderCustomResponse( - "You can't access this page.", - response=response, - ) - - -class TestAccountSettingsRender(PipelineStep): - """ - Utility class used when getting steps for pipeline. - """ - - def run_filter(self, context, template_name): # pylint: disable=arguments-differ - """Pipeline step that returns a custom response when rendering the account settings page.""" - template_name = 'static_templates/about.html' - return { - "context": context, "template_name": template_name, - } - - -@skip_unless_lms -class TestAccountSettingsFilters(SharedModuleStoreTestCase): - """ - Tests for the Open edX Filters associated with the account settings proccess. - - This class guarantees that the following filters are triggered during the user's account settings rendering: - - - AccountSettingsRenderStarted - """ - def setUp(self): # pylint: disable=arguments-differ - super().setUp() - self.user = UserFactory.create( - username="somestudent", - first_name="Student", - last_name="Person", - email="robot@robot.org", - is_active=True, - password="password", - ) - self.client.login(username=self.user.username, password="password") - self.account_settings_url = settings.ACCOUNT_MICROFRONTEND_URL - - @override_settings( - OPEN_EDX_FILTERS_CONFIG={ - "org.openedx.learning.student.settings.render.started.v1": { - "pipeline": [ - "openedx.core.djangoapps.user_api.accounts.tests.test_filters.TestAccountSettingsRender", - ], - "fail_silently": False, - }, - }, - ) - def test_account_settings_render_filter_executed(self): - """ - Test whether the account settings filter is triggered before the user's - account settings page is rendered. - - Expected result: - - AccountSettingsRenderStarted is triggered and executes TestAccountSettingsRender - """ - response = self.client.get(self.account_settings_url) - self.assertEqual(response.status_code, status.HTTP_200_OK) - self.assertContains(response, "This page left intentionally blank. Feel free to add your own content.") - - @override_settings( - OPEN_EDX_FILTERS_CONFIG={ - "org.openedx.learning.student.settings.render.started.v1": { - "pipeline": [ - "openedx.core.djangoapps.user_api.accounts.tests.test_filters.TestRenderInvalidAccountSettings", # pylint: disable=line-too-long - ], - "fail_silently": False, - }, - }, - PLATFORM_NAME="My site", - ) - def test_account_settings_render_alternative(self): - """ - Test whether the account settings filter is triggered before the user's - account settings page is rendered. - - Expected result: - - AccountSettingsRenderStarted is triggered and executes TestRenderInvalidAccountSettings # pylint: disable=line-too-long - """ - response = self.client.get(self.account_settings_url) - - self.assertContains(response, "There has been a 500 error on the My site servers") - - @override_settings( - OPEN_EDX_FILTERS_CONFIG={ - "org.openedx.learning.student.settings.render.started.v1": { - "pipeline": [ - "openedx.core.djangoapps.user_api.accounts.tests.test_filters.TestRenderCustomResponse", - ], - "fail_silently": False, - }, - }, - ) - def test_account_settings_render_custom_response(self): - """ - Test whether the account settings filter is triggered before the user's - account settings page is rendered. - - Expected result: - - AccountSettingsRenderStarted is triggered and executes TestRenderCustomResponse - """ - response = self.client.get(self.account_settings_url) - - self.assertEqual(response.content, b"Here's the text of the web page.") - - @override_settings( - OPEN_EDX_FILTERS_CONFIG={ - "org.openedx.learning.student.settings.render.started.v1": { - "pipeline": [ - "openedx.core.djangoapps.user_api.accounts.tests.test_filters.TestRedirectToPage", - ], - "fail_silently": False, - }, - }, - ) - def test_account_settings_redirect_to_page(self): - """ - Test whether the account settings filter is triggered before the user's - account settings page is rendered. - - Expected result: - - AccountSettingsRenderStarted is triggered and executes TestRedirectToPage - """ - response = self.client.get(self.account_settings_url) - - self.assertEqual(response.status_code, status.HTTP_302_FOUND) - self.assertEqual('/courses', response.url) - - @override_settings( - OPEN_EDX_FILTERS_CONFIG={ - "org.openedx.learning.student.settings.render.started.v1": { - "pipeline": [ - "openedx.core.djangoapps.user_api.accounts.tests.test_filters.TestRedirectToDefaultPage", - ], - "fail_silently": False, - }, - }, - ) - def test_account_settings_redirect_default(self): - """ - Test whether the account settings filter is triggered before the user's - account settings page is rendered. - - Expected result: - - AccountSettingsRenderStarted is triggered and executes TestRedirectToDefaultPage - """ - response = self.client.get(self.account_settings_url) - - self.assertEqual(response.status_code, status.HTTP_302_FOUND) - self.assertEqual(f"{reverse('dashboard')}", response.url) - - @override_settings(OPEN_EDX_FILTERS_CONFIG={}) - def test_account_settings_render_without_filter_config(self): - """ - Test whether the course about filter is triggered before the course about - render without affecting its execution flow. - - Expected result: - - AccountSettingsRenderStarted executes a noop (empty pipeline). Without any - modification comparing it with the effects of TestAccountSettingsRender. - - The view response is HTTP_200_OK. - """ - response = self.client.get(self.account_settings_url) - - self.assertNotContains(response, "This page left intentionally blank. Feel free to add your own content.") diff --git a/openedx/core/djangoapps/user_authn/cookies.py b/openedx/core/djangoapps/user_authn/cookies.py index 24f929698fa7..036baf2125b2 100644 --- a/openedx/core/djangoapps/user_authn/cookies.py +++ b/openedx/core/djangoapps/user_authn/cookies.py @@ -6,6 +6,7 @@ import json import logging import time +from urllib.parse import urljoin from django.conf import settings from django.contrib.auth.models import User # lint-amnesty, pylint: disable=imported-auth-user @@ -244,8 +245,8 @@ def _get_user_info_cookie_data(request, user): # External sites will need to have fallback mechanisms to handle this case # (most likely just hiding the links). try: - header_urls['account_settings'] = reverse('account_settings') - header_urls['learner_profile'] = reverse('learner_profile', kwargs={'username': user.username}) + header_urls['account_settings'] = settings.ACCOUNT_MICROFRONTEND_URL + header_urls['learner_profile'] = urljoin(settings.PROFILE_MICROFRONTEND_URL, f'/u/{user.username}') except NoReverseMatch: pass diff --git a/openedx/core/djangoapps/user_authn/tests/test_cookies.py b/openedx/core/djangoapps/user_authn/tests/test_cookies.py index 8a7841b3b980..d4d9a59fc9ff 100644 --- a/openedx/core/djangoapps/user_authn/tests/test_cookies.py +++ b/openedx/core/djangoapps/user_authn/tests/test_cookies.py @@ -4,6 +4,7 @@ from datetime import date import json from unittest.mock import MagicMock, patch +from urllib.parse import urljoin from django.conf import settings from django.http import HttpResponse from django.test import RequestFactory, TestCase @@ -57,8 +58,8 @@ def _get_expected_image_urls(self): def _get_expected_header_urls(self): expected_header_urls = { 'logout': reverse('logout'), - 'account_settings': reverse('account_settings'), - 'learner_profile': reverse('learner_profile', kwargs={'username': self.user.username}), + 'account_settings': settings.ACCOUNT_MICROFRONTEND_URL, + 'learner_profile': urljoin(settings.PROFILE_MICROFRONTEND_URL, f'/u/{self.user.username}'), } block_url = retrieve_last_sitewide_block_completed(self.user) if block_url: diff --git a/openedx/core/djangoapps/user_authn/views/tests/test_login.py b/openedx/core/djangoapps/user_authn/views/tests/test_login.py index aa34a076d403..38649bb93a03 100644 --- a/openedx/core/djangoapps/user_authn/views/tests/test_login.py +++ b/openedx/core/djangoapps/user_authn/views/tests/test_login.py @@ -496,7 +496,7 @@ def test_login_user_info_cookie(self): # Check that the URLs are absolute for url in user_info["header_urls"].values(): - assert 'http://testserver/' in url + assert 'http://' in url def test_logout_deletes_mktg_cookies(self): response, _ = self._login_response(self.user_email, self.password) diff --git a/openedx/features/learner_profile/README.rst b/openedx/features/learner_profile/README.rst deleted file mode 100644 index 0dce8e10ccdc..000000000000 --- a/openedx/features/learner_profile/README.rst +++ /dev/null @@ -1,8 +0,0 @@ -Learner Profile ---------------- - -This directory contains a Django application that provides a view to render -a profile for any Open edX learner. See `Exploring Your Dashboard and Profile`_ -for more details. - -.. _Exploring Your Dashboard and Profile: https://edx.readthedocs.io/projects/open-edx-learner-guide/en/latest/SFD_dashboard_profile_SectionHead.html?highlight=profile diff --git a/openedx/features/learner_profile/__init__.py b/openedx/features/learner_profile/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/openedx/features/learner_profile/static/learner_profile/fixtures/learner_profile.html b/openedx/features/learner_profile/static/learner_profile/fixtures/learner_profile.html deleted file mode 100644 index 61c139210a20..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/fixtures/learner_profile.html +++ /dev/null @@ -1,40 +0,0 @@ -
-
-
- - -
-
-

- - - - - Loading - -

-
- -
diff --git a/openedx/features/learner_profile/static/learner_profile/js/learner_profile_factory.js b/openedx/features/learner_profile/static/learner_profile/js/learner_profile_factory.js deleted file mode 100644 index d4f81ae7f47c..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/learner_profile_factory.js +++ /dev/null @@ -1,219 +0,0 @@ -(function(define) { - 'use strict'; - - define([ - 'gettext', - 'jquery', - 'underscore', - 'backbone', - 'logger', - 'edx-ui-toolkit/js/utils/string-utils', - 'edx-ui-toolkit/js/pagination/paging-collection', - 'js/student_account/models/user_account_model', - 'js/student_account/models/user_preferences_model', - 'js/views/fields', - 'learner_profile/js/views/learner_profile_fields', - 'learner_profile/js/views/learner_profile_view', - 'js/student_account/views/account_settings_fields', - 'js/views/message_banner', - 'string_utils' - ], function(gettext, $, _, Backbone, Logger, StringUtils, PagingCollection, AccountSettingsModel, - AccountPreferencesModel, FieldsView, LearnerProfileFieldsView, LearnerProfileView, - AccountSettingsFieldViews, MessageBannerView) { - return function(options) { - var $learnerProfileElement = $('.wrapper-profile'); - - var accountSettingsModel = new AccountSettingsModel( - _.extend( - options.account_settings_data, - { - default_public_account_fields: options.default_public_account_fields, - parental_consent_age_limit: options.parental_consent_age_limit, - enable_coppa_compliance: options.enable_coppa_compliance - } - ), - {parse: true} - ); - var AccountPreferencesModelWithDefaults = AccountPreferencesModel.extend({ - defaults: { - account_privacy: options.default_visibility - } - }); - var accountPreferencesModel = new AccountPreferencesModelWithDefaults(options.preferences_data); - - var editable = options.own_profile ? 'toggle' : 'never'; - - var messageView = new MessageBannerView({ - el: $('.message-banner') - }); - - var accountPrivacyFieldView, - profileImageFieldView, - usernameFieldView, - nameFieldView, - sectionOneFieldViews, - sectionTwoFieldViews, - learnerProfileView, - getProfileVisibility, - showLearnerProfileView; - - accountSettingsModel.url = options.accounts_api_url; - accountPreferencesModel.url = options.preferences_api_url; - - accountPrivacyFieldView = new LearnerProfileFieldsView.AccountPrivacyFieldView({ - model: accountPreferencesModel, - required: true, - editable: 'always', - showMessages: false, - title: gettext('Profile Visibility:'), - valueAttribute: 'account_privacy', - options: [ - ['private', gettext('Limited Profile')], - ['all_users', gettext('Full Profile')] - ], - helpMessage: '', - accountSettingsPageUrl: options.account_settings_page_url, - persistChanges: true - }); - - profileImageFieldView = new LearnerProfileFieldsView.ProfileImageFieldView({ - model: accountSettingsModel, - valueAttribute: 'profile_image', - editable: editable === 'toggle', - messageView: messageView, - imageMaxBytes: options.profile_image_max_bytes, - imageMinBytes: options.profile_image_min_bytes, - imageUploadUrl: options.profile_image_upload_url, - imageRemoveUrl: options.profile_image_remove_url - }); - - usernameFieldView = new FieldsView.ReadonlyFieldView({ - model: accountSettingsModel, - screenReaderTitle: gettext('Username'), - valueAttribute: 'username', - helpMessage: '' - }); - - nameFieldView = new FieldsView.ReadonlyFieldView({ - model: accountSettingsModel, - screenReaderTitle: gettext('Full Name'), - valueAttribute: 'name', - helpMessage: '' - }); - - sectionOneFieldViews = [ - new LearnerProfileFieldsView.SocialLinkIconsView({ - model: accountSettingsModel, - socialPlatforms: options.social_platforms, - ownProfile: options.own_profile - }), - - new FieldsView.DateFieldView({ - title: gettext('Joined'), - titleVisible: true, - model: accountSettingsModel, - screenReaderTitle: gettext('Joined Date'), - valueAttribute: 'date_joined', - helpMessage: '', - userLanguage: accountSettingsModel.get('language'), - userTimezone: accountPreferencesModel.get('time_zone'), - dateFormat: 'MMMM YYYY' // not localized, but hopefully ok. - }), - - new FieldsView.DropdownFieldView({ - title: gettext('Location'), - titleVisible: true, - model: accountSettingsModel, - screenReaderTitle: gettext('Country'), - required: true, - editable: editable, - showMessages: false, - placeholderValue: gettext('Add Country'), - valueAttribute: 'country', - options: options.country_options, - helpMessage: '', - persistChanges: true - }), - - new AccountSettingsFieldViews.LanguageProficienciesFieldView({ - title: gettext('Language'), - titleVisible: true, - model: accountSettingsModel, - screenReaderTitle: gettext('Preferred Language'), - required: false, - editable: editable, - showMessages: false, - placeholderValue: gettext('Add language'), - valueAttribute: 'language_proficiencies', - options: options.language_options, - helpMessage: '', - persistChanges: true - }) - ]; - - sectionTwoFieldViews = [ - new FieldsView.TextareaFieldView({ - model: accountSettingsModel, - editable: editable, - showMessages: false, - title: gettext('About me'), - // eslint-disable-next-line max-len - placeholderValue: gettext("Tell other learners a little about yourself: where you live, what your interests are, why you're taking courses, or what you hope to learn."), - valueAttribute: 'bio', - helpMessage: '', - persistChanges: true, - messagePosition: 'header', - maxCharacters: 300 - }) - ]; - - learnerProfileView = new LearnerProfileView({ - el: $learnerProfileElement, - ownProfile: options.own_profile, - has_preferences_access: options.has_preferences_access, - accountSettingsModel: accountSettingsModel, - preferencesModel: accountPreferencesModel, - accountPrivacyFieldView: accountPrivacyFieldView, - profileImageFieldView: profileImageFieldView, - usernameFieldView: usernameFieldView, - nameFieldView: nameFieldView, - sectionOneFieldViews: sectionOneFieldViews, - sectionTwoFieldViews: sectionTwoFieldViews, - platformName: options.platform_name - }); - - getProfileVisibility = function() { - if (options.has_preferences_access) { - return accountPreferencesModel.get('account_privacy'); - } else { - return accountSettingsModel.get('profile_is_public') ? 'all_users' : 'private'; - } - }; - - showLearnerProfileView = function() { - // Record that the profile page was viewed - Logger.log('edx.user.settings.viewed', { - page: 'profile', - visibility: getProfileVisibility(), - user_id: options.profile_user_id - }); - - // Render the view for the first time - learnerProfileView.render(); - }; - - if (options.has_preferences_access) { - if (accountSettingsModel.get('requires_parental_consent')) { - accountPreferencesModel.set('account_privacy', 'private'); - } - } - showLearnerProfileView(); - - return { - accountSettingsModel: accountSettingsModel, - accountPreferencesModel: accountPreferencesModel, - learnerProfileView: learnerProfileView, - }; - }; - }); -}).call(this, define || RequireJS.define); diff --git a/openedx/features/learner_profile/static/learner_profile/js/spec/learner_profile_factory_spec.js b/openedx/features/learner_profile/static/learner_profile/js/spec/learner_profile_factory_spec.js deleted file mode 100644 index 0019c2fc136f..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/spec/learner_profile_factory_spec.js +++ /dev/null @@ -1,79 +0,0 @@ -define( - [ - 'backbone', 'jquery', 'underscore', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', - 'common/js/spec_helpers/template_helpers', - 'js/spec/student_account/helpers', - 'learner_profile/js/spec_helpers/helpers', - 'js/views/fields', - 'js/student_account/models/user_account_model', - 'js/student_account/models/user_preferences_model', - 'learner_profile/js/views/learner_profile_view', - 'learner_profile/js/views/learner_profile_fields', - 'learner_profile/js/learner_profile_factory', - 'js/views/message_banner' - ], - function(Backbone, $, _, AjaxHelpers, TemplateHelpers, Helpers, LearnerProfileHelpers, FieldViews, - UserAccountModel, UserPreferencesModel, LearnerProfileView, LearnerProfileFields, LearnerProfilePage) { - 'use strict'; - - describe('edx.user.LearnerProfileFactory', function() { - var createProfilePage; - - beforeEach(function() { - loadFixtures('learner_profile/fixtures/learner_profile.html'); - }); - - afterEach(function() { - Backbone.history.stop(); - }); - - createProfilePage = function(ownProfile, options) { - return new LearnerProfilePage({ - accounts_api_url: Helpers.USER_ACCOUNTS_API_URL, - preferences_api_url: Helpers.USER_PREFERENCES_API_URL, - own_profile: ownProfile, - account_settings_page_url: Helpers.USER_ACCOUNTS_API_URL, - country_options: Helpers.FIELD_OPTIONS, - language_options: Helpers.FIELD_OPTIONS, - has_preferences_access: true, - profile_image_max_bytes: Helpers.IMAGE_MAX_BYTES, - profile_image_min_bytes: Helpers.IMAGE_MIN_BYTES, - 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', - find_courses_url: '/courses/', - account_settings_data: Helpers.createAccountSettingsData(options), - preferences_data: Helpers.createUserPreferencesData() - }); - }; - - it('renders the full profile for a user', function() { - var context, - learnerProfileView; - AjaxHelpers.requests(this); - context = createProfilePage(true); - learnerProfileView = context.learnerProfileView; - - // sets the profile for full view. - context.accountPreferencesModel.set({account_privacy: 'all_users'}); - LearnerProfileHelpers.expectProfileSectionsAndFieldsToBeRendered(learnerProfileView, false); - }); - - it("renders the limited profile for undefined 'year_of_birth'", function() { - var context = createProfilePage(true, {year_of_birth: '', requires_parental_consent: true}), - learnerProfileView = context.learnerProfileView; - - LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView); - }); - - it('renders the limited profile for under 13 users', function() { - var context = createProfilePage( - true, - {year_of_birth: new Date().getFullYear() - 10, requires_parental_consent: true} - ); - var learnerProfileView = context.learnerProfileView; - LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView); - }); - }); - }); diff --git a/openedx/features/learner_profile/static/learner_profile/js/spec/views/learner_profile_fields_spec.js b/openedx/features/learner_profile/static/learner_profile/js/spec/views/learner_profile_fields_spec.js deleted file mode 100644 index 49b3dbc630df..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/spec/views/learner_profile_fields_spec.js +++ /dev/null @@ -1,381 +0,0 @@ -define( - [ - 'backbone', - 'jquery', - 'underscore', - 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', - 'common/js/spec_helpers/template_helpers', - 'js/spec/student_account/helpers', - 'js/student_account/models/user_account_model', - 'learner_profile/js/views/learner_profile_fields', - 'js/views/message_banner' - ], - function(Backbone, $, _, AjaxHelpers, TemplateHelpers, Helpers, UserAccountModel, LearnerProfileFields, - MessageBannerView) { - 'use strict'; - - describe('edx.user.LearnerProfileFields', function() { - var MOCK_YEAR_OF_BIRTH = 1989; - var MOCK_IMAGE_MAX_BYTES = 64; - var MOCK_IMAGE_MIN_BYTES = 16; - - var createImageView = function(options) { - var yearOfBirth = _.isUndefined(options.yearOfBirth) ? MOCK_YEAR_OF_BIRTH : options.yearOfBirth; - var imageMaxBytes = _.isUndefined(options.imageMaxBytes) ? MOCK_IMAGE_MAX_BYTES : options.imageMaxBytes; - var imageMinBytes = _.isUndefined(options.imageMinBytes) ? MOCK_IMAGE_MIN_BYTES : options.imageMinBytes; - var messageView; - - var imageData = { - image_url_large: '/media/profile-images/default.jpg', - has_image: !!options.hasImage - }; - - var accountSettingsModel = new UserAccountModel(); - accountSettingsModel.set({profile_image: imageData}); - accountSettingsModel.set({year_of_birth: yearOfBirth}); - accountSettingsModel.set({requires_parental_consent: !!_.isEmpty(yearOfBirth)}); - - accountSettingsModel.url = Helpers.USER_ACCOUNTS_API_URL; - - messageView = new MessageBannerView({ - el: $('.message-banner') - }); - - return new LearnerProfileFields.ProfileImageFieldView({ - model: accountSettingsModel, - valueAttribute: 'profile_image', - editable: options.ownProfile, - messageView: messageView, - imageMaxBytes: imageMaxBytes, - imageMinBytes: imageMinBytes, - imageUploadUrl: Helpers.IMAGE_UPLOAD_API_URL, - imageRemoveUrl: Helpers.IMAGE_REMOVE_API_URL - }); - }; - - var createSocialLinksView = function(ownProfile, socialPlatformLinks) { - var accountSettingsModel = new UserAccountModel(); - accountSettingsModel.set({social_platforms: socialPlatformLinks}); - - return new LearnerProfileFields.SocialLinkIconsView({ - model: accountSettingsModel, - socialPlatforms: ['twitter', 'facebook', 'linkedin'], - ownProfile: ownProfile - }); - }; - - var createFakeImageFile = function(size) { - var fileFakeData = 'i63ljc6giwoskyb9x5sw0169bdcmcxr3cdz8boqv0lik971972cmd6yknvcxr5sw0nvc169bdcmcxsdf'; - return new Blob( - [fileFakeData.substr(0, size)], - {type: 'image/jpg'} - ); - }; - - var initializeUploader = function(view) { - view.$('.upload-button-input').fileupload({ - url: Helpers.IMAGE_UPLOAD_API_URL, - type: 'POST', - add: view.fileSelected, - done: view.imageChangeSucceeded, - fail: view.imageChangeFailed - }); - }; - - beforeEach(function() { - loadFixtures('learner_profile/fixtures/learner_profile.html'); - TemplateHelpers.installTemplate('templates/fields/field_image'); - TemplateHelpers.installTemplate('templates/fields/message_banner'); - TemplateHelpers.installTemplate('learner_profile/templates/social_icons'); - }); - - afterEach(function() { - // image_field.js's window.onBeforeUnload breaks Karma in Chrome, clean it up after each test - $(window).off('beforeunload'); - }); - - describe('ProfileImageFieldView', function() { - var verifyImageUploadButtonMessage = function(view, inProgress) { - var iconName = inProgress ? 'fa-spinner' : 'fa-camera'; - var message = inProgress ? view.titleUploading : view.uploadButtonTitle(); - expect(view.$('.upload-button-icon span').attr('class')).toContain(iconName); - expect(view.$('.upload-button-title').text().trim()).toBe(message); - }; - - var verifyImageRemoveButtonMessage = function(view, inProgress) { - var iconName = inProgress ? 'fa-spinner' : 'fa-remove'; - var message = inProgress ? view.titleRemoving : view.removeButtonTitle(); - expect(view.$('.remove-button-icon span').attr('class')).toContain(iconName); - expect(view.$('.remove-button-title').text().trim()).toBe(message); - }; - - it('can upload profile image', function() { - var requests = AjaxHelpers.requests(this); - var imageName = 'profile_image.jpg'; - var imageView = createImageView({ownProfile: true, hasImage: false}); - var data; - imageView.render(); - - initializeUploader(imageView); - - // Remove button should not be present for default image - expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy(); - - // For default image, image title should be `Upload an image` - verifyImageUploadButtonMessage(imageView, false); - - // Add image to upload queue. Validate the image size and send POST request to upload image - imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(60)]}); - - // Verify image upload progress message - verifyImageUploadButtonMessage(imageView, true); - - // Verify if POST request received for image upload - AjaxHelpers.expectRequest(requests, 'POST', Helpers.IMAGE_UPLOAD_API_URL, new FormData()); - - // Send 204 NO CONTENT to confirm the image upload success - AjaxHelpers.respondWithNoContent(requests); - - // Upon successful image upload, account settings model will be fetched to - // get the url for newly uploaded image, So we need to send the response for that GET - data = { - profile_image: { - image_url_large: '/media/profile-images/' + imageName, - has_image: true - } - }; - AjaxHelpers.respondWithJson(requests, data); - - // Verify uploaded image name - expect(imageView.$('.image-frame').attr('src')).toContain(imageName); - - // Remove button should be present after successful image upload - expect(imageView.$('.u-field-remove-button').css('display') !== 'none').toBeTruthy(); - - // After image upload, image title should be `Change image` - verifyImageUploadButtonMessage(imageView, false); - }); - - it('can remove profile image', function() { - var requests = AjaxHelpers.requests(this); - var imageView = createImageView({ownProfile: true, hasImage: false}); - var data; - imageView.render(); - - // Verify image remove title - verifyImageRemoveButtonMessage(imageView, false); - - imageView.$('.u-field-remove-button').click(); - - // Verify image remove progress message - verifyImageRemoveButtonMessage(imageView, true); - - // Verify if POST request received for image remove - AjaxHelpers.expectRequest(requests, 'POST', Helpers.IMAGE_REMOVE_API_URL, null); - - // Send 204 NO CONTENT to confirm the image removal success - AjaxHelpers.respondWithNoContent(requests); - - // Upon successful image removal, account settings model will be fetched to get default image url - // So we need to send the response for that GET - data = { - profile_image: { - image_url_large: '/media/profile-images/default.jpg', - has_image: false - } - }; - AjaxHelpers.respondWithJson(requests, data); - - // Remove button should not be present for default image - expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy(); - }); - - it("can't remove default profile image", function() { - var imageView = createImageView({ownProfile: true, hasImage: false}); - imageView.render(); - - spyOn(imageView, 'clickedRemoveButton'); - - // Remove button should not be present for default image - expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy(); - - imageView.$('.u-field-remove-button').click(); - - // Remove button click handler should not be called - expect(imageView.clickedRemoveButton).not.toHaveBeenCalled(); - }); - - it("can't upload image having size greater than max size", function() { - var imageView = createImageView({ownProfile: true, hasImage: false}); - imageView.render(); - - initializeUploader(imageView); - - // Add image to upload queue, this will validate the image size - imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(70)]}); - - // Verify error message - expect($('.message-banner').text().trim()) - .toBe('The file must be smaller than 64 bytes in size.'); - }); - - it("can't upload image having size less than min size", function() { - var imageView = createImageView({ownProfile: true, hasImage: false}); - imageView.render(); - - initializeUploader(imageView); - - // Add image to upload queue, this will validate the image size - imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(10)]}); - - // Verify error message - expect($('.message-banner').text().trim()).toBe('The file must be at least 16 bytes in size.'); - }); - - it("can't upload and remove image if parental consent required", function() { - var imageView = createImageView({ownProfile: true, hasImage: false, yearOfBirth: ''}); - imageView.render(); - - spyOn(imageView, 'clickedUploadButton'); - spyOn(imageView, 'clickedRemoveButton'); - - expect(imageView.$('.u-field-upload-button').css('display') === 'none').toBeTruthy(); - expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy(); - - imageView.$('.u-field-upload-button').click(); - imageView.$('.u-field-remove-button').click(); - - expect(imageView.clickedUploadButton).not.toHaveBeenCalled(); - expect(imageView.clickedRemoveButton).not.toHaveBeenCalled(); - }); - - it("can't upload and remove image on others profile", function() { - var imageView = createImageView({ownProfile: false}); - imageView.render(); - - spyOn(imageView, 'clickedUploadButton'); - spyOn(imageView, 'clickedRemoveButton'); - - expect(imageView.$('.u-field-upload-button').css('display') === 'none').toBeTruthy(); - expect(imageView.$('.u-field-remove-button').css('display') === 'none').toBeTruthy(); - - imageView.$('.u-field-upload-button').click(); - imageView.$('.u-field-remove-button').click(); - - expect(imageView.clickedUploadButton).not.toHaveBeenCalled(); - expect(imageView.clickedRemoveButton).not.toHaveBeenCalled(); - }); - - it('shows message if we try to navigate away during image upload/remove', function() { - var imageView = createImageView({ownProfile: true, hasImage: false}); - spyOn(imageView, 'onBeforeUnload'); - imageView.render(); - - initializeUploader(imageView); - - // Add image to upload queue, this will validate image size and send POST request to upload image - imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(60)]}); - - // Verify image upload progress message - verifyImageUploadButtonMessage(imageView, true); - - window.onbeforeunload = null; - $(window).trigger('beforeunload'); - expect(imageView.onBeforeUnload).toHaveBeenCalled(); - }); - - it('shows error message for HTTP 500', function() { - var requests = AjaxHelpers.requests(this); - var imageView = createImageView({ownProfile: true, hasImage: false}); - imageView.render(); - - initializeUploader(imageView); - - // Add image to upload queue. Validate the image size and send POST request to upload image - imageView.$('.upload-button-input').fileupload('add', {files: [createFakeImageFile(60)]}); - - // Verify image upload progress message - verifyImageUploadButtonMessage(imageView, true); - - // Verify if POST request received for image upload - AjaxHelpers.expectRequest(requests, 'POST', Helpers.IMAGE_UPLOAD_API_URL, new FormData()); - - // Send HTTP 500 - AjaxHelpers.respondWithError(requests); - - expect($('.message-banner').text().trim()).toBe(imageView.errorMessage); - }); - }); - - describe('SocialLinkIconsView', function() { - var socialPlatformLinks, - socialLinkData, - socialLinksView, - socialPlatform, - $icon; - - it('icons are visible and links to social profile if added in account settings', function() { - socialPlatformLinks = { - twitter: { - platform: 'twitter', - social_link: 'https://www.twitter.com/edX' - }, - facebook: { - platform: 'facebook', - social_link: 'https://www.facebook.com/edX' - }, - linkedin: { - platform: 'linkedin', - social_link: '' - } - }; - - socialLinksView = createSocialLinksView(true, socialPlatformLinks); - - // Icons should be present and contain links if defined - for (var i = 0; i < Object.keys(socialPlatformLinks); i++) { // eslint-disable-line vars-on-top - socialPlatform = Object.keys(socialPlatformLinks)[i]; - socialLinkData = socialPlatformLinks[socialPlatform]; - if (socialLinkData.social_link) { - // Icons with a social_link value should be displayed with a surrounding link - $icon = socialLinksView.$('span.fa-' + socialPlatform + '-square'); - expect($icon).toExist(); - expect($icon.parent().is('a')); - } else { - // Icons without a social_link value should be displayed without a surrounding link - $icon = socialLinksView.$('span.fa-' + socialPlatform + '-square'); - expect($icon).toExist(); - expect(!$icon.parent().is('a')); - } - } - }); - - it('icons are not visible on a profile with no links', function() { - socialPlatformLinks = { - twitter: { - platform: 'twitter', - social_link: '' - }, - facebook: { - platform: 'facebook', - social_link: '' - }, - linkedin: { - platform: 'linkedin', - social_link: '' - } - }; - - socialLinksView = createSocialLinksView(false, socialPlatformLinks); - - // Icons should not be present if not defined on another user's profile - for (var i = 0; i < Object.keys(socialPlatformLinks); i++) { // eslint-disable-line vars-on-top - socialPlatform = Object.keys(socialPlatformLinks)[i]; - socialLinkData = socialPlatformLinks[socialPlatform]; - $icon = socialLinksView.$('span.fa-' + socialPlatform + '-square'); - expect($icon).toBe(null); - } - }); - }); - }); - }); diff --git a/openedx/features/learner_profile/static/learner_profile/js/spec/views/learner_profile_view_spec.js b/openedx/features/learner_profile/static/learner_profile/js/spec/views/learner_profile_view_spec.js deleted file mode 100644 index 21d2dd0f5df8..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/spec/views/learner_profile_view_spec.js +++ /dev/null @@ -1,218 +0,0 @@ -/* eslint-disable vars-on-top */ -define( - [ - 'gettext', - 'backbone', - 'jquery', - 'underscore', - 'edx-ui-toolkit/js/pagination/paging-collection', - 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers', - 'common/js/spec_helpers/template_helpers', - 'js/spec/student_account/helpers', - 'learner_profile/js/spec_helpers/helpers', - 'js/views/fields', - 'js/student_account/models/user_account_model', - 'js/student_account/models/user_preferences_model', - 'learner_profile/js/views/learner_profile_fields', - 'learner_profile/js/views/learner_profile_view', - 'js/student_account/views/account_settings_fields', - 'js/views/message_banner' - ], - function(gettext, Backbone, $, _, PagingCollection, AjaxHelpers, TemplateHelpers, Helpers, LearnerProfileHelpers, - FieldViews, UserAccountModel, AccountPreferencesModel, LearnerProfileFields, LearnerProfileView, - AccountSettingsFieldViews, MessageBannerView) { - 'use strict'; - - describe('edx.user.LearnerProfileView', function() { - var createLearnerProfileView = function(ownProfile, accountPrivacy, profileIsPublic) { - var accountSettingsModel = new UserAccountModel(); - accountSettingsModel.set(Helpers.createAccountSettingsData()); - accountSettingsModel.set({profile_is_public: profileIsPublic}); - accountSettingsModel.set({profile_image: Helpers.PROFILE_IMAGE}); - - var accountPreferencesModel = new AccountPreferencesModel(); - accountPreferencesModel.set({account_privacy: accountPrivacy}); - - accountPreferencesModel.url = Helpers.USER_PREFERENCES_API_URL; - - var editable = ownProfile ? 'toggle' : 'never'; - - var accountPrivacyFieldView = new LearnerProfileFields.AccountPrivacyFieldView({ - model: accountPreferencesModel, - required: true, - editable: 'always', - showMessages: false, - title: 'edX learners can see my:', - valueAttribute: 'account_privacy', - options: [ - ['all_users', 'Full Profile'], - ['private', 'Limited Profile'] - ], - helpMessage: '', - accountSettingsPageUrl: '/account/settings/' - }); - - var messageView = new MessageBannerView({ - el: $('.message-banner') - }); - - var profileImageFieldView = new LearnerProfileFields.ProfileImageFieldView({ - model: accountSettingsModel, - valueAttribute: 'profile_image', - editable: editable, - messageView: messageView, - imageMaxBytes: Helpers.IMAGE_MAX_BYTES, - imageMinBytes: Helpers.IMAGE_MIN_BYTES, - imageUploadUrl: Helpers.IMAGE_UPLOAD_API_URL, - imageRemoveUrl: Helpers.IMAGE_REMOVE_API_URL - }); - - var usernameFieldView = new FieldViews.ReadonlyFieldView({ - model: accountSettingsModel, - valueAttribute: 'username', - helpMessage: '' - }); - - var nameFieldView = new FieldViews.ReadonlyFieldView({ - model: accountSettingsModel, - valueAttribute: 'name', - helpMessage: '' - }); - - var sectionOneFieldViews = [ - new LearnerProfileFields.SocialLinkIconsView({ - model: accountSettingsModel, - socialPlatforms: Helpers.SOCIAL_PLATFORMS, - ownProfile: true - }), - - new FieldViews.DropdownFieldView({ - title: gettext('Location'), - model: accountSettingsModel, - required: false, - editable: editable, - showMessages: false, - placeholderValue: '', - valueAttribute: 'country', - options: Helpers.FIELD_OPTIONS, - helpMessage: '' - }), - - new AccountSettingsFieldViews.LanguageProficienciesFieldView({ - title: gettext('Language'), - model: accountSettingsModel, - required: false, - editable: editable, - showMessages: false, - placeholderValue: 'Add language', - valueAttribute: 'language_proficiencies', - options: Helpers.FIELD_OPTIONS, - helpMessage: '' - }), - - new FieldViews.DateFieldView({ - model: accountSettingsModel, - valueAttribute: 'date_joined', - helpMessage: '' - }) - ]; - - var sectionTwoFieldViews = [ - new FieldViews.TextareaFieldView({ - model: accountSettingsModel, - editable: editable, - showMessages: false, - title: 'About me', - placeholderValue: 'Tell other edX learners a little about yourself: where you live, ' - + "what your interests are, why you're taking courses on edX, or what you hope to learn.", - valueAttribute: 'bio', - helpMessage: '', - messagePosition: 'header' - }) - ]; - - return new LearnerProfileView( - { - el: $('.wrapper-profile'), - ownProfile: ownProfile, - hasPreferencesAccess: true, - accountSettingsModel: accountSettingsModel, - preferencesModel: accountPreferencesModel, - accountPrivacyFieldView: accountPrivacyFieldView, - usernameFieldView: usernameFieldView, - nameFieldView: nameFieldView, - profileImageFieldView: profileImageFieldView, - sectionOneFieldViews: sectionOneFieldViews, - sectionTwoFieldViews: sectionTwoFieldViews, - }); - }; - - beforeEach(function() { - loadFixtures('learner_profile/fixtures/learner_profile.html'); - }); - - afterEach(function() { - Backbone.history.stop(); - }); - - it('shows loading error correctly', function() { - var learnerProfileView = createLearnerProfileView(false, 'all_users'); - - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - - learnerProfileView.render(); - learnerProfileView.showLoadingError(); - - Helpers.expectLoadingErrorIsVisible(learnerProfileView, true); - }); - - it('renders all fields as expected for self with full access', function() { - var learnerProfileView = createLearnerProfileView(true, 'all_users', true); - - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - - learnerProfileView.render(); - - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - LearnerProfileHelpers.expectProfileSectionsAndFieldsToBeRendered(learnerProfileView); - }); - - it('renders all fields as expected for self with limited access', function() { - var learnerProfileView = createLearnerProfileView(true, 'private', false); - - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - - learnerProfileView.render(); - - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView); - }); - - it('renders the fields as expected for others with full access', function() { - var learnerProfileView = createLearnerProfileView(false, 'all_users', true); - - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - - learnerProfileView.render(); - - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - LearnerProfileHelpers.expectProfileSectionsAndFieldsToBeRendered(learnerProfileView, true); - }); - - it('renders the fields as expected for others with limited access', function() { - var learnerProfileView = createLearnerProfileView(false, 'private', false); - - Helpers.expectLoadingIndicatorIsVisible(learnerProfileView, true); - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - - learnerProfileView.render(); - - Helpers.expectLoadingErrorIsVisible(learnerProfileView, false); - LearnerProfileHelpers.expectLimitedProfileSectionsAndFieldsToBeRendered(learnerProfileView, true); - }); - }); - }); diff --git a/openedx/features/learner_profile/static/learner_profile/js/spec/views/section_two_tab_spec.js b/openedx/features/learner_profile/static/learner_profile/js/spec/views/section_two_tab_spec.js deleted file mode 100644 index d0e22d670beb..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/spec/views/section_two_tab_spec.js +++ /dev/null @@ -1,113 +0,0 @@ -/* eslint-disable vars-on-top */ -define( - [ - 'backbone', 'jquery', 'underscore', - 'js/spec/student_account/helpers', - 'learner_profile/js/views/section_two_tab', - 'js/views/fields', - 'js/student_account/models/user_account_model' - ], - function(Backbone, $, _, Helpers, SectionTwoTabView, FieldViews, UserAccountModel) { - 'use strict'; - - describe('edx.user.SectionTwoTab', function() { - var createSectionTwoView = function(ownProfile, profileIsPublic) { - var accountSettingsModel = new UserAccountModel(); - accountSettingsModel.set(Helpers.createAccountSettingsData()); - accountSettingsModel.set({profile_is_public: profileIsPublic}); - accountSettingsModel.set({profile_image: Helpers.PROFILE_IMAGE}); - - var editable = ownProfile ? 'toggle' : 'never'; - - var sectionTwoFieldViews = [ - new FieldViews.TextareaFieldView({ - model: accountSettingsModel, - editable: editable, - showMessages: false, - title: 'About me', - placeholderValue: 'Tell other edX learners a little about yourself: where you live, ' - + "what your interests are, why you're taking courses on edX, or what you hope to learn.", - valueAttribute: 'bio', - helpMessage: '', - messagePosition: 'header' - }) - ]; - - return new SectionTwoTabView({ - viewList: sectionTwoFieldViews, - showFullProfile: function() { - return profileIsPublic; - }, - ownProfile: ownProfile - }); - }; - - it('full profile displayed for public profile', function() { - var view = createSectionTwoView(false, true); - view.render(); - var bio = view.$el.find('.u-field-bio'); - expect(bio.length).toBe(1); - }); - - it('profile field parts are actually rendered for public profile', function() { - var view = createSectionTwoView(false, true); - _.each(view.options.viewList, function(fieldView) { - spyOn(fieldView, 'render').and.callThrough(); - }); - view.render(); - _.each(view.options.viewList, function(fieldView) { - expect(fieldView.render).toHaveBeenCalled(); - }); - }); - - var testPrivateProfile = function(ownProfile, messageString) { - var view = createSectionTwoView(ownProfile, false); - view.render(); - var bio = view.$el.find('.u-field-bio'); - expect(bio.length).toBe(0); - var msg = view.$el.find('span.profile-private-message'); - expect(msg.length).toBe(1); - expect(_.count(msg.html(), messageString)).toBeTruthy(); - }; - - it('no profile when profile is private for other people', function() { - testPrivateProfile(false, 'This learner is currently sharing a limited profile'); - }); - - it('no profile when profile is private for the user herself', function() { - testPrivateProfile(true, 'You are currently sharing a limited profile'); - }); - - var testProfilePrivatePartsDoNotRender = function(ownProfile) { - var view = createSectionTwoView(ownProfile, false); - _.each(view.options.viewList, function(fieldView) { - spyOn(fieldView, 'render'); - }); - view.render(); - _.each(view.options.viewList, function(fieldView) { - expect(fieldView.render).not.toHaveBeenCalled(); - }); - }; - - it('profile field parts are not rendered for private profile for owner', function() { - testProfilePrivatePartsDoNotRender(true); - }); - - it('profile field parts are not rendered for private profile for other people', function() { - testProfilePrivatePartsDoNotRender(false); - }); - - it('does not allow fields to be edited when visiting a profile for other people', function() { - var view = createSectionTwoView(false, true); - var bio = view.options.viewList[0]; - expect(bio.editable).toBe('never'); - }); - - it("allows fields to be edited when visiting one's own profile", function() { - var view = createSectionTwoView(true, true); - var bio = view.options.viewList[0]; - expect(bio.editable).toBe('toggle'); - }); - }); - } -); diff --git a/openedx/features/learner_profile/static/learner_profile/js/spec_helpers/helpers.js b/openedx/features/learner_profile/static/learner_profile/js/spec_helpers/helpers.js deleted file mode 100644 index e1369284a4b1..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/spec_helpers/helpers.js +++ /dev/null @@ -1,133 +0,0 @@ -define(['underscore', 'URI', 'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers'], function(_, URI, AjaxHelpers) { - 'use strict'; - - var expectProfileElementContainsField = function(element, view) { - var titleElement, fieldTitle; - var $element = $(element); - - // Avoid testing for elements without titles - titleElement = $element.find('.u-field-title'); - if (titleElement.length === 0) { - return; - } - - fieldTitle = titleElement.text().trim(); - if (!_.isUndefined(view.options.title) && !_.isUndefined(fieldTitle)) { - expect(fieldTitle).toBe(view.options.title); - } - - if ('fieldValue' in view || 'imageUrl' in view) { - if ('imageUrl' in view) { - expect($($element.find('.image-frame')[0]).attr('src')).toBe(view.imageUrl()); - } else if (view.fieldType === 'date') { - expect(view.fieldValue()).toBe(view.timezoneFormattedDate()); - } else if (view.fieldValue()) { - expect(view.fieldValue()).toBe(view.modelValue()); - } else if ('optionForValue' in view) { - expect($($element.find('.u-field-value .u-field-value-readonly')[0]).text()).toBe( - view.displayValue(view.modelValue()) - ); - } else { - expect($($element.find('.u-field-value .u-field-value-readonly')[0]).text()).toBe(view.modelValue()); - } - } else { - throw new Error('Unexpected field type: ' + view.fieldType); - } - }; - - var expectProfilePrivacyFieldTobeRendered = function(learnerProfileView, othersProfile) { - var $accountPrivacyElement = $('.wrapper-profile-field-account-privacy'); - var $privacyFieldElement = $($accountPrivacyElement).find('.u-field'); - - if (othersProfile) { - expect($privacyFieldElement.length).toBe(0); - } else { - expect($privacyFieldElement.length).toBe(1); - expectProfileElementContainsField($privacyFieldElement, learnerProfileView.options.accountPrivacyFieldView); - } - }; - - var expectSectionOneTobeRendered = function(learnerProfileView) { - var sectionOneFieldElements = $(learnerProfileView.$('.wrapper-profile-section-one')) - .find('.u-field, .social-links'); - - expect(sectionOneFieldElements.length).toBe(7); - expectProfileElementContainsField(sectionOneFieldElements[0], learnerProfileView.options.profileImageFieldView); - expectProfileElementContainsField(sectionOneFieldElements[1], learnerProfileView.options.usernameFieldView); - expectProfileElementContainsField(sectionOneFieldElements[2], learnerProfileView.options.nameFieldView); - - _.each(_.rest(sectionOneFieldElements, 3), function(sectionFieldElement, fieldIndex) { - expectProfileElementContainsField( - sectionFieldElement, - learnerProfileView.options.sectionOneFieldViews[fieldIndex] - ); - }); - }; - - var expectSectionTwoTobeRendered = function(learnerProfileView) { - var $sectionTwoElement = $('.wrapper-profile-section-two'); - var $sectionTwoFieldElements = $($sectionTwoElement).find('.u-field'); - - expect($sectionTwoFieldElements.length).toBe(learnerProfileView.options.sectionTwoFieldViews.length); - - _.each($sectionTwoFieldElements, function(sectionFieldElement, fieldIndex) { - expectProfileElementContainsField( - sectionFieldElement, - learnerProfileView.options.sectionTwoFieldViews[fieldIndex] - ); - }); - }; - - var expectProfileSectionsAndFieldsToBeRendered = function(learnerProfileView, othersProfile) { - expectProfilePrivacyFieldTobeRendered(learnerProfileView, othersProfile); - expectSectionOneTobeRendered(learnerProfileView); - expectSectionTwoTobeRendered(learnerProfileView); - }; - - var expectLimitedProfileSectionsAndFieldsToBeRendered = function(learnerProfileView, othersProfile) { - var sectionOneFieldElements = $('.wrapper-profile-section-one').find('.u-field'); - - expectProfilePrivacyFieldTobeRendered(learnerProfileView, othersProfile); - - expect(sectionOneFieldElements.length).toBe(2); - expectProfileElementContainsField( - sectionOneFieldElements[0], - learnerProfileView.options.profileImageFieldView - ); - expectProfileElementContainsField( - sectionOneFieldElements[1], - learnerProfileView.options.usernameFieldView - ); - - if (othersProfile) { - expect($('.profile-private-message').text()) - .toBe('This learner is currently sharing a limited profile.'); - } else { - expect($('.profile-private-message').text()).toBe('You are currently sharing a limited profile.'); - } - }; - - var expectProfileSectionsNotToBeRendered = function() { - expect($('.wrapper-profile-field-account-privacy').length).toBe(0); - expect($('.wrapper-profile-section-one').length).toBe(0); - expect($('.wrapper-profile-section-two').length).toBe(0); - }; - - var expectTabbedViewToBeUndefined = function(requests, tabbedViewView) { - // Unrelated initial request, no badge request - expect(requests.length).toBe(1); - expect(tabbedViewView).toBe(undefined); - }; - - var expectTabbedViewToBeShown = function(tabbedViewView) { - expect(tabbedViewView.$el.find('.page-content-nav').is(':visible')).toBe(true); - }; - - return { - expectLimitedProfileSectionsAndFieldsToBeRendered: expectLimitedProfileSectionsAndFieldsToBeRendered, - expectProfileSectionsAndFieldsToBeRendered: expectProfileSectionsAndFieldsToBeRendered, - expectProfileSectionsNotToBeRendered: expectProfileSectionsNotToBeRendered, - expectTabbedViewToBeUndefined: expectTabbedViewToBeUndefined, - expectTabbedViewToBeShown: expectTabbedViewToBeShown - }; -}); diff --git a/openedx/features/learner_profile/static/learner_profile/js/views/learner_profile_fields.js b/openedx/features/learner_profile/static/learner_profile/js/views/learner_profile_fields.js deleted file mode 100644 index 807e3e4c5b88..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/views/learner_profile_fields.js +++ /dev/null @@ -1,169 +0,0 @@ -/* eslint-disable no-underscore-dangle */ -(function(define) { - 'use strict'; - - define([ - 'gettext', - 'jquery', - 'underscore', - 'backbone', - 'edx-ui-toolkit/js/utils/string-utils', - 'edx-ui-toolkit/js/utils/html-utils', - 'js/views/fields', - 'js/views/image_field', - 'text!learner_profile/templates/social_icons.underscore', - 'backbone-super' - ], function(gettext, $, _, Backbone, StringUtils, HtmlUtils, FieldViews, ImageFieldView, socialIconsTemplate) { - var LearnerProfileFieldViews = {}; - - LearnerProfileFieldViews.AccountPrivacyFieldView = FieldViews.DropdownFieldView.extend({ - - events: { - 'click button.btn-change-privacy': 'finishEditing', - 'change select': 'showSaveButton' - }, - - render: function() { - this._super(); - this.showNotificationMessage(); - this.updateFieldValue(); - return this; - }, - - showNotificationMessage: function() { - var accountSettingsLink = HtmlUtils.joinHtml( - HtmlUtils.interpolateHtml( - HtmlUtils.HTML(''), {settings_url: this.options.accountSettingsPageUrl} - ), - gettext('Account Settings page.'), - HtmlUtils.HTML('') - ); - if (this.profileIsPrivate) { - this._super( - HtmlUtils.interpolateHtml( - gettext('You must specify your birth year before you can share your full profile. To specify your birth year, go to the {account_settings_page_link}'), // eslint-disable-line max-len - {account_settings_page_link: accountSettingsLink} - ) - ); - } else if (this.requiresParentalConsent) { - this._super( - HtmlUtils.interpolateHtml( - gettext('You must be over 13 to share a full profile. If you are over 13, make sure that you have specified a birth year on the {account_settings_page_link}'), // eslint-disable-line max-len - {account_settings_page_link: accountSettingsLink} - ) - ); - } else { - this._super(''); - } - }, - - updateFieldValue: function() { - if (!this.isAboveMinimumAge) { - this.$('.u-field-value select').val('private'); - this.disableField(true); - } - }, - - showSaveButton: function() { - $('.btn-change-privacy').removeClass('hidden'); - } - }); - - LearnerProfileFieldViews.ProfileImageFieldView = ImageFieldView.extend({ - - screenReaderTitle: gettext('Profile Image'), - - imageUrl: function() { - return this.model.profileImageUrl(); - }, - - imageAltText: function() { - return StringUtils.interpolate( - gettext('Profile image for {username}'), - {username: this.model.get('username')} - ); - }, - - imageChangeSucceeded: function() { - var view = this; - // Update model to get the latest urls of profile image. - this.model.fetch().done(function() { - view.setCurrentStatus(''); - view.render(); - view.$('.u-field-upload-button').focus(); - }).fail(function() { - view.setCurrentStatus(''); - view.showErrorMessage(view.errorMessage); - }); - }, - - imageChangeFailed: function(e, data) { - this.setCurrentStatus(''); - this.showImageChangeFailedMessage(data.jqXHR.status, data.jqXHR.responseText); - }, - - showImageChangeFailedMessage: function(status, responseText) { - var errors; - if (_.contains([400, 404], status)) { - try { - errors = JSON.parse(responseText); - this.showErrorMessage(errors.user_message); - } catch (error) { - this.showErrorMessage(this.errorMessage); - } - } else { - this.showErrorMessage(this.errorMessage); - } - }, - - showErrorMessage: function(message) { - this.options.messageView.showMessage(message); - }, - - isEditingAllowed: function() { - return this.model.isAboveMinimumAge(); - }, - - isShowingPlaceholder: function() { - return !this.model.hasProfileImage(); - }, - - clickedRemoveButton: function(e, data) { - this.options.messageView.hideMessage(); - this._super(e, data); - }, - - fileSelected: function(e, data) { - this.options.messageView.hideMessage(); - this._super(e, data); - } - }); - - LearnerProfileFieldViews.SocialLinkIconsView = Backbone.View.extend({ - - initialize: function(options) { - this.options = _.extend({}, options); - }, - - render: function() { - var socialLinks = {}; - for (var platformName in this.options.socialPlatforms) { // eslint-disable-line no-restricted-syntax, guard-for-in, vars-on-top, max-len - socialLinks[platformName] = null; - for (var link in this.model.get('social_links')) { // eslint-disable-line no-restricted-syntax, vars-on-top, max-len - if (platformName === this.model.get('social_links')[link].platform) { - socialLinks[platformName] = this.model.get('social_links')[link].social_link; - } - } - } - - HtmlUtils.setHtml(this.$el, HtmlUtils.template(socialIconsTemplate)({ - socialLinks: socialLinks, - ownProfile: this.options.ownProfile - })); - return this; - } - }); - - return LearnerProfileFieldViews; - }); -}).call(this, define || RequireJS.define); diff --git a/openedx/features/learner_profile/static/learner_profile/js/views/learner_profile_view.js b/openedx/features/learner_profile/static/learner_profile/js/views/learner_profile_view.js deleted file mode 100644 index 74c0e6b819fb..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/views/learner_profile_view.js +++ /dev/null @@ -1,150 +0,0 @@ -(function(define) { - 'use strict'; - - define( - [ - 'gettext', 'jquery', 'underscore', 'backbone', 'edx-ui-toolkit/js/utils/html-utils', - 'common/js/components/views/tabbed_view', - 'learner_profile/js/views/section_two_tab' - ], - function(gettext, $, _, Backbone, HtmlUtils, TabbedView, SectionTwoTab) { - var LearnerProfileView = Backbone.View.extend({ - - initialize: function(options) { - var Router; - this.options = _.extend({}, options); - _.bindAll(this, 'showFullProfile', 'render', 'renderFields', 'showLoadingError'); - this.listenTo(this.options.preferencesModel, 'change:account_privacy', this.render); - Router = Backbone.Router.extend({ - routes: {':about_me': 'loadTab', ':accomplishments': 'loadTab'} - }); - - this.router = new Router(); - this.firstRender = true; - }, - - showFullProfile: function() { - var isAboveMinimumAge = this.options.accountSettingsModel.isAboveMinimumAge(); - if (this.options.ownProfile) { - return isAboveMinimumAge - && this.options.preferencesModel.get('account_privacy') === 'all_users'; - } else { - return this.options.accountSettingsModel.get('profile_is_public'); - } - }, - - setActiveTab: function(tab) { - // This tab may not actually exist. - if (this.tabbedView.getTabMeta(tab).tab) { - this.tabbedView.setActiveTab(tab); - } - }, - - render: function() { - var tabs, - $tabbedViewElement, - $wrapperProfileBioElement = this.$el.find('.wrapper-profile-bio'), - self = this; - - this.sectionTwoView = new SectionTwoTab({ - viewList: this.options.sectionTwoFieldViews, - showFullProfile: this.showFullProfile, - ownProfile: this.options.ownProfile - }); - - this.renderFields(); - - // Reveal the profile and hide the loading indicator - $('.ui-loading-indicator').addClass('is-hidden'); - $('.wrapper-profile-section-container-one').removeClass('is-hidden'); - $('.wrapper-profile-section-container-two').removeClass('is-hidden'); - - - if (this.showFullProfile()) { - tabs = [ - {view: this.sectionTwoView, title: gettext('About Me'), url: 'about_me'} - ]; - - this.tabbedView = new TabbedView({ - tabs: tabs, - router: this.router, - viewLabel: gettext('Profile') - }); - - $tabbedViewElement = this.tabbedView.render().el; - HtmlUtils.setHtml( - $wrapperProfileBioElement, - HtmlUtils.HTML($tabbedViewElement) - ); - - if (this.firstRender) { - this.router.on('route:loadTab', _.bind(this.setActiveTab, this)); - Backbone.history.start(); - this.firstRender = false; - // Load from history. - this.router.navigate((Backbone.history.getFragment() || 'about_me'), {trigger: true}); - } else { - // Restart the router so the tab will be brought up anew. - Backbone.history.stop(); - Backbone.history.start(); - } - } else { - if (this.isCoppaCompliant()) { - // xss-lint: disable=javascript-jquery-html - $wrapperProfileBioElement.html(this.sectionTwoView.render().el); - } - } - return this; - }, - - isCoppaCompliant: function() { - var enableCoppaCompliance = this.options.accountSettingsModel.get('enable_coppa_compliance'), - isAboveAge = this.options.accountSettingsModel.isAboveMinimumAge(); - return !enableCoppaCompliance || (enableCoppaCompliance && isAboveAge); - }, - - renderFields: function() { - var view = this, - fieldView, - imageView, - settings; - - if (this.options.ownProfile && this.isCoppaCompliant()) { - fieldView = this.options.accountPrivacyFieldView; - settings = this.options.accountSettingsModel; - fieldView.profileIsPrivate = !settings.get('year_of_birth'); - fieldView.requiresParentalConsent = settings.get('requires_parental_consent'); - fieldView.isAboveMinimumAge = settings.isAboveMinimumAge(); - fieldView.undelegateEvents(); - this.$('.wrapper-profile-field-account-privacy').prepend(fieldView.render().el); - fieldView.delegateEvents(); - } - - // Clear existing content in user profile card - this.$('.profile-section-one-fields').html(''); - - // Do not show name when in limited mode or no name has been set - if (this.showFullProfile() && this.options.accountSettingsModel.get('name')) { - this.$('.profile-section-one-fields').append(this.options.nameFieldView.render().el); - } - this.$('.profile-section-one-fields').append(this.options.usernameFieldView.render().el); - - imageView = this.options.profileImageFieldView; - this.$('.profile-image-field').append(imageView.render().el); - - if (this.showFullProfile()) { - _.each(this.options.sectionOneFieldViews, function(childFieldView) { - view.$('.profile-section-one-fields').append(childFieldView.render().el); - }); - } - }, - - showLoadingError: function() { - this.$('.ui-loading-indicator').addClass('is-hidden'); - this.$('.ui-loading-error').removeClass('is-hidden'); - } - }); - - return LearnerProfileView; - }); -}).call(this, define || RequireJS.define); diff --git a/openedx/features/learner_profile/static/learner_profile/js/views/section_two_tab.js b/openedx/features/learner_profile/static/learner_profile/js/views/section_two_tab.js deleted file mode 100644 index 23064c9e6b68..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/js/views/section_two_tab.js +++ /dev/null @@ -1,33 +0,0 @@ -(function(define) { - 'use strict'; - - define( - [ - 'gettext', 'jquery', 'underscore', 'backbone', 'text!learner_profile/templates/section_two.underscore', - 'edx-ui-toolkit/js/utils/html-utils' - ], - function(gettext, $, _, Backbone, sectionTwoTemplate, HtmlUtils) { - var SectionTwoTab = Backbone.View.extend({ - attributes: { - class: 'wrapper-profile-section-two' - }, - template: _.template(sectionTwoTemplate), - initialize: function(options) { - this.options = _.extend({}, options); - }, - render: function() { - var self = this; - var showFullProfile = this.options.showFullProfile(); - this.$el.html(HtmlUtils.HTML(this.template({ownProfile: self.options.ownProfile, showFullProfile: showFullProfile})).toString()); // eslint-disable-line max-len - if (showFullProfile) { - _.each(this.options.viewList, function(fieldView) { - self.$el.find('.field-container').append(fieldView.render().el); - }); - } - return this; - } - }); - - return SectionTwoTab; - }); -}).call(this, define || RequireJS.define); diff --git a/openedx/features/learner_profile/static/learner_profile/templates/section_two.underscore b/openedx/features/learner_profile/static/learner_profile/templates/section_two.underscore deleted file mode 100644 index 0c7d11cd8b18..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/templates/section_two.underscore +++ /dev/null @@ -1,10 +0,0 @@ -
-
- <% if (!showFullProfile) { %> - <% if(ownProfile) { %> - <%- gettext("You are currently sharing a limited profile.") %> - <% } else { %> - <%- gettext("This learner is currently sharing a limited profile.") %> - <% } %> - <% } %> -
\ No newline at end of file diff --git a/openedx/features/learner_profile/static/learner_profile/templates/social_icons.underscore b/openedx/features/learner_profile/static/learner_profile/templates/social_icons.underscore deleted file mode 100644 index 52b864cfb669..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/templates/social_icons.underscore +++ /dev/null @@ -1,9 +0,0 @@ - diff --git a/openedx/features/learner_profile/static/learner_profile/templates/third_party_auth.html b/openedx/features/learner_profile/static/learner_profile/templates/third_party_auth.html deleted file mode 100644 index 07e14bc48ab5..000000000000 --- a/openedx/features/learner_profile/static/learner_profile/templates/third_party_auth.html +++ /dev/null @@ -1,47 +0,0 @@ -<%page expression_filter="h"/> -<%! -from django.utils.translation import gettext as _ -from common.djangoapps.third_party_auth import pipeline -%> - -
  • - - ## Translators: this section lists all the third-party authentication providers (for example, Google and LinkedIn) the user can link with or unlink from their edX account. - ${_("Connected Accounts")} - - - - % for state in provider_user_states: -
    -
    - % if state.has_account: - ${_('Linked')} - % else: - ${_('Not Linked')} - % endif -
    - ${state.provider.name} - -
    - % if state.has_account: - - - - % elif state.provider.display_for_login: - - ## Translators: clicking on this creates a link between a user's edX account and their account with an external authentication provider (like Google or LinkedIn). - ${_("Link")} - - % endif -
    -
    -
    - % endfor -
    -
  • diff --git a/openedx/features/learner_profile/templates/learner_profile/learner-achievements-fragment.html b/openedx/features/learner_profile/templates/learner_profile/learner-achievements-fragment.html deleted file mode 100644 index 09e6ce36b9b3..000000000000 --- a/openedx/features/learner_profile/templates/learner_profile/learner-achievements-fragment.html +++ /dev/null @@ -1,69 +0,0 @@ -## mako - -<%page expression_filter="h"/> - -<%namespace name='static' file='/static_content.html'/> - -<%! -from django.utils.translation import gettext as _ -from openedx.core.djangolib.markup import HTML, Text -%> - -
    - % if course_certificates or own_profile: -

    Course Certificates

    - % if course_certificates: - % for certificate in course_certificates: - <% - course = certificate['course'] - - completion_date_message_html = Text(_('Completed {completion_date_html}')).format( - completion_date_html=HTML( - '' - ).format( - completion_date=certificate['created'], - user_timezone=user_timezone, - user_language=user_language, - ), - ) - %> -
    - -
    -
    ${course.display_org_with_default}
    -
    ${course.display_name_with_default}
    -

    ${completion_date_message_html}

    -
    -
    - % endfor - % elif own_profile: -
    -

    ${_("You haven't earned any certificates yet.")}

    - % if settings.FEATURES.get('COURSES_ARE_BROWSABLE'): -

    - - - ${_('Explore New Courses')} - -

    - % endif -
    - % endif - % endif -
    - -<%static:require_module_async module_name="js/dateutil_factory" class_name="DateUtilFactory"> - DateUtilFactory.transform('.localized-datetime'); - diff --git a/openedx/features/learner_profile/templates/learner_profile/learner_profile.html b/openedx/features/learner_profile/templates/learner_profile/learner_profile.html deleted file mode 100644 index 6de4744e66f7..000000000000 --- a/openedx/features/learner_profile/templates/learner_profile/learner_profile.html +++ /dev/null @@ -1,79 +0,0 @@ -## mako - -<%page expression_filter="h"/> -<%inherit file="/main.html" /> -<%def name="online_help_token()"><% return "profile" %> -<%namespace name='static' file='/static_content.html'/> - -<%! -import json -from django.urls import reverse -from django.utils.translation import gettext as _ -from openedx.core.djangolib.js_utils import dump_js_escaped_json -from openedx.core.djangolib.markup import HTML -%> - -<%block name="pagetitle">${_("Learner Profile")} - -<%block name="bodyclass">view-profile - -<%block name="headextra"> -<%static:css group='style-course'/> - - -
    -
    -
    -
    - - % if own_profile: -
    -

    ${_("My Profile")}

    -
    - ${_('Build out your profile to personalize your identity on {platform_name}.').format( - platform_name=platform_name, - )} -
    -
    - % endif - -
    -
    -
    - -<%block name="js_extra"> -<%static:require_module module_name="learner_profile/js/learner_profile_factory" class_name="LearnerProfileFactory"> - var options = ${data | n, dump_js_escaped_json}; - LearnerProfileFactory(options); - - diff --git a/openedx/features/learner_profile/tests/__init__.py b/openedx/features/learner_profile/tests/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/openedx/features/learner_profile/tests/views/__init__.py b/openedx/features/learner_profile/tests/views/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/openedx/features/learner_profile/tests/views/test_learner_profile.py b/openedx/features/learner_profile/tests/views/test_learner_profile.py deleted file mode 100644 index f7430a3ac263..000000000000 --- a/openedx/features/learner_profile/tests/views/test_learner_profile.py +++ /dev/null @@ -1,279 +0,0 @@ -""" Tests for student profile views. """ - - -import datetime -from unittest import mock - -import ddt -from django.conf import settings -from django.test.client import RequestFactory -from django.urls import reverse -from edx_toggles.toggles.testutils import override_waffle_flag -from opaque_keys.edx.locator import CourseLocator - -from common.djangoapps.course_modes.models import CourseMode -from common.djangoapps.student.tests.factories import CourseEnrollmentFactory, UserFactory -from common.djangoapps.util.testing import UrlResetMixin -from lms.djangoapps.certificates.data import CertificateStatuses -from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory -from lms.envs.test import CREDENTIALS_PUBLIC_SERVICE_URL -from openedx.core.djangoapps.content.course_overviews.models import CourseOverview -from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin -from openedx.features.learner_profile.views.learner_profile import learner_profile_context -from xmodule.data import CertificatesDisplayBehaviors # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase # lint-amnesty, pylint: disable=wrong-import-order -from xmodule.modulestore.tests.factories import CourseFactory # lint-amnesty, pylint: disable=wrong-import-order - - -@ddt.ddt -class LearnerProfileViewTest(SiteMixin, UrlResetMixin, ModuleStoreTestCase): - """ Tests for the student profile view. """ - - USERNAME = "username" - OTHER_USERNAME = "other_user" - PASSWORD = "password" - DOWNLOAD_URL = "http://www.example.com/certificate.pdf" - CONTEXT_DATA = [ - 'default_public_account_fields', - 'accounts_api_url', - 'preferences_api_url', - 'account_settings_page_url', - 'has_preferences_access', - 'own_profile', - 'country_options', - 'language_options', - 'account_settings_data', - 'preferences_data', - ] - - def setUp(self): - super().setUp() - self.user = UserFactory.create(username=self.USERNAME, password=self.PASSWORD) - self.other_user = UserFactory.create(username=self.OTHER_USERNAME, password=self.PASSWORD) - self.client.login(username=self.USERNAME, password=self.PASSWORD) - self.course = CourseFactory.create( - start=datetime.datetime(2013, 9, 16, 7, 17, 28), - end=datetime.datetime.now(), - certificate_available_date=datetime.datetime.now(), - ) - - def test_context(self): - """ - Verify learner profile page context data. - """ - request = RequestFactory().get('/url') - request.user = self.user - - context = learner_profile_context(request, self.USERNAME, self.user.is_staff) - - assert context['data']['default_public_account_fields'] == \ - settings.ACCOUNT_VISIBILITY_CONFIGURATION['public_fields'] - - assert context['data']['accounts_api_url'] == \ - reverse('accounts_api', kwargs={'username': self.user.username}) - - assert context['data']['preferences_api_url'] == \ - reverse('preferences_api', kwargs={'username': self.user.username}) - - assert context['data']['profile_image_upload_url'] == \ - reverse('profile_image_upload', kwargs={'username': self.user.username}) - - assert context['data']['profile_image_remove_url'] == \ - reverse('profile_image_remove', kwargs={'username': self.user.username}) - - assert context['data']['profile_image_max_bytes'] == settings.PROFILE_IMAGE_MAX_BYTES - - assert context['data']['profile_image_min_bytes'] == settings.PROFILE_IMAGE_MIN_BYTES - - assert context['data']['account_settings_page_url'] == reverse('account_settings') - - for attribute in self.CONTEXT_DATA: - assert attribute in context['data'] - - def test_view(self): - """ - Verify learner profile page view. - """ - profile_path = reverse('learner_profile', kwargs={'username': self.USERNAME}) - response = self.client.get(path=profile_path) - - for attribute in self.CONTEXT_DATA: - self.assertContains(response, attribute) - - def test_redirect_view(self): - profile_path = reverse('learner_profile', kwargs={'username': self.USERNAME}) - - # Test with waffle flag active and site setting disabled, does not redirect - response = self.client.get(path=profile_path) - for attribute in self.CONTEXT_DATA: - self.assertContains(response, attribute) - - # Test with waffle flag active and site setting enabled, redirects to microfrontend - site_domain = 'othersite.example.com' - self.set_up_site(site_domain, { - 'SITE_NAME': site_domain, - 'ENABLE_PROFILE_MICROFRONTEND': True - }) - self.client.login(username=self.USERNAME, password=self.PASSWORD) - response = self.client.get(path=profile_path) - profile_url = settings.PROFILE_MICROFRONTEND_URL - self.assertRedirects(response, profile_url + self.USERNAME, fetch_redirect_response=False) - - def test_records_link(self): - profile_path = reverse('learner_profile', kwargs={'username': self.USERNAME}) - response = self.client.get(path=profile_path) - self.assertContains(response, f'') - - def test_undefined_profile_page(self): - """ - Verify that a 404 is returned for a non-existent profile page. - """ - profile_path = reverse('learner_profile', kwargs={'username': "no_such_user"}) - response = self.client.get(path=profile_path) - assert 404 == response.status_code - - def _create_certificate(self, course_key=None, enrollment_mode=CourseMode.HONOR, status='downloadable'): - """Simulate that the user has a generated certificate. """ - CourseEnrollmentFactory.create(user=self.user, course_id=self.course.id, mode=enrollment_mode) - return GeneratedCertificateFactory( - user=self.user, - course_id=course_key or self.course.id, - mode=enrollment_mode, - download_url=self.DOWNLOAD_URL, - status=status, - ) - - @ddt.data(CourseMode.HONOR, CourseMode.PROFESSIONAL, CourseMode.VERIFIED) - def test_certificate_visibility(self, cert_mode): - """ - Verify that certificates are displayed with the correct card mode. - """ - # Add new certificate - cert = self._create_certificate(enrollment_mode=cert_mode) - cert.save() - - response = self.client.get(f'/u/{self.user.username}') - - self.assertContains(response, f'card certificate-card mode-{cert_mode}') - - @ddt.data( - ['downloadable', True], - ['notpassing', False], - ) - @ddt.unpack - def test_certificate_status_visibility(self, status, is_passed_status): - """ - Verify that certificates are only displayed for passing status. - """ - # Add new certificate - cert = self._create_certificate(status=status) - cert.save() - - # Ensure that this test is actually using both passing and non-passing certs. - assert CertificateStatuses.is_passing_status(cert.status) == is_passed_status - - response = self.client.get(f'/u/{self.user.username}') - - if is_passed_status: - self.assertContains(response, f'card certificate-card mode-{cert.mode}') - else: - self.assertNotContains(response, f'card certificate-card mode-{cert.mode}') - - def test_certificate_for_missing_course(self): - """ - Verify that a certificate is not shown for a missing course. - """ - # Add new certificate - cert = self._create_certificate(course_key=CourseLocator.from_string('course-v1:edX+INVALID+1')) - cert.save() - - response = self.client.get(f'/u/{self.user.username}') - - self.assertNotContains(response, f'card certificate-card mode-{cert.mode}') - - @ddt.data(True, False) - def test_no_certificate_visibility(self, own_profile): - """ - Verify that the 'You haven't earned any certificates yet.' well appears on the user's - own profile when they do not have certificates and does not appear when viewing - another user that does not have any certificates. - """ - profile_username = self.user.username if own_profile else self.other_user.username - response = self.client.get(f'/u/{profile_username}') - - if own_profile: - self.assertContains(response, 'You haven't earned any certificates yet.') - else: - self.assertNotContains(response, 'You haven't earned any certificates yet.') - - @ddt.data(True, False) - def test_explore_courses_visibility(self, courses_browsable): - with mock.patch.dict('django.conf.settings.FEATURES', {'COURSES_ARE_BROWSABLE': courses_browsable}): - response = self.client.get(f'/u/{self.user.username}') - if courses_browsable: - self.assertContains(response, 'Explore New Courses') - else: - self.assertNotContains(response, 'Explore New Courses') - - def test_certificate_for_visibility_for_not_viewable_course(self): - """ - Verify that a certificate is not shown if certificate are not viewable to users. - """ - # add new course with certificate_available_date is future date. - course = CourseFactory.create( - certificate_available_date=datetime.datetime.now() + datetime.timedelta(days=5), - certificates_display_behavior=CertificatesDisplayBehaviors.END_WITH_DATE - ) - - cert = self._create_certificate(course_key=course.id) - cert.save() - - response = self.client.get(f'/u/{self.user.username}') - - self.assertNotContains(response, f'card certificate-card mode-{cert.mode}') - - def test_certificates_visible_only_for_staff_and_profile_user(self): - """ - Verify that certificates data are passed to template only in case of staff user - and profile user. - """ - request = RequestFactory().get('/url') - request.user = self.user - profile_username = self.other_user.username - user_is_staff = True - context = learner_profile_context(request, profile_username, user_is_staff) - - assert 'achievements_fragment' in context - - user_is_staff = False - context = learner_profile_context(request, profile_username, user_is_staff) - assert 'achievements_fragment' not in context - - profile_username = self.user.username - context = learner_profile_context(request, profile_username, user_is_staff) - assert 'achievements_fragment' in context - - @mock.patch.dict(settings.FEATURES, {'CERTIFICATES_HTML_VIEW': True}) - def test_certificate_visibility_with_no_cert_config(self): - """ - Verify that certificates are not displayed until there is an active - certificate configuration. - """ - # Add new certificate - cert = self._create_certificate(enrollment_mode=CourseMode.VERIFIED) - cert.download_url = '' - cert.save() - - response = self.client.get(f'/u/{self.user.username}') - self.assertNotContains( - response, f'card certificate-card mode-{CourseMode.VERIFIED}' - ) - - course_overview = CourseOverview.get_from_id(self.course.id) - course_overview.has_any_active_web_certificate = True - course_overview.save() - - response = self.client.get(f'/u/{self.user.username}') - self.assertContains( - response, f'card certificate-card mode-{CourseMode.VERIFIED}' - ) diff --git a/openedx/features/learner_profile/urls.py b/openedx/features/learner_profile/urls.py deleted file mode 100644 index 0f020765686b..000000000000 --- a/openedx/features/learner_profile/urls.py +++ /dev/null @@ -1,24 +0,0 @@ -""" -Defines URLs for the learner profile. -""" - - -from django.conf import settings -from django.urls import path, re_path - -from openedx.features.learner_profile.views.learner_profile import learner_profile - -from .views.learner_achievements import LearnerAchievementsFragmentView - -urlpatterns = [ - re_path( - r'^{username_pattern}$'.format( - username_pattern=settings.USERNAME_PATTERN, - ), - learner_profile, - name='learner_profile', - ), - path('achievements', LearnerAchievementsFragmentView.as_view(), - name='openedx.learner_profile.learner_achievements_fragment_view', - ), -] diff --git a/openedx/features/learner_profile/views/__init__.py b/openedx/features/learner_profile/views/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/openedx/features/learner_profile/views/learner_achievements.py b/openedx/features/learner_profile/views/learner_achievements.py deleted file mode 100644 index 6a7a07e3392d..000000000000 --- a/openedx/features/learner_profile/views/learner_achievements.py +++ /dev/null @@ -1,58 +0,0 @@ -""" -Views to render a learner's achievements. -""" - - -from django.template.loader import render_to_string -from web_fragments.fragment import Fragment - -from lms.djangoapps.certificates import api as certificate_api -from openedx.core.djangoapps.content.course_overviews.models import CourseOverview -from openedx.core.djangoapps.plugin_api.views import EdxFragmentView - - -class LearnerAchievementsFragmentView(EdxFragmentView): - """ - A fragment to render a learner's achievements. - """ - - def render_to_fragment(self, request, username=None, own_profile=False, **kwargs): # lint-amnesty, pylint: disable=arguments-differ - """ - Renders the current learner's achievements. - """ - course_certificates = self._get_ordered_certificates_for_user(request, username) - context = { - 'course_certificates': course_certificates, - 'own_profile': own_profile, - 'disable_courseware_js': True, - } - if course_certificates or own_profile: - html = render_to_string('learner_profile/learner-achievements-fragment.html', context) - return Fragment(html) - else: - return None - - def _get_ordered_certificates_for_user(self, request, username): - """ - Returns a user's certificates sorted by course name. - """ - course_certificates = certificate_api.get_certificates_for_user(username) - passing_certificates = [] - for course_certificate in course_certificates: - if course_certificate.get('is_passing', False): - course_key = course_certificate['course_key'] - try: - course_overview = CourseOverview.get_from_id(course_key) - course_certificate['course'] = course_overview - if certificate_api.certificates_viewable_for_course(course_overview): - # add certificate into passing certificate list only if it's a PDF certificate - # or there is an active certificate configuration. - if course_certificate['is_pdf_certificate'] or course_overview.has_any_active_web_certificate: - passing_certificates.append(course_certificate) - except CourseOverview.DoesNotExist: - # This is unlikely to fail as the course should exist. - # Ideally the cert should have all the information that - # it needs. This might be solved by the Credentials API. - pass - passing_certificates.sort(key=lambda certificate: certificate['course'].display_name_with_default) - return passing_certificates diff --git a/openedx/features/learner_profile/views/learner_profile.py b/openedx/features/learner_profile/views/learner_profile.py deleted file mode 100644 index 9fc8096a28ba..000000000000 --- a/openedx/features/learner_profile/views/learner_profile.py +++ /dev/null @@ -1,116 +0,0 @@ -""" Views for a student's profile information. """ - - -from django.conf import settings -from django.contrib.auth.decorators import login_required -from django.contrib.staticfiles.storage import staticfiles_storage -from django.core.exceptions import ObjectDoesNotExist -from django.http import Http404 -from django.shortcuts import redirect, render -from django.urls import reverse -from django.views.decorators.http import require_http_methods -from django_countries import countries - -from common.djangoapps.edxmako.shortcuts import marketing_link -from openedx.core.djangoapps.credentials.utils import get_credentials_records_url -from openedx.core.djangoapps.programs.models import ProgramsApiConfig -from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers -from openedx.core.djangoapps.user_api.accounts.api import get_account_settings -from openedx.core.djangoapps.user_api.errors import UserNotAuthorized, UserNotFound -from openedx.core.djangoapps.user_api.preferences.api import get_user_preferences -from openedx.features.learner_profile.views.learner_achievements import LearnerAchievementsFragmentView -from common.djangoapps.student.models import User - - -@login_required -@require_http_methods(['GET']) -def learner_profile(request, username): - """Render the profile page for the specified username. - - Args: - request (HttpRequest) - username (str): username of user whose profile is requested. - - Returns: - HttpResponse: 200 if the page was sent successfully - HttpResponse: 302 if not logged in (redirect to login page) - 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 - """ - profile_microfrontend_url = f"{settings.PROFILE_MICROFRONTEND_URL}{username}" - if request.GET: - profile_microfrontend_url += f'?{request.GET.urlencode()}' - return redirect(profile_microfrontend_url) - - -def learner_profile_context(request, profile_username, user_is_staff): - """Context for the learner profile page. - - Args: - 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 - - Raises: - ObjectDoesNotExist: the specified profile_username does not exist. - """ - profile_user = User.objects.get(username=profile_username) - logged_in_user = request.user - - own_profile = (logged_in_user.username == profile_username) - - account_settings_data = get_account_settings(request, [profile_username])[0] - - preferences_data = get_user_preferences(profile_user, profile_username) - - context = { - 'own_profile': own_profile, - 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), - 'data': { - 'profile_user_id': profile_user.id, - 'default_public_account_fields': settings.ACCOUNT_VISIBILITY_CONFIGURATION['public_fields'], - 'default_visibility': settings.ACCOUNT_VISIBILITY_CONFIGURATION['default_visibility'], - 'accounts_api_url': reverse("accounts_api", kwargs={'username': profile_username}), - 'preferences_api_url': reverse('preferences_api', kwargs={'username': profile_username}), - 'preferences_data': preferences_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_user.username == profile_username or user_is_staff), - 'own_profile': own_profile, - 'country_options': list(countries), - 'find_courses_url': marketing_link('COURSES'), - 'language_options': settings.ALL_LANGUAGES, - 'backpack_ui_img': staticfiles_storage.url('certificates/images/backpack-ui.png'), - 'platform_name': configuration_helpers.get_value('platform_name', settings.PLATFORM_NAME), - 'social_platforms': settings.SOCIAL_PLATFORMS, - 'enable_coppa_compliance': settings.ENABLE_COPPA_COMPLIANCE, - 'parental_consent_age_limit': settings.PARENTAL_CONSENT_AGE_LIMIT - }, - 'show_program_listing': ProgramsApiConfig.is_enabled(), - 'show_dashboard_tabs': True, - 'disable_courseware_js': True, - 'nav_hidden': True, - 'records_url': get_credentials_records_url(), - } - - if own_profile or user_is_staff: - achievements_fragment = LearnerAchievementsFragmentView().render_to_fragment( - request, - username=profile_user.username, - own_profile=own_profile, - ) - context['achievements_fragment'] = achievements_fragment - - return context diff --git a/webpack-config/file-lists.js b/webpack-config/file-lists.js index 7167a6f5ddd8..d9e818f9127c 100644 --- a/webpack-config/file-lists.js +++ b/webpack-config/file-lists.js @@ -79,9 +79,6 @@ module.exports = { path.resolve(__dirname, '../lms/static/js/learner_dashboard/views/program_header_view.js'), path.resolve(__dirname, '../lms/static/js/learner_dashboard/views/sidebar_view.js'), path.resolve(__dirname, '../lms/static/js/learner_dashboard/views/upgrade_message_view.js'), - path.resolve(__dirname, '../lms/static/js/student_account/views/account_section_view.js'), - path.resolve(__dirname, '../lms/static/js/student_account/views/account_settings_fields.js'), - path.resolve(__dirname, '../lms/static/js/student_account/views/account_settings_view.js'), path.resolve(__dirname, '../lms/static/js/student_account/views/FormView.js'), path.resolve(__dirname, '../lms/static/js/student_account/views/LoginView.js'), path.resolve(__dirname, '../lms/static/js/student_account/views/RegisterView.js'), From e52d49ff6b4c9d713cb2a02eccdf43331926fbca Mon Sep 17 00:00:00 2001 From: Deborah Kaplan Date: Tue, 4 Feb 2025 21:06:43 +0000 Subject: [PATCH 3/3] chore: linting it looks like prior attempts to reformat the files moved pylint directives to the wrong lines. --- .../third_party_auth/tests/specs/base.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/common/djangoapps/third_party_auth/tests/specs/base.py b/common/djangoapps/third_party_auth/tests/specs/base.py index 635f606b8e43..524cd64ff366 100644 --- a/common/djangoapps/third_party_auth/tests/specs/base.py +++ b/common/djangoapps/third_party_auth/tests/specs/base.py @@ -682,8 +682,8 @@ def test_full_pipeline_succeeds_for_unlinking_account(self): login_user(post_request) actions.do_complete( post_request.backend, - social_views._do_login, - user=user, # pylint: disable=protected-access, no-member + social_views._do_login, # pylint: disable=protected-access + user=user, # pylint: disable=no-member request=post_request, ) @@ -914,8 +914,11 @@ def test_pipeline_redirects_to_requested_url(self): self.assert_redirect_after_pipeline_completes( actions.do_complete( - request.backend, social_views._do_login, user=user, request=request - ), # pylint: disable=protected-access + request.backend, + social_views._do_login, # pylint: disable=protected-access + user=user, + request=request, + ), requested_redirect_url, ) @@ -1126,9 +1129,9 @@ def do_complete(self, strategy, request, partial_pipeline_token, partial_data, u user = request.user return actions.do_complete( request.backend, - social_views._do_login, + social_views._do_login, # pylint: disable=protected-access user, - None, # pylint: disable=protected-access + None, redirect_field_name=auth.REDIRECT_FIELD_NAME, request=request, partial_token=partial_pipeline_token,