diff --git a/lms/djangoapps/ccx/tests/test_field_override_performance.py b/lms/djangoapps/ccx/tests/test_field_override_performance.py index 339a10b853..12bc086794 100644 --- a/lms/djangoapps/ccx/tests/test_field_override_performance.py +++ b/lms/djangoapps/ccx/tests/test_field_override_performance.py @@ -231,18 +231,18 @@ class TestFieldOverrideMongoPerformance(FieldOverridePerformanceTestCase): # # of sql queries to default, # # of mongo queries, # ) - ('no_overrides', 1, True, False): (25, 1), - ('no_overrides', 2, True, False): (25, 1), - ('no_overrides', 3, True, False): (25, 1), - ('ccx', 1, True, False): (25, 1), - ('ccx', 2, True, False): (25, 1), - ('ccx', 3, True, False): (25, 1), - ('no_overrides', 1, False, False): (25, 1), - ('no_overrides', 2, False, False): (25, 1), - ('no_overrides', 3, False, False): (25, 1), - ('ccx', 1, False, False): (25, 1), - ('ccx', 2, False, False): (25, 1), - ('ccx', 3, False, False): (25, 1), + ('no_overrides', 1, True, False): (27, 1), + ('no_overrides', 2, True, False): (27, 1), + ('no_overrides', 3, True, False): (27, 1), + ('ccx', 1, True, False): (27, 1), + ('ccx', 2, True, False): (27, 1), + ('ccx', 3, True, False): (27, 1), + ('no_overrides', 1, False, False): (27, 1), + ('no_overrides', 2, False, False): (27, 1), + ('no_overrides', 3, False, False): (27, 1), + ('ccx', 1, False, False): (27, 1), + ('ccx', 2, False, False): (27, 1), + ('ccx', 3, False, False): (27, 1), } @@ -254,19 +254,19 @@ class TestFieldOverrideSplitPerformance(FieldOverridePerformanceTestCase): __test__ = True TEST_DATA = { - ('no_overrides', 1, True, False): (25, 3), - ('no_overrides', 2, True, False): (25, 3), - ('no_overrides', 3, True, False): (25, 3), - ('ccx', 1, True, False): (25, 3), - ('ccx', 2, True, False): (25, 3), - ('ccx', 3, True, False): (25, 3), - ('ccx', 1, True, True): (26, 3), - ('ccx', 2, True, True): (26, 3), - ('ccx', 3, True, True): (26, 3), - ('no_overrides', 1, False, False): (25, 3), - ('no_overrides', 2, False, False): (25, 3), - ('no_overrides', 3, False, False): (25, 3), - ('ccx', 1, False, False): (25, 3), - ('ccx', 2, False, False): (25, 3), - ('ccx', 3, False, False): (25, 3), + ('no_overrides', 1, True, False): (27, 3), + ('no_overrides', 2, True, False): (27, 3), + ('no_overrides', 3, True, False): (27, 3), + ('ccx', 1, True, False): (27, 3), + ('ccx', 2, True, False): (27, 3), + ('ccx', 3, True, False): (27, 3), + ('ccx', 1, True, True): (28, 3), + ('ccx', 2, True, True): (28, 3), + ('ccx', 3, True, True): (28, 3), + ('no_overrides', 1, False, False): (27, 3), + ('no_overrides', 2, False, False): (27, 3), + ('no_overrides', 3, False, False): (27, 3), + ('ccx', 1, False, False): (27, 3), + ('ccx', 2, False, False): (27, 3), + ('ccx', 3, False, False): (27, 3), } diff --git a/lms/djangoapps/courseware/date_summary.py b/lms/djangoapps/courseware/date_summary.py index c0f92b92a8..0fc6797d90 100644 --- a/lms/djangoapps/courseware/date_summary.py +++ b/lms/djangoapps/courseware/date_summary.py @@ -72,9 +72,10 @@ class DateSummary(object): self.user.preferences.model.get_value(self.user, "time_zone", "UTC") ) - def __init__(self, course, user): + def __init__(self, course, user, course_id=None): self.course = course self.user = user + self.course_id = course_id or self.course.id @property def relative_datestring(self): @@ -174,7 +175,7 @@ class CourseEndDate(DateSummary): @property def description(self): if datetime.now(utc) <= self.date: - mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) + mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_id) if is_active and CourseMode.is_eligible_for_certificate(mode): return _('To earn a certificate, you must complete all requirements before this date.') else: @@ -204,10 +205,10 @@ class VerifiedUpgradeDeadlineDate(DateSummary): ecommerce_service = EcommerceService() if ecommerce_service.is_enabled(self.user): course_mode = CourseMode.objects.get( - course_id=self.course.id, mode_slug=CourseMode.VERIFIED + course_id=self.course_id, mode_slug=CourseMode.VERIFIED ) return ecommerce_service.checkout_page_url(course_mode.sku) - return reverse('verify_student_upgrade_and_verify', args=(self.course.id,)) + return reverse('verify_student_upgrade_and_verify', args=(self.course_id,)) @property def is_enabled(self): @@ -221,7 +222,7 @@ class VerifiedUpgradeDeadlineDate(DateSummary): if not is_enabled: return False - enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) + enrollment_mode, is_active = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_id) # Return `true` if user is not enrolled in course if enrollment_mode is None and is_active is None: @@ -234,7 +235,7 @@ class VerifiedUpgradeDeadlineDate(DateSummary): def date(self): try: verified_mode = CourseMode.objects.get( - course_id=self.course.id, mode_slug=CourseMode.VERIFIED + course_id=self.course_id, mode_slug=CourseMode.VERIFIED ) return verified_mode.expiration_datetime except CourseMode.DoesNotExist: @@ -273,7 +274,7 @@ class VerificationDeadlineDate(DateSummary): 'verification-deadline-retry': (_('Retry Verification'), reverse('verify_student_reverify')), 'verification-deadline-upcoming': ( _('Verify My Identity'), - reverse('verify_student_verify_now', args=(self.course.id,)) + reverse('verify_student_verify_now', args=(self.course_id,)) ) } @@ -297,13 +298,13 @@ class VerificationDeadlineDate(DateSummary): @lazy def date(self): - return VerificationDeadline.deadline_for_course(self.course.id) + return VerificationDeadline.deadline_for_course(self.course_id) @lazy def is_enabled(self): if self.date is None: return False - (mode, is_active) = CourseEnrollment.enrollment_mode_for_user(self.user, self.course.id) + (mode, is_active) = CourseEnrollment.enrollment_mode_for_user(self.user, self.course_id) if is_active and mode == 'verified': return self.verification_status in ('expired', 'none', 'must_reverify') return False diff --git a/lms/djangoapps/courseware/tests/test_course_info.py b/lms/djangoapps/courseware/tests/test_course_info.py index 9de1a6e56b..0bc1bd59b2 100644 --- a/lms/djangoapps/courseware/tests/test_course_info.py +++ b/lms/djangoapps/courseware/tests/test_course_info.py @@ -367,7 +367,7 @@ class SelfPacedCourseInfoTestCase(LoginEnrollmentTestCase, SharedModuleStoreTest self.assertEqual(resp.status_code, 200) def test_num_queries_instructor_paced(self): - self.fetch_course_info_with_queries(self.instructor_paced_course, 21, 4) + self.fetch_course_info_with_queries(self.instructor_paced_course, 23, 4) def test_num_queries_self_paced(self): - self.fetch_course_info_with_queries(self.self_paced_course, 21, 4) + self.fetch_course_info_with_queries(self.self_paced_course, 23, 4) diff --git a/lms/djangoapps/courseware/tests/test_date_summary.py b/lms/djangoapps/courseware/tests/test_date_summary.py index f8f17420b1..f1a6adb48b 100644 --- a/lms/djangoapps/courseware/tests/test_date_summary.py +++ b/lms/djangoapps/courseware/tests/test_date_summary.py @@ -310,89 +310,6 @@ class CourseDateSummaryTest(SharedModuleStoreTestCase): ) self.assertEqual(block.title, 'Course End') - # Tests Verified Upgrade Deadline Date Block - - def check_upgrade_banner( - self, - banner_expected=True, - include_url_parameter=True, - expected_cookie_value=None - ): - """ - 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) - upgrade_cookie_name = 'show_upgrade_notification' - expected_banner_text = "Give yourself an additional incentive to complete" - if banner_expected: - self.assertIn(expected_banner_text, resp.content) - self.assertIn(str(self.course.id), self.client.cookies[upgrade_cookie_name].value) - else: - self.assertNotIn(expected_banner_text, resp.content) - if upgrade_cookie_name in self.client.cookies: - self.assertNotIn(str(self.course.id), self.client.cookies[upgrade_cookie_name].value) - if expected_cookie_value is not None: - self.assertIn(str(expected_cookie_value), self.client.cookies[upgrade_cookie_name].value) - - def test_verified_upgrade_deadline_date(self): - with freeze_time('2015-01-02'): - 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) - - def test_verified_upgrade_banner_not_present_past_deadline(self): - with freeze_time('2015-01-02'): - 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) - - def test_verified_upgrade_banner_cookie(self): - with freeze_time('2015-01-02'): - 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) - - # Store the current course_id for testing - old_course_id = self.course.id - - # Change to another course - self.setup_course_and_user(days_till_upgrade_deadline=1, - user_enrollment_mode=CourseMode.AUDIT, - create_user=False) - - # Banner should not be present in the newly created course - self.check_upgrade_banner(include_url_parameter=False, - banner_expected=False, - expected_cookie_value=old_course_id) - - # 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.""" sku = 'TESTSKU' diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index c509ab0b35..90e708ea8e 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -210,8 +210,8 @@ class IndexQueryTestCase(ModuleStoreTestCase): NUM_PROBLEMS = 20 @ddt.data( - (ModuleStoreEnum.Type.mongo, 10, 145), - (ModuleStoreEnum.Type.split, 4, 145), + (ModuleStoreEnum.Type.mongo, 10, 147), + (ModuleStoreEnum.Type.split, 4, 147), ) @ddt.unpack def test_index_query_counts(self, store_type, expected_mongo_query_count, expected_mysql_query_count): @@ -572,16 +572,18 @@ class ViewsTestCase(ModuleStoreTestCase): """ registration_price = 99 self.course.cosmetic_display_price = 10 - # Since registration_price is set, it overrides the cosmetic_display_price and should be returned - self.assertEqual(views.get_cosmetic_display_price(self.course, registration_price), "$99") + with patch('course_modes.models.CourseMode.min_course_price_for_currency', return_value=registration_price): + # Since registration_price is set, it overrides the cosmetic_display_price and should be returned + self.assertEqual(views.get_cosmetic_display_price(self.course), "$99") registration_price = 0 - # Since registration_price is not set, cosmetic_display_price should be returned - self.assertEqual(views.get_cosmetic_display_price(self.course, registration_price), "$10") + with patch('course_modes.models.CourseMode.min_course_price_for_currency', return_value=registration_price): + # Since registration_price is not set, cosmetic_display_price should be returned + self.assertEqual(views.get_cosmetic_display_price(self.course), "$10") self.course.cosmetic_display_price = 0 # Since both prices are not set, there is no price, thus "Free" - self.assertEqual(views.get_cosmetic_display_price(self.course, registration_price), "Free") + self.assertEqual(views.get_cosmetic_display_price(self.course), "Free") def test_jump_to_invalid(self): # TODO add a test for invalid location @@ -1420,12 +1422,12 @@ class ProgressPageTests(ProgressPageBaseTests): """Test that query counts remain the same for self-paced and instructor-paced courses.""" SelfPacedConfiguration(enabled=self_paced_enabled).save() self.setup_course(self_paced=self_paced) - with self.assertNumQueries(41), check_mongo_calls(1): + with self.assertNumQueries(43), check_mongo_calls(1): self._get_progress_page() @ddt.data( - (False, 41, 27), - (True, 34, 23) + (False, 43, 29), + (True, 36, 25) ) @ddt.unpack def test_progress_queries(self, enable_waffle, initial, subsequent): diff --git a/lms/djangoapps/courseware/views/index.py b/lms/djangoapps/courseware/views/index.py index 9c78c63346..46878cf66e 100644 --- a/lms/djangoapps/courseware/views/index.py +++ b/lms/djangoapps/courseware/views/index.py @@ -55,7 +55,10 @@ from ..entrance_exams import ( from ..masquerade import setup_masquerade from ..model_data import FieldDataCache from ..module_render import toc_for_course, get_module_for_descriptor -from .views import CourseTabView, check_access_to_course +from .views import ( + CourseTabView, check_access_to_course, check_and_get_upgrade_link, + get_cosmetic_verified_display_price +) TEMPLATE_IMPORTS = {'urllib': urllib} @@ -149,7 +152,7 @@ class CoursewareIndex(View): self._save_positions() self._prefetch_and_bind_section() - return render_to_response('courseware/courseware.html', self._create_courseware_context()) + return render_to_response('courseware/courseware.html', self._create_courseware_context(request)) def _redirect_if_not_requested_section(self): """ @@ -319,12 +322,11 @@ class CoursewareIndex(View): save_child_position(self.course, self.chapter_url_name) save_child_position(self.chapter, self.section_url_name) - def _create_courseware_context(self): + def _create_courseware_context(self, request): """ Returns and creates the rendering context for the courseware. Also returns the table of contents for the courseware. """ - request = RequestCache.get_current_request() course_url_name = default_course_url_name(request) course_url = reverse(course_url_name, kwargs={'course_id': unicode(self.course.id)}) courseware_context = { @@ -346,6 +348,8 @@ class CoursewareIndex(View): 'section_title': None, 'sequence_title': None, 'disable_accordion': waffle.flag_is_active(request, UNIFIED_COURSE_VIEW_FLAG), + 'upgrade_link': check_and_get_upgrade_link(request, self.effective_user, self.course.id), + 'upgrade_price': get_cosmetic_verified_display_price(self.course), } table_of_contents = toc_for_course( self.effective_user, diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index 3db429ad70..c803fb7ed9 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -320,30 +320,12 @@ 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 - # Construct the dates fragment dates_fragment = None if request.user.is_authenticated(): if SelfPacedConfiguration.current().enable_course_home_improvements: dates_fragment = CourseDatesFragmentView().render_to_fragment(request, course_id=course_id) - 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 course_id in 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 - else: - # The upgrade is not enabled so the cookie does not need to be stored - store_upgrade_cookie = False context = { 'request': request, @@ -358,7 +340,8 @@ def course_info(request, course_id): 'show_enroll_banner': show_enroll_banner, 'dates_fragment': dates_fragment, 'url_to_enroll': url_to_enroll, - 'upgrade_link': upgrade_link, + 'upgrade_link': check_and_get_upgrade_link(request, user, course.id), + 'upgrade_price': get_cosmetic_verified_display_price(course), } # Get the URL of the user's last position in order to display the 'where you were last' message @@ -375,25 +358,22 @@ def course_info(request, course_id): if CourseEnrollment.is_enrolled(request.user, course.id): inject_coursetalk_keys_into_context(context, course_key) - response = render_to_response('courseware/info.html', context) - if store_upgrade_cookie: - if upgrade_cookie_name in request.COOKIES and str(course_id) not in request.COOKIES[upgrade_cookie_name]: - cookie_value = '%s,%s' % (course_id, request.COOKIES[upgrade_cookie_name]) - elif upgrade_cookie_name in request.COOKIES and str(course_id) in request.COOKIES[upgrade_cookie_name]: - cookie_value = request.COOKIES[upgrade_cookie_name] - else: - cookie_value = course_id + return render_to_response('courseware/info.html', context) - if cookie_value is not None: - response.set_cookie( - upgrade_cookie_name, - cookie_value, - 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 +UPGRADE_COOKIE_NAME = 'show_upgrade_notification' + + +def check_and_get_upgrade_link(request, user, course_id): + upgrade_link = None + + if request.user.is_authenticated(): + upgrade_data = VerifiedUpgradeDeadlineDate(None, user, course_id=course_id) + if upgrade_data.is_enabled: + upgrade_link = upgrade_data.link + request.need_to_set_upgrade_cookie = True + + return upgrade_link class StaticCourseTabView(EdxFragmentView): @@ -518,6 +498,8 @@ class CourseTabView(EdxFragmentView): 'supports_preview_menu': supports_preview_menu, 'uses_pattern_library': True, 'disable_courseware_js': True, + 'upgrade_link': check_and_get_upgrade_link(request, request.user, course.id), + 'upgrade_price': get_cosmetic_verified_display_price(course), } def render_to_fragment(self, request, course=None, page_context=None, **kwargs): @@ -569,23 +551,57 @@ def registered_for_course(course, user): return False -def get_cosmetic_display_price(course, registration_price): +def get_cosmetic_verified_display_price(course): """ - Return Course Price as a string preceded by correct currency, or 'Free' + Returns the minimum verified cert course price as a string preceded by correct currency, or 'Free'. """ + return get_course_prices(course, verified_only=True)[1] + + +def get_cosmetic_display_price(course): + """ + Returns the course price as a string preceded by correct currency, or 'Free'. + """ + return get_course_prices(course)[1] + + +def get_course_prices(course, verified_only=False): + """ + Return registration_price and cosmetic_display_prices. + registration_price is the minimum price for the course across all course modes. + cosmetic_display_prices is the course price as a string preceded by correct currency, or 'Free'. + """ + # Find the + if verified_only: + registration_price = CourseMode.min_course_price_for_verified_for_currency( + course.id, + settings.PAID_COURSE_REGISTRATION_CURRENCY[0] + ) + else: + registration_price = CourseMode.min_course_price_for_currency( + course.id, + settings.PAID_COURSE_REGISTRATION_CURRENCY[0] + ) + currency_symbol = settings.PAID_COURSE_REGISTRATION_CURRENCY[1] - price = course.cosmetic_display_price if registration_price > 0: price = registration_price + # Handle course overview objects which have no cosmetic_display_price + elif hasattr(course, 'cosmetic_display_price'): + price = course.cosmetic_display_price + else: + price = None if price: # Translators: This will look like '$50', where {currency_symbol} is a symbol such as '$' and {price} is a # numerical amount in that currency. Adjust this display as needed for your language. - return _("{currency_symbol}{price}").format(currency_symbol=currency_symbol, price=price) + cosmetic_display_price = _("{currency_symbol}{price}").format(currency_symbol=currency_symbol, price=price) else: # Translators: This refers to the cost of the course. In this case, the course costs nothing so it is free. - return _('Free') + cosmetic_display_price = _('Free') + + return registration_price, cosmetic_display_price class EnrollStaffView(View): @@ -720,12 +736,7 @@ def course_about(request, course_id): if professional_mode.bulk_sku: ecommerce_bulk_checkout_link = ecomm_service.checkout_page_url(professional_mode.bulk_sku) - # Find the minimum price for the course across all course modes - registration_price = CourseMode.min_course_price_for_currency( - course_key, - settings.PAID_COURSE_REGISTRATION_CURRENCY[0] - ) - course_price = get_cosmetic_display_price(course, registration_price) + registration_price, course_price = get_course_prices(course) # Determine which checkout workflow to use -- LMS shoppingcart or Otto basket can_add_course_to_cart = _is_shopping_cart_enabled and registration_price and not ecommerce_checkout_link @@ -889,6 +900,8 @@ def _progress(request, course_key, student_id): 'passed': is_course_passed(course, grade_summary), 'credit_course_requirements': _credit_course_requirements(course_key, student), 'certificate_data': _get_cert_data(student, course, course_key, is_active, enrollment_mode), + 'upgrade_link': check_and_get_upgrade_link(request, student, course.id), + 'upgrade_price': get_cosmetic_verified_display_price(course), } with outer_atomic(): diff --git a/lms/djangoapps/discussion/views.py b/lms/djangoapps/discussion/views.py index 3333e20e60..ee67d9e196 100644 --- a/lms/djangoapps/discussion/views.py +++ b/lms/djangoapps/discussion/views.py @@ -39,6 +39,8 @@ from django_comment_client.utils import ( strip_none ) from django_comment_common.utils import ThreadContext, get_course_discussion_settings, set_course_discussion_settings +from lms.djangoapps.courseware.views.views import check_and_get_upgrade_link, get_cosmetic_verified_display_price + from opaque_keys.edx.keys import CourseKey from openedx.core.djangoapps.plugin_api.views import EdxFragmentView from rest_framework import status @@ -440,7 +442,9 @@ def _create_discussion_board_context(request, course_key, discussion_id=None, th 'sort_preference': cc_user.default_sort_key, 'category_map': course_settings["category_map"], 'course_settings': course_settings, - 'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings) + 'is_commentable_divided': is_commentable_divided(course_key, discussion_id, course_discussion_settings), + 'upgrade_link': check_and_get_upgrade_link(request, user, course.id), + 'upgrade_price': get_cosmetic_verified_display_price(course), }) return context diff --git a/lms/templates/courseware/info.html b/lms/templates/courseware/info.html index 3ed50fb706..5fa1ec4c6c 100644 --- a/lms/templates/courseware/info.html +++ b/lms/templates/courseware/info.html @@ -82,26 +82,6 @@ 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'))} diff --git a/lms/templates/courseware/upgrade.html b/lms/templates/courseware/upgrade.html new file mode 100644 index 0000000000..06d256f899 --- /dev/null +++ b/lms/templates/courseware/upgrade.html @@ -0,0 +1,7 @@ +% if upgrade_link: + +% endif diff --git a/lms/templates/main.html b/lms/templates/main.html index 621b3e0c5a..ff2a074725 100644 --- a/lms/templates/main.html +++ b/lms/templates/main.html @@ -98,6 +98,7 @@ from pipeline_mako import render_require_js_path_overrides <%block name="headextra"/> <%block name="head_extra"/> + <%include file="/courseware/upgrade.html"/> <%static:optional_include_mako file="head-extra.html" is_theming_enabled="True" /> <%include file="widgets/optimizely.html" /> diff --git a/openedx/features/course_experience/tests/views/test_course_home.py b/openedx/features/course_experience/tests/views/test_course_home.py index 1c6f934f50..9b4eaee9ef 100644 --- a/openedx/features/course_experience/tests/views/test_course_home.py +++ b/openedx/features/course_experience/tests/views/test_course_home.py @@ -89,7 +89,7 @@ class TestCourseHomePage(SharedModuleStoreTestCase): course_home_url(self.course) # Fetch the view and verify the query counts - with self.assertNumQueries(42): + with self.assertNumQueries(45): with check_mongo_calls(5): url = course_home_url(self.course) self.client.get(url) diff --git a/openedx/features/course_experience/tests/views/test_course_updates.py b/openedx/features/course_experience/tests/views/test_course_updates.py index 01db7926fb..86fa677ecd 100644 --- a/openedx/features/course_experience/tests/views/test_course_updates.py +++ b/openedx/features/course_experience/tests/views/test_course_updates.py @@ -124,7 +124,7 @@ class TestCourseUpdatesPage(SharedModuleStoreTestCase): course_updates_url(self.course) # Fetch the view and verify that the query counts haven't changed - with self.assertNumQueries(32): + with self.assertNumQueries(34): with check_mongo_calls(4): url = course_updates_url(self.course) self.client.get(url) diff --git a/openedx/features/course_experience/views/course_outline.py b/openedx/features/course_experience/views/course_outline.py index 1c653cdb4e..b5d0231b5d 100644 --- a/openedx/features/course_experience/views/course_outline.py +++ b/openedx/features/course_experience/views/course_outline.py @@ -8,6 +8,7 @@ from opaque_keys.edx.keys import CourseKey from web_fragments.fragment import Fragment from courseware.courses import get_course_overview_with_access +from lms.djangoapps.courseware.views.views import check_and_get_upgrade_link, get_cosmetic_verified_display_price from openedx.core.djangoapps.plugin_api.views import EdxFragmentView from ..utils import get_course_outline_block_tree @@ -30,7 +31,9 @@ class CourseOutlineFragmentView(EdxFragmentView): context = { 'csrf': csrf(request)['csrf_token'], 'course': course_overview, - 'blocks': course_block_tree + 'blocks': course_block_tree, + 'upgrade_link': check_and_get_upgrade_link(request, request.user, course_key), + 'upgrade_price': get_cosmetic_verified_display_price(course_overview), } html = render_to_string('course_experience/course-outline-fragment.html', context) return Fragment(html)