Commit 7dfe12a1 by Andy Armstrong

Show course home messages for important course dates

LEARNER-2073
parent 1aff33dc
...@@ -121,3 +121,32 @@ ...@@ -121,3 +121,32 @@
color: $btn-brand-disabled-color; color: $btn-brand-disabled-color;
} }
} }
// ----------------------------
// #UPGRADE
// ----------------------------
.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;
}
}
...@@ -143,9 +143,8 @@ $error-color: rgb(203, 7, 18) !default; ...@@ -143,9 +143,8 @@ $error-color: rgb(203, 7, 18) !default;
$success-color: rgb(0, 155, 0) !default; $success-color: rgb(0, 155, 0) !default;
$warning-color: rgb(255, 192, 31) !default; $warning-color: rgb(255, 192, 31) !default;
$warning-color-accent: rgb(255, 252, 221) !default; $warning-color-accent: rgb(255, 252, 221) !default;
$general-color: $uxpl-blue-base !default;; $general-color: $uxpl-blue-base !default;
$general-color-accent: $uxpl-blue-base !default $general-color-accent: $uxpl-blue-base !default;
// CAPA correctness color to be consistent with Alert styles above // CAPA correctness color to be consistent with Alert styles above
$correct: $success-color !default; $correct: $success-color !default;
...@@ -181,6 +180,16 @@ $btn-brand-active-background: $uxpl-blue-base !default; ...@@ -181,6 +180,16 @@ $btn-brand-active-background: $uxpl-blue-base !default;
$btn-brand-disabled-background: #f2f3f3 !default; $btn-brand-disabled-background: #f2f3f3 !default;
$btn-brand-disabled-color: #676666 !default; $btn-brand-disabled-color: #676666 !default;
// 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;
// ---------------------------- // ----------------------------
// #SETTINGS // #SETTINGS
// ---------------------------- // ----------------------------
......
...@@ -4,6 +4,8 @@ from urlparse import urljoin ...@@ -4,6 +4,8 @@ from urlparse import urljoin
import waffle import waffle
from django.conf import settings from django.conf import settings
from django.core.urlresolvers import reverse
from student.models import CourseEnrollment
from commerce.models import CommerceConfiguration from commerce.models import CommerceConfiguration
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
...@@ -93,3 +95,16 @@ class EcommerceService(object): ...@@ -93,3 +95,16 @@ class EcommerceService(object):
checkout_page_path=self.get_absolute_ecommerce_url(self.config.MULTIPLE_ITEMS_BASKET_PAGE_URL), checkout_page_path=self.get_absolute_ecommerce_url(self.config.MULTIPLE_ITEMS_BASKET_PAGE_URL),
skus=urlencode({'sku': skus}, doseq=True), skus=urlencode({'sku': skus}, doseq=True),
) )
def upgrade_url(self, user, course_key):
"""
Returns the URL for the user to upgrade, or None if not applicable.
"""
enrollment = CourseEnrollment.get_enrollment(user, course_key)
verified_mode = enrollment.verified_mode if enrollment else None
if verified_mode:
if self.is_enabled(user):
return self.get_checkout_page_url(verified_mode.sku)
else:
return reverse('verify_student_upgrade_and_verify', args=(course_key,))
return None
...@@ -213,8 +213,8 @@ class IndexQueryTestCase(ModuleStoreTestCase): ...@@ -213,8 +213,8 @@ class IndexQueryTestCase(ModuleStoreTestCase):
NUM_PROBLEMS = 20 NUM_PROBLEMS = 20
@ddt.data( @ddt.data(
(ModuleStoreEnum.Type.mongo, 10, 145), (ModuleStoreEnum.Type.mongo, 10, 147),
(ModuleStoreEnum.Type.split, 4, 145), (ModuleStoreEnum.Type.split, 4, 147),
) )
@ddt.unpack @ddt.unpack
def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count): def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count):
......
...@@ -431,6 +431,9 @@ XQUEUE_WAITTIME_BETWEEN_REQUESTS = 5 # seconds ...@@ -431,6 +431,9 @@ XQUEUE_WAITTIME_BETWEEN_REQUESTS = 5 # seconds
RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS = 5 RETRY_ACTIVATION_EMAIL_MAX_ATTEMPTS = 5
RETRY_ACTIVATION_EMAIL_TIMEOUT = 0.5 RETRY_ACTIVATION_EMAIL_TIMEOUT = 0.5
# Deadline message configurations
COURSE_MESSAGE_ALERT_DURATION_IN_DAYS = 14
############################# SET PATH INFORMATION ############################# ############################# SET PATH INFORMATION #############################
PROJECT_ROOT = path(__file__).abspath().dirname().dirname() # /edx-platform/lms PROJECT_ROOT = path(__file__).abspath().dirname().dirname() # /edx-platform/lms
REPO_ROOT = PROJECT_ROOT.dirname() REPO_ROOT = PROJECT_ROOT.dirname()
...@@ -2589,6 +2592,7 @@ MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS = 15 * 60 ...@@ -2589,6 +2592,7 @@ MAX_FAILED_LOGIN_ATTEMPTS_LOCKOUT_PERIOD_SECS = 15 * 60
TIME_ZONE_DISPLAYED_FOR_DEADLINES = 'UTC' TIME_ZONE_DISPLAYED_FOR_DEADLINES = 'UTC'
########################## VIDEO IMAGE STORAGE ############################ ########################## VIDEO IMAGE STORAGE ############################
VIDEO_IMAGE_SETTINGS = dict( VIDEO_IMAGE_SETTINGS = dict(
......
...@@ -7,3 +7,7 @@ ...@@ -7,3 +7,7 @@
@import 'base/variables'; @import 'base/variables';
@import 'base/mixins'; @import 'base/mixins';
@import 'base/theme'; @import 'base/theme';
// Pattern Library shims
@import 'edx-pattern-library-shims/base/variables';
@import 'edx-pattern-library-shims/buttons';
...@@ -21,6 +21,7 @@ ...@@ -21,6 +21,7 @@
// Elements // Elements
@import 'notifications'; @import 'notifications';
@import 'elements/controls'; @import 'elements/controls';
@import 'elements-v2/buttons';
@import 'elements-v2/pagination'; @import 'elements-v2/pagination';
// Features // Features
......
// 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 //// Notifications
// Upgrade // Upgrade
...@@ -142,31 +132,6 @@ div.info-wrapper { ...@@ -142,31 +132,6 @@ div.info-wrapper {
@include margin(0, 0, 0, auto); @include margin(0, 0, 0, auto);
padding: $baseline/2 $baseline; 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;
}
}
} }
} }
......
// ----------------------------
// #UPGRADE
// ----------------------------
$upgrade-color: #009b00 !default;
$upgrade-dark-color: #008100 !default;
.btn-upgrade {
@extend %btn;
border-color: $upgrade-color;
background: $upgrade-color;
color: palette(primary, x-back);
text-decoration: none;
// STATE: hover and focus
&:hover,
&.is-hovered,
&:focus,
&.is-focused {
border-color: $upgrade-dark-color;
background: $upgrade-dark-color;
text-decoration: none;
}
// STATE: is pressed or active
&:active,
&.is-pressed,
&.is-active {
border-color: $upgrade-dark-color;
background: $upgrade-dark-color;
text-decoration: none;
}
// STATE: is disabled
&:disabled,
&.is-disabled {
border-color: $btn-disabled-border-color;
background: $btn-disabled-background-color;
color: $btn-disabled-text-color;
text-decoration: none;
}
}
...@@ -16,6 +16,7 @@ ...@@ -16,6 +16,7 @@
.message-content { .message-content {
@include margin(0, 0, $baseline, $baseline); @include margin(0, 0, $baseline, $baseline);
position: relative; position: relative;
border: 1px solid $lms-border-color; border: 1px solid $lms-border-color;
padding: $baseline; padding: $baseline;
...@@ -60,15 +61,17 @@ ...@@ -60,15 +61,17 @@
.message-header { .message-header {
font-weight: $font-semibold; font-weight: $font-semibold;
margin-bottom: $baseline/2; margin-bottom: $baseline/2;
width: calc(100% - 40px) width: calc(100% - 40px);
} }
a { a:not(.btn) {
font-weight: $font-semibold; font-weight: $font-semibold;
text-decoration: underline; text-decoration: underline;
} }
.dismiss { .dismiss {
@include right($baseline/4); @include right($baseline/4);
top: $baseline/4; top: $baseline/4;
position: absolute; position: absolute;
cursor: pointer; cursor: pointer;
...@@ -90,6 +93,7 @@ ...@@ -90,6 +93,7 @@
&.dismissible { &.dismissible {
@include right($baseline/4); @include right($baseline/4);
position: absolute; position: absolute;
top: $baseline/2; top: $baseline/2;
font-size: font-size(small); font-size: font-size(small);
...@@ -103,6 +107,12 @@ ...@@ -103,6 +107,12 @@
} }
} }
} }
.message-actions {
display: flex;
margin-top: $baseline/2;
justify-content: flex-end;
}
} }
// Welcome message / Latest Update message // Welcome message / Latest Update message
......
...@@ -111,10 +111,6 @@ ...@@ -111,10 +111,6 @@
.action-upgrade-certificate { .action-upgrade-certificate {
position: absolute; position: absolute;
right: $baseline; right: $baseline;
background-color: $success-color;
border-color: $success-color;
background-image: none;
box-shadow: none;
@media (max-width: 960px) { @media (max-width: 960px) {
& { & {
...@@ -142,11 +138,6 @@ ...@@ -142,11 +138,6 @@
top: auto; top: auto;
} }
} }
&:hover {
background-color: $success-color-hover;
border-color: $success-color-hover;
}
} }
} }
} }
......
...@@ -70,13 +70,6 @@ $upgrade-message-background-color: $blue-d1; ...@@ -70,13 +70,6 @@ $upgrade-message-background-color: $blue-d1;
color: $white; color: $white;
} }
// Upgrade Button
.btn-upgrade {
@extend %btn-primary-green;
background: $uxpl-green-base;
}
// Cert image // Cert image
.vc-hero { .vc-hero {
@include float(right); @include float(right);
......
...@@ -28,8 +28,12 @@ SHOW_REVIEWS_TOOL_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_reviews_t ...@@ -28,8 +28,12 @@ SHOW_REVIEWS_TOOL_FLAG = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_reviews_t
# Waffle flag to enable the setting of course goals. # Waffle flag to enable the setting of course goals.
ENABLE_COURSE_GOALS = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enable_course_goals') ENABLE_COURSE_GOALS = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'enable_course_goals')
# Waffle flag to control the display of the hero
SHOW_UPGRADE_MSG_ON_COURSE_HOME = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_upgrade_msg_on_course_home') SHOW_UPGRADE_MSG_ON_COURSE_HOME = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'show_upgrade_msg_on_course_home')
# Waffle flag to control the display of the upgrade deadline message
UPGRADE_DEADLINE_MESSAGE = CourseWaffleFlag(WAFFLE_FLAG_NAMESPACE, 'upgrade_deadline_message')
# Waffle flag to switch between the 'welcome message' and 'latest update' on the course home page. # Waffle flag to switch between the 'welcome message' and 'latest update' on the course home page.
# Important Admin Note: This is meant to be configured using waffle_utils course # Important Admin Note: This is meant to be configured using waffle_utils course
# override only. Either do not create the actual waffle flag, or be sure to unset the # override only. Either do not create the actual waffle flag, or be sure to unset the
......
...@@ -82,7 +82,7 @@ from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, SHOW_REV ...@@ -82,7 +82,7 @@ from openedx.features.course_experience import UNIFIED_COURSE_TAB_FLAG, SHOW_REV
</ul> </ul>
<div class="vc-cta vc-fade vc-polite-only"> <div class="vc-cta vc-fade vc-polite-only">
<a class="btn-upgrade" href="${ upgrade_url }">${_("Upgrade ({price})").format(price='$' + str(upgrade_price))}</a> <a class="btn-upgrade" href="${ upgrade_url }">${_("Upgrade ({price})").format(price=upgrade_price)}</a>
</div> </div>
</div> </div>
</div> </div>
......
...@@ -55,9 +55,9 @@ from openedx.features.course_experience import DISPLAY_COURSE_SOCK_FLAG ...@@ -55,9 +55,9 @@ from openedx.features.course_experience import DISPLAY_COURSE_SOCK_FLAG
</div> </div>
</div> </div>
<img class="mini-cert" src="${static.url('course_experience/images/verified-cert.png')}"/> <img class="mini-cert" src="${static.url('course_experience/images/verified-cert.png')}"/>
<a href="/verify_student/upgrade/${course_id}/"> <a href="${upgrade_url}">
<button type="button" class="btn btn-brand stuck-top focusable action-upgrade-certificate"> <button type="button" class="btn btn-upgrade stuck-top focusable action-upgrade-certificate">
Upgrade Now (${HTML(course_price)}) Upgrade (${HTML(course_price)})
</button> </button>
</a> </a>
</div> </div>
......
...@@ -173,7 +173,7 @@ class TestCourseHomePage(CourseHomePageTestCase): ...@@ -173,7 +173,7 @@ class TestCourseHomePage(CourseHomePageTestCase):
course_home_url(self.course) course_home_url(self.course)
# Fetch the view and verify the query counts # Fetch the view and verify the query counts
with self.assertNumQueries(44, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST): with self.assertNumQueries(45, table_blacklist=QUERY_COUNT_TABLE_BLACKLIST):
with check_mongo_calls(4): with check_mongo_calls(4):
url = course_home_url(self.course) url = course_home_url(self.course)
self.client.get(url) self.client.get(url)
...@@ -477,11 +477,9 @@ class CourseHomeFragmentViewTests(ModuleStoreTestCase): ...@@ -477,11 +477,9 @@ class CourseHomeFragmentViewTests(ModuleStoreTestCase):
response = self.client.get(self.url) response = self.client.get(self.url)
self.assertIn('vc-message', response.content) self.assertIn('vc-message', response.content)
url = EcommerceService().get_checkout_page_url(self.verified_mode.sku) url = EcommerceService().get_checkout_page_url(self.verified_mode.sku)
expected = '<a class="btn-upgrade" href="{url}">Upgrade (${price})</a>'.format( self.assertIn('<a class="btn-upgrade"', response.content)
url=url, self.assertIn(url, response.content)
price=self.verified_mode.min_price self.assertIn('Upgrade (${price})</a>'.format(price=self.verified_mode.min_price), response.content)
)
self.assertIn(expected, response.content)
def test_no_upgrade_message_if_logged_out(self): def test_no_upgrade_message_if_logged_out(self):
self.client.logout() self.client.logout()
......
...@@ -10,6 +10,7 @@ from django.views.decorators.cache import cache_control ...@@ -10,6 +10,7 @@ from django.views.decorators.cache import cache_control
from django.views.decorators.csrf import ensure_csrf_cookie from django.views.decorators.csrf import ensure_csrf_cookie
from commerce.utils import EcommerceService from commerce.utils import EcommerceService
from course_modes.models import get_cosmetic_verified_display_price
from courseware.access import has_access from courseware.access import has_access
from courseware.courses import ( from courseware.courses import (
can_self_enroll_in_course, can_self_enroll_in_course,
...@@ -165,15 +166,8 @@ class CourseHomeFragmentView(EdxFragmentView): ...@@ -165,15 +166,8 @@ class CourseHomeFragmentView(EdxFragmentView):
# TODO Add switch to control deployment # TODO Add switch to control deployment
if SHOW_UPGRADE_MSG_ON_COURSE_HOME.is_enabled(course_key) and enrollment and enrollment.upgrade_deadline: if SHOW_UPGRADE_MSG_ON_COURSE_HOME.is_enabled(course_key) and enrollment and enrollment.upgrade_deadline:
verified_mode = enrollment.verified_mode upgrade_url = EcommerceService().upgrade_url(request.user, course_key)
if verified_mode: upgrade_price = get_cosmetic_verified_display_price(course)
upgrade_price = verified_mode.min_price
ecommerce_service = EcommerceService()
if ecommerce_service.is_enabled(request.user):
upgrade_url = ecommerce_service.get_checkout_page_url(verified_mode.sku)
else:
upgrade_url = reverse('verify_student_upgrade_and_verify', args=(course_key,))
# Render the course home fragment # Render the course home fragment
context = { context = {
......
...@@ -6,10 +6,11 @@ from django.utils.translation import get_language ...@@ -6,10 +6,11 @@ from django.utils.translation import get_language
from opaque_keys.edx.keys import CourseKey from opaque_keys.edx.keys import CourseKey
from web_fragments.fragment import Fragment from web_fragments.fragment import Fragment
from student.models import CourseEnrollment from commerce.utils import EcommerceService
from course_modes.models import CourseMode, get_cosmetic_verified_display_price from course_modes.models import CourseMode, get_cosmetic_verified_display_price
from courseware.date_summary import VerifiedUpgradeDeadlineDate from courseware.date_summary import VerifiedUpgradeDeadlineDate
from openedx.core.djangoapps.plugin_api.views import EdxFragmentView from openedx.core.djangoapps.plugin_api.views import EdxFragmentView
from student.models import CourseEnrollment
class CourseSockFragmentView(EdxFragmentView): class CourseSockFragmentView(EdxFragmentView):
...@@ -44,13 +45,15 @@ class CourseSockFragmentView(EdxFragmentView): ...@@ -44,13 +45,15 @@ class CourseSockFragmentView(EdxFragmentView):
not deadline_has_passed and get_language() == 'en' not deadline_has_passed and get_language() == 'en'
) )
# Get the price of the course and format correctly # Get information about the upgrade
course_price = get_cosmetic_verified_display_price(course) course_price = get_cosmetic_verified_display_price(course)
upgrade_url = EcommerceService().upgrade_url(request.user, course_key)
context = { context = {
'show_course_sock': show_course_sock, 'show_course_sock': show_course_sock,
'course_price': course_price, 'course_price': course_price,
'course_id': course.id 'course_id': course.id,
'upgrade_url': upgrade_url,
} }
return context return context
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment