diff --git a/lms/djangoapps/learner_dashboard/tests/test_programs.py b/lms/djangoapps/learner_dashboard/tests/test_programs.py index a3662a1c4c4154ca41a912db1a112fc0f2ddc667..5387b733aa5b8406c5631300645bc234c4731d3a 100644 --- a/lms/djangoapps/learner_dashboard/tests/test_programs.py +++ b/lms/djangoapps/learner_dashboard/tests/test_programs.py @@ -12,12 +12,13 @@ from bs4 import BeautifulSoup from django.conf import settings from django.core.urlresolvers import reverse, reverse_lazy from django.test import override_settings -from waffle.testutils import override_switch from lms.envs.test import CREDENTIALS_PUBLIC_SERVICE_URL from openedx.core.djangoapps.catalog.tests.factories import CourseFactory, CourseRunFactory, ProgramFactory from openedx.core.djangoapps.catalog.tests.mixins import CatalogIntegrationMixin +from openedx.core.djangoapps.credentials import STUDENT_RECORDS_FLAG from openedx.core.djangoapps.programs.tests.mixins import ProgramsApiConfigMixin +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag from openedx.core.djangolib.testing.utils import skip_unless_lms from student.tests.factories import CourseEnrollmentFactory, UserFactory from xmodule.modulestore.tests.django_utils import SharedModuleStoreTestCase @@ -175,7 +176,7 @@ class TestProgramListing(ProgramsApiConfigMixin, SharedModuleStoreTestCase): @skip_unless_lms @mock.patch(PROGRAMS_UTILS_MODULE + '.get_programs') -@override_switch('student_records', True) +@override_waffle_flag(STUDENT_RECORDS_FLAG, active=True) class TestProgramDetails(ProgramsApiConfigMixin, CatalogIntegrationMixin, SharedModuleStoreTestCase): """Unit tests for the program details page.""" shard = 4 diff --git a/lms/static/sass/features/_learner-profile.scss b/lms/static/sass/features/_learner-profile.scss index 5c48fa5871915ffdeeda00c0e185c51be594332e..9958d723cddeb2747f1e2d9ac8a5e63b5a598710 100644 --- a/lms/static/sass/features/_learner-profile.scss +++ b/lms/static/sass/features/_learner-profile.scss @@ -269,6 +269,28 @@ 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; + } + } + + @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; @@ -277,6 +299,7 @@ box-shadow: none; padding: 0; margin: 0; + display: table-cell; @media (max-width: $learner-profile-container-flex) { // Switch to map-get($grid-breakpoints,md) for bootstrap max-width: calc(100% - 40px); diff --git a/lms/static/sass/views/_program-details.scss b/lms/static/sass/views/_program-details.scss index 6ded24172b3c3ad091da3960a360d0bc6d025f2e..228b050ceefe169497c20f409ddea0cafabec9ac 100644 --- a/lms/static/sass/views/_program-details.scss +++ b/lms/static/sass/views/_program-details.scss @@ -632,6 +632,7 @@ .program-record { text-align: center; + padding-bottom: 2em; } @media (min-width: $bp-screen-md) { diff --git a/lms/templates/learner_dashboard/program_details_sidebar.underscore b/lms/templates/learner_dashboard/program_details_sidebar.underscore index 24f0ce19111afb03941e4952f459ce7a996cfab1..3f3e1ea7367644362b88b09a72bf834e661fb198 100644 --- a/lms/templates/learner_dashboard/program_details_sidebar.underscore +++ b/lms/templates/learner_dashboard/program_details_sidebar.underscore @@ -10,7 +10,7 @@ <% if (programRecordUrl) { %> <aside class="aside js-program-record program-record"> <a href="<%- programRecordUrl %>" class="program-record-link"> - <button class="program-record-button"><%- gettext('View Program Record') %></button> + <button class="btn program-record-button"><%- gettext('View Program Record') %></button> </a> </aside> <% } %> diff --git a/openedx/core/djangoapps/credentials/__init__.py b/openedx/core/djangoapps/credentials/__init__.py index b7c6418ae687bef43012840c7cd7456e3f84afc3..106a5a5257c31ebafe9ad17601ef7cb2719a6560 100644 --- a/openedx/core/djangoapps/credentials/__init__.py +++ b/openedx/core/djangoapps/credentials/__init__.py @@ -4,3 +4,10 @@ edX Platform support for credentials. This package will be used as a wrapper for interacting with the credentials service. """ + +from openedx.core.djangoapps.waffle_utils import WaffleFlag, WaffleFlagNamespace + +WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name='credentials') + +# Waffle flag to enable the experimental Student Records feature +STUDENT_RECORDS_FLAG = WaffleFlag(WAFFLE_FLAG_NAMESPACE, 'student_records') diff --git a/openedx/core/djangoapps/credentials/models.py b/openedx/core/djangoapps/credentials/models.py index a1005ec4cbd0b53e9364c4874865716bc7b81f46..bab4f940798068ac97b971a211a7b8bb65a698ac 100644 --- a/openedx/core/djangoapps/credentials/models.py +++ b/openedx/core/djangoapps/credentials/models.py @@ -12,6 +12,8 @@ from django.utils.translation import ugettext_lazy as _ from openedx.core.djangoapps.site_configuration import helpers +from . import STUDENT_RECORDS_FLAG + API_VERSION = 'v2' @@ -84,7 +86,7 @@ class CredentialsApiConfig(ConfigurationModel): Publicly-accessible Records URL root. """ # Temporarily disable this feature while we work on it - if not waffle.switch_is_active('student_records'): + if not STUDENT_RECORDS_FLAG.is_enabled(): return None root = helpers.get_value('CREDENTIALS_PUBLIC_SERVICE_URL', settings.CREDENTIALS_PUBLIC_SERVICE_URL) return urljoin(root, '/records/') 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 index 5bb9ad6da9bf8f06fa1cfe55eda4165459b54012..015245f7960fd5b535238b02057c5fe3aeb35d68 100644 --- 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 @@ -126,7 +126,7 @@ fieldView.requiresParentalConsent = settings.get('requires_parental_consent'); fieldView.isAboveMinimumAge = settings.isAboveMinimumAge(); fieldView.undelegateEvents(); - this.$('.wrapper-profile-field-account-privacy').append(fieldView.render().el); + this.$('.wrapper-profile-field-account-privacy').prepend(fieldView.render().el); fieldView.delegateEvents(); } diff --git a/openedx/features/learner_profile/templates/learner_profile/learner_profile.html b/openedx/features/learner_profile/templates/learner_profile/learner_profile.html index 2dc6248773bffc82be7b7e179afcde80f188f25e..010bc2193f8cc685bf53a705bbe061f0e64d361c 100644 --- a/openedx/features/learner_profile/templates/learner_profile/learner_profile.html +++ b/openedx/features/learner_profile/templates/learner_profile/learner_profile.html @@ -25,7 +25,15 @@ from openedx.core.djangolib.markup import HTML <main id="main" aria-label="Content" tabindex="-1"> <div class="wrapper-profile"> <div class="profile ${'profile-self' if own_profile else 'profile-other'}"> - <div class="wrapper-profile-field-account-privacy"></div> + <div class="wrapper-profile-field-account-privacy"> + % if own_profile and records_url: + <div class="wrapper-profile-records"> + <a href="${records_url}"> + <button class="btn profile-records-button">${_("View My Records")}</button> + </a> + </div> + % endif + </div> % if own_profile: <div class="profile-header"> <div class="header">${_("My Profile")}</div> @@ -33,10 +41,6 @@ from openedx.core.djangolib.markup import HTML ${_('Build out your profile to personalize your identity on {platform_name}.').format( platform_name=platform_name, )} - % if records_url: - ## We don't translate this yet because we know it's not the final string - <p>To view and share your program records, go to <a href="${records_url}">My Records</a>.</p> - % endif </div> </div> % endif diff --git a/openedx/features/learner_profile/tests/views/test_learner_profile.py b/openedx/features/learner_profile/tests/views/test_learner_profile.py index 061e1717cd0e8853d38e22e5268dd7f855851923..b3ecbf62176940efc8de87088842c4088319a281 100644 --- a/openedx/features/learner_profile/tests/views/test_learner_profile.py +++ b/openedx/features/learner_profile/tests/views/test_learner_profile.py @@ -4,7 +4,6 @@ import datetime import ddt import mock -from waffle.testutils import override_switch from lms.djangoapps.certificates.tests.factories import GeneratedCertificateFactory # pylint: disable=import-error from lms.envs.test import CREDENTIALS_PUBLIC_SERVICE_URL @@ -14,6 +13,8 @@ from django.core.urlresolvers import reverse from django.test.client import RequestFactory from lms.djangoapps.certificates.api import is_passing_status from opaque_keys.edx.locator import CourseLocator +from openedx.core.djangoapps.credentials import STUDENT_RECORDS_FLAG +from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag from openedx.features.learner_profile.views.learner_profile import learner_profile_context from student.tests.factories import CourseEnrollmentFactory, UserFactory from util.testing import UrlResetMixin @@ -22,7 +23,7 @@ from xmodule.modulestore.tests.factories import CourseFactory @ddt.ddt -@override_switch('student_records', True) +@override_waffle_flag(STUDENT_RECORDS_FLAG, active=True) class LearnerProfileViewTest(UrlResetMixin, ModuleStoreTestCase): """ Tests for the student profile view. """