From 97d7633774f5fbd9782b1657f4c50b76718f8ab4 Mon Sep 17 00:00:00 2001 From: "Albert St. Aubin" Date: Wed, 1 Feb 2017 12:58:04 -0500 Subject: [PATCH] Added upgrade upsell to course info banner and url parameter TNL-6381 --- .../base/_variables.scss | 1 - .../courseware/tests/test_date_summary.py | 58 ++++++++- lms/djangoapps/courseware/views/views.py | 30 ++++- lms/static/images/edx-verified-mini-cert.png | Bin 0 -> 9120 bytes .../js/courseware/course_home_events.js | 8 +- .../courseware/course_home_events.html | 18 +++ .../courseware/course_home_events_spec.js | 18 ++- lms/static/sass/course/_info.scss | 121 ++++++++++++++++++ lms/templates/courseware/info.html | 20 +++ 9 files changed, 265 insertions(+), 9 deletions(-) create mode 100644 lms/static/images/edx-verified-mini-cert.png diff --git a/common/static/sass/edx-pattern-library-shims/base/_variables.scss b/common/static/sass/edx-pattern-library-shims/base/_variables.scss index 317eb2fa5a..af2e80a2e5 100644 --- a/common/static/sass/edx-pattern-library-shims/base/_variables.scss +++ b/common/static/sass/edx-pattern-library-shims/base/_variables.scss @@ -191,7 +191,6 @@ $btn-border-radius: $component-border-radius !default; $btn-large-padding-vertical: spacing-vertical(small); $btn-large-padding-horizontal: spacing-horizontal(mid-large); - $btn-base-padding-vertical: spacing-vertical(x-small); $btn-base-padding-horizontal: spacing-horizontal(base); $btn-base-font-size: font-size(base); diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py index edffea818d..2627fef09d 100644 --- a/lms/djangoapps/courseware/tests/test_date_summary.py +++ b/lms/djangoapps/courseware/tests/test_date_summary.py @@ -45,6 +45,7 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): days_till_upgrade_deadline=4, enroll_user=True, enrollment_mode=CourseMode.VERIFIED, + user_enrollment_mode=None, course_min_price=100, days_till_verification_deadline=14, verification_status=None, @@ -72,8 +73,11 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ) if enroll_user: - enrollment_mode = enrollment_mode or CourseMode.DEFAULT_MODE_SLUG - CourseEnrollmentFactory.create(course_id=self.course.id, user=self.user, mode=enrollment_mode) + if user_enrollment_mode: + CourseEnrollmentFactory.create(course_id=self.course.id, user=self.user, mode=user_enrollment_mode) + else: + enrollment_mode = enrollment_mode or CourseMode.DEFAULT_MODE_SLUG + CourseEnrollmentFactory.create(course_id=self.course.id, user=self.user, mode=enrollment_mode) if days_till_verification_deadline is not None: VerificationDeadline.objects.create( @@ -274,18 +278,64 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ) self.assertEqual(block.title, 'Course End') - ## Tests Verified Upgrade Deadline Date Block + # Tests Verified Upgrade Deadline Date Block + + def check_upgrade_banner(self, banner_expected=True, include_url_parameter=True): + """ + Helper method to check for the presence of the Upgrade Banner + """ + url = reverse('info', args=[self.course.id.to_deprecated_string()]) + if include_url_parameter: + url += '?upgrade=true' + resp = self.client.get(url) + expected_banner_text = "Give yourself an additional incentive to complete" + if banner_expected: + self.assertIn(expected_banner_text, resp.content) + else: + self.assertNotIn(expected_banner_text, resp.content) + @freeze_time('2015-01-02') def test_verified_upgrade_deadline_date(self): - self.setup_course_and_user(days_till_upgrade_deadline=1) + self.setup_course_and_user(days_till_upgrade_deadline=1, user_enrollment_mode=CourseMode.AUDIT) + self.client.login(username='mrrobot', password='test') block = VerifiedUpgradeDeadlineDate(self.course, self.user) self.assertEqual(block.date, datetime.now(utc) + timedelta(days=1)) + self.assertTrue(block.is_enabled) self.assertEqual(block.link, reverse('verify_student_upgrade_and_verify', args=(self.course.id,))) + self.check_upgrade_banner() def test_without_upgrade_deadline(self): self.setup_course_and_user(enrollment_mode=None) + self.client.login(username='mrrobot', password='test') block = VerifiedUpgradeDeadlineDate(self.course, self.user) + self.assertFalse(block.is_enabled) self.assertIsNone(block.date) + self.check_upgrade_banner(banner_expected=False) + + @freeze_time('2015-01-02') + def test_verified_upgrade_banner_not_present_past_deadline(self): + self.setup_course_and_user(days_till_upgrade_deadline=-1, user_enrollment_mode=CourseMode.AUDIT) + self.client.login(username='mrrobot', password='test') + block = VerifiedUpgradeDeadlineDate(self.course, self.user) + self.assertFalse(block.is_enabled) + self.check_upgrade_banner(banner_expected=False) + + @freeze_time('2015-01-02') + def test_verified_upgrade_banner_cookie(self): + self.setup_course_and_user(days_till_upgrade_deadline=1, user_enrollment_mode=CourseMode.AUDIT) + self.client.login(username='mrrobot', password='test') + + # No URL parameter or cookie present, notification should not be shown. + self.check_upgrade_banner(include_url_parameter=False, banner_expected=False) + + # Now pass URL parameter-- notification should be shown. + self.check_upgrade_banner(include_url_parameter=True) + + # A cookie should be set in the previous call, so it is no longer necessary to pass + # the URL parameter in order to see the notification. + self.check_upgrade_banner(include_url_parameter=False) + + # Unfortunately (according to django doc), it is not possible to test expiration of the cookie. def test_ecommerce_checkout_redirect(self): """Verify the block link redirects to ecommerce checkout if it's enabled.""" diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 1823345d8a..51675ddeea 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -65,6 +65,7 @@ from courseware.courses import ( sort_by_start_date, UserNotEnrolled ) +from courseware.date_summary import VerifiedUpgradeDeadlineDate from courseware.masquerade import setup_masquerade from courseware.model_data import FieldDataCache from courseware.models import StudentModule, BaseStudentModuleHistory @@ -337,6 +338,22 @@ def course_info(request, course_id): if settings.FEATURES.get('ENABLE_MKTG_SITE'): url_to_enroll = marketing_link('COURSES') + store_upgrade_cookie = False + upgrade_cookie_name = 'show_upgrade_notification' + upgrade_link = None + if request.user.is_authenticated(): + show_upgrade_notification = False + if request.GET.get('upgrade', 'false') == 'true': + store_upgrade_cookie = True + show_upgrade_notification = True + elif upgrade_cookie_name in request.COOKIES and bool(request.COOKIES[upgrade_cookie_name]): + show_upgrade_notification = True + + if show_upgrade_notification: + upgrade_data = VerifiedUpgradeDeadlineDate(course, user) + if upgrade_data.is_enabled: + upgrade_link = upgrade_data.link + context = { 'request': request, 'masquerade_user': user, @@ -348,6 +365,7 @@ def course_info(request, course_id): 'studio_url': studio_url, 'show_enroll_banner': show_enroll_banner, 'url_to_enroll': url_to_enroll, + 'upgrade_link': upgrade_link } # Get the URL of the user's last position in order to display the 'where you were last' message @@ -365,7 +383,17 @@ def course_info(request, course_id): if CourseEnrollment.is_enrolled(request.user, course.id): inject_coursetalk_keys_into_context(context, course_key) - return render_to_response('courseware/info.html', context) + response = render_to_response('courseware/info.html', context) + if store_upgrade_cookie: + response.set_cookie( + upgrade_cookie_name, + True, + max_age=10 * 24 * 60 * 60, # set for 10 days + domain=settings.SESSION_COOKIE_DOMAIN, + httponly=True # no use case for accessing from JavaScript + ) + + return response def get_last_accessed_courseware(course, request, user): diff --git a/lms/static/images/edx-verified-mini-cert.png b/lms/static/images/edx-verified-mini-cert.png new file mode 100644 index 0000000000000000000000000000000000000000..35b9b9f1dc75887b2618d49c558609977db2f221 GIT binary patch literal 9120 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 RWA@Sj)ReRp8xYpv{{csAESCTP literal 0 HcmV?d00001 diff --git a/lms/static/js/courseware/course_home_events.js b/lms/static/js/courseware/course_home_events.js index 28f49d06d6..9b9da6ca73 100644 --- a/lms/static/js/courseware/course_home_events.js +++ b/lms/static/js/courseware/course_home_events.js @@ -9,7 +9,13 @@ }); }); $('.date-summary-verified-upgrade-deadline .date-summary-link').on('click', function() { - Logger.log('edx.course.home.upgrade_verified.clicked', {}); + Logger.log('edx.course.home.upgrade_verified.clicked', {location: 'sidebar'}); + }); + $('.upgrade-banner-button').on('click', function() { + Logger.log('edx.course.home.upgrade_verified.clicked', {location: 'notification'}); + }); + $('.view-verified-info').on('click', function() { + Logger.log('edx.course.home.learn_about_verified.clicked', {location: 'notification'}); }); }; }); diff --git a/lms/static/js/fixtures/courseware/course_home_events.html b/lms/static/js/fixtures/courseware/course_home_events.html index e115db5f41..a86ab48b53 100644 --- a/lms/static/js/fixtures/courseware/course_home_events.html +++ b/lms/static/js/fixtures/courseware/course_home_events.html @@ -1,3 +1,21 @@ +
+
+
+
+ +
+
+

Give yourself an additional incentive to complete

+

Earn a verified certificate + Learn More +

+
+ +
+
+

Verification Upgrade Deadline

diff --git a/lms/static/js/spec/courseware/course_home_events_spec.js b/lms/static/js/spec/courseware/course_home_events_spec.js index 749e0c13aa..c7eee6ba13 100644 --- a/lms/static/js/spec/courseware/course_home_events_spec.js +++ b/lms/static/js/spec/courseware/course_home_events_spec.js @@ -17,9 +17,23 @@ define(['jquery', 'logger', 'js/courseware/course_home_events'], function($, Log }); }); - it('sends an event when "Upgrade to Verified" is clicked', function() { + it('sends an event when "Upgrade to Verified" is clicked from the sidebar', function() { $('.date-summary-link').click(); - expect(Logger.log).toHaveBeenCalledWith('edx.course.home.upgrade_verified.clicked', {}); + expect(Logger.log).toHaveBeenCalledWith('edx.course.home.upgrade_verified.clicked', {location: 'sidebar'}); + }); + + it('sends an event when "Upgrade Now" is clicked from the upsell notification', function() { + $('.upgrade-banner-button').click(); + expect(Logger.log).toHaveBeenCalledWith( + 'edx.course.home.upgrade_verified.clicked', {location: 'notification'} + ); + }); + + it('sends an event when "Learn More" is clicked from the upsell notification', function() { + $('.view-verified-info').click(); + expect(Logger.log).toHaveBeenCalledWith( + 'edx.course.home.learn_about_verified.clicked', {location: 'notification'} + ); }); }); }); diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss index 2d1a57b24f..fac2c9ed18 100644 --- a/lms/static/sass/course/_info.scss +++ b/lms/static/sass/course/_info.scss @@ -1,3 +1,20 @@ +// Upgrade button +$btn-upgrade-border-color: $uxpl-green-base !default; +$btn-upgrade-background: $uxpl-green-base !default; +$btn-upgrade-color: #fcfcfc !default; +$btn-upgrade-focus-color: $btn-upgrade-color !default; +$btn-upgrade-focus-border-color: rgb(0, 155, 0) !default; +$btn-upgrade-focus-background: rgb(0, 155, 0) !default; +$btn-upgrade-active-border-color: $uxpl-green-base !default; +$btn-upgrade-active-background: $uxpl-green-base !default; + +//// Notifications +// Upgrade + +$notification-highlight-border-color: $uxpl-green-base !default; +$lms-border-color: $uxpl-gray-background !default; +$notification-background: rgb(255, 255, 255) !default + .home { @include clearfix(); max-width: 1140px; @@ -56,6 +73,110 @@ div.info-wrapper { font-style: normal; } + div.upgrade-banner { + // This banner uses the Pattern Library's defined variables + @include border-left(0px); + + border: 1px solid $lms-border-color; + width: 100%; + display: table; + + .notification-color-border { + width: 6px; //Value defined by UX team + min-height: 100%; + margin: 0; + display: table-cell; + background: $notification-highlight-border-color; + } + + .notification-content { + display: inline-flex; + align-items: center; + align-content: flex-start; + flex-flow: row wrap; + background: $notification-background; + width: 100%; + padding: $baseline/2 0; + margin-bottom: 0; + justify-content: space-between; + + .upgrade-icon { + margin: 0; + padding: $baseline/2 $baseline; + flex-flow: row nowrap; + align-items: center; + // flex: grow, shrink, base + // The 7 was the value that allowed the icon image to grow to the UX + // desired size. + flex: 7 1 50px; + // The following dimensions were added so that the + // icon will adjust as the notification is adjusted + // but will not be smaller or larger than UX requirements. + min-height: 50px; + min-width: 80px; + max-height: 90px; + max-width: 130px; + + img { + min-height: 50px; + min-width: 80px; + } + } + + .upgrade-msg { + flex: 5 1 60%; //This percentage was required to get the text + // in the message to wrap when collapsed. + flex-direction: column; + margin: 0; + padding: $baseline/2 0; + .msg-title { + font-weight: font-weight(semi-bold); + font-size: font-size(large); + line-height: $base-line-height; + } + .view-verified-info { + margin-top: $baseline/4; + font-weight: font-weight(normal); + font-size: font-size(base); + } + + a:link, a:hover, a:visited, a:active { + text-decoration: underline !important; + } + } + + .upgrade-banner-button { + @include margin(0, 0, 0, auto); + padding: $baseline/2 $baseline; + } + + .btn-upgrade { + @extend %btn-shims; + + border-color: $btn-upgrade-border-color; + background: $btn-upgrade-background; + color: $btn-upgrade-color; + // STATE: hover and focus + &:hover, + &.is-hovered, + &:focus, + &.is-focused { + border-color: $btn-upgrade-focus-border-color; + background-color: $btn-upgrade-focus-background; + color: $btn-upgrade-focus-color; + } + + // STATE: is disabled + &:disabled, + &.is-disabled { + border-color: $btn-disabled-border-color; + background: $btn-brand-disabled-background; + color: $btn-upgrade-color; + } + } + } + } + > p { margin-bottom: lh(); } diff --git a/lms/templates/courseware/info.html b/lms/templates/courseware/info.html index 7e9b874341..1891c4c5a2 100644 --- a/lms/templates/courseware/info.html +++ b/lms/templates/courseware/info.html @@ -81,6 +81,26 @@ from openedx.core.djangolib.markup import HTML, Text
% endif + % if upgrade_link: +
+
+
+
+ +
+
+

${_("Give yourself an additional incentive to complete")}

+

${_("Earn a verified certificate.")} + ${_("Learn More")} +

+
+ +
+
+ % endif +

${_("Course Updates and News")}

${HTML(get_course_info_section(request, masquerade_user, course, 'updates'))}