diff --git a/common/djangoapps/util/views.py b/common/djangoapps/util/views.py index af4e115b52..ecad99bf84 100644 --- a/common/djangoapps/util/views.py +++ b/common/djangoapps/util/views.py @@ -1,6 +1,3 @@ - - -import ast import json import logging import sys @@ -14,7 +11,6 @@ from django.http import Http404, HttpResponse, HttpResponseForbidden, HttpRespon from django.views.decorators.csrf import ensure_csrf_cookie, requires_csrf_token from django.views.defaults import server_error from django.shortcuts import redirect -from django.urls import reverse from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey from six.moves import map @@ -201,28 +197,16 @@ def reset_course_deadlines(request): Set the start_date of a schedule to today, which in turn will adjust due dates for sequentials belonging to a self paced course """ - from lms.urls import RENDER_XBLOCK_NAME - from openedx.features.course_experience.urls import COURSE_HOME_VIEW_NAME - - detail_id_dict = ast.literal_eval(request.POST.get('reset_deadlines_redirect_url_id_dict')) - redirect_url = request.POST.get('reset_deadlines_redirect_url_base', COURSE_HOME_VIEW_NAME) - course_key = CourseKey.from_string(detail_id_dict['course_id']) - masquerade_details, masquerade_user = setup_masquerade( + course_key = CourseKey.from_string(request.POST.get('course_id')) + _course_masquerade, user = setup_masquerade( request, course_key, has_access(request.user, 'staff', course_key) ) - if masquerade_details and masquerade_details.role == 'student' and masquerade_details.user_name and ( - redirect_url == COURSE_HOME_VIEW_NAME - ): - # Masquerading as a specific student, so reset that student's schedule - user = masquerade_user - else: - user = request.user missed_deadlines, missed_gated_content = dates_banner_should_display(course_key, user) if missed_deadlines and not missed_gated_content: reset_self_paced_schedule(user, course_key) - if redirect_url == RENDER_XBLOCK_NAME: - detail_id_dict.pop('course_id') - return redirect(reverse(redirect_url, kwargs=detail_id_dict)) + + referrer = request.META.get('HTTP_REFERER') + return redirect(referrer) if referrer else HttpResponse() diff --git a/common/lib/xmodule/xmodule/capa_base.py b/common/lib/xmodule/xmodule/capa_base.py index 3d52de8a5e..fc9c0fb609 100644 --- a/common/lib/xmodule/xmodule/capa_base.py +++ b/common/lib/xmodule/xmodule/capa_base.py @@ -737,6 +737,11 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): submit_button = self.submit_button_name() submit_button_submitting = self.submit_button_submitting_name() should_enable_submit_button = self.should_enable_submit_button() + submit_disabled_ctas = None + if not should_enable_submit_button: + cta_service = self.runtime.service(self, "call_to_action") + if cta_service: + submit_disabled_ctas = cta_service.get_ctas(self, 'capa_submit_disabled') content = { 'name': self.display_name_with_default, @@ -775,6 +780,7 @@ class CapaMixin(ScorableXBlockMixin, CapaFields): 'answer_notification_message': answer_notification_message, 'has_saved_answers': self.has_saved_answers, 'save_message': save_message, + 'submit_disabled_cta': submit_disabled_ctas[0] if submit_disabled_ctas else None, } html = self.runtime.render_template('problem.html', context) diff --git a/common/lib/xmodule/xmodule/capa_module.py b/common/lib/xmodule/xmodule/capa_module.py index 1dbfa7429f..2fc2120f2f 100644 --- a/common/lib/xmodule/xmodule/capa_module.py +++ b/common/lib/xmodule/xmodule/capa_module.py @@ -37,6 +37,7 @@ log = logging.getLogger("edx.courseware") @XBlock.wants('user') @XBlock.needs('i18n') +@XBlock.wants('call_to_action') class ProblemBlock( CapaMixin, RawMixin, XmlMixin, EditingMixin, XModuleDescriptorToXBlockMixin, XModuleToXBlockMixin, HTMLSnippet, ResourceTemplates, XModuleMixin): diff --git a/common/lib/xmodule/xmodule/css/capa/display.scss b/common/lib/xmodule/xmodule/css/capa/display.scss index 2f1a8eda13..29412b79ce 100644 --- a/common/lib/xmodule/xmodule/css/capa/display.scss +++ b/common/lib/xmodule/xmodule/css/capa/display.scss @@ -995,6 +995,19 @@ div.problem .action { white-space: nowrap; } + + .submit-cta { + display: inline-block; + } + .submit-cta-description { + margin-left: 8px; + } + .submit-cta-link-button { + background: none; + border: none; + color: $blue; + cursor: pointer; + } } .submission-feedback { diff --git a/common/lib/xmodule/xmodule/vertical_block.py b/common/lib/xmodule/xmodule/vertical_block.py index 9de73be343..11d83bec69 100644 --- a/common/lib/xmodule/xmodule/vertical_block.py +++ b/common/lib/xmodule/xmodule/vertical_block.py @@ -30,6 +30,7 @@ CLASS_PRIORITY = ['video', 'problem'] @XBlock.needs('user', 'bookmarks') @XBlock.wants('completion') +@XBlock.wants('call_to_action') class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParserMixin, MakoTemplateBlockBase, XBlock): """ Layout XBlock for rendering subblocks vertically. @@ -91,6 +92,9 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse 'content': rendered_child.content }) + cta_service = self.runtime.service(self, 'call_to_action') + vertical_banner_ctas = cta_service and cta_service.get_ctas(self, 'vertical_banner') + completed = self.is_block_complete_for_assignments(completion_service) past_due = completed is False and self.due and self.due < datetime.now(pytz.UTC) fragment_context = { @@ -101,6 +105,7 @@ class VerticalBlock(SequenceFields, XModuleFields, StudioEditableBlock, XmlParse 'completed': completed, 'past_due': past_due, 'subsection_format': context.get('format', ''), + 'vertical_banner_ctas': vertical_banner_ctas, } if view == STUDENT_VIEW: diff --git a/lms/djangoapps/courseware/tests/test_views.py b/lms/djangoapps/courseware/tests/test_views.py index 2125e0307a..98827a369f 100644 --- a/lms/djangoapps/courseware/tests/test_views.py +++ b/lms/djangoapps/courseware/tests/test_views.py @@ -3205,7 +3205,7 @@ class DatesTabTestCase(ModuleStoreTestCase): graded=True, ) response = self._get_response(self.course) - self.assertContains(response, 'div class="dates-banner-text"') + self.assertContains(response, 'div class="banner-cta-text"') class TestShowCoursewareMFE(TestCase): diff --git a/lms/djangoapps/courseware/views/views.py b/lms/djangoapps/courseware/views/views.py index b23e60cf0c..fd9701d2d9 100644 --- a/lms/djangoapps/courseware/views/views.py +++ b/lms/djangoapps/courseware/views/views.py @@ -1079,8 +1079,6 @@ def dates(request, course_id): 'missed_deadlines': missed_deadlines, 'missed_gated_content': missed_gated_content, 'reset_deadlines_url': reverse(RESET_COURSE_DEADLINES_NAME), - 'reset_deadlines_redirect_url_base': COURSE_DATES_NAME, - 'reset_deadlines_redirect_url_id_dict': {'course_id': str(course.id)}, 'has_ended': course.has_ended(), } @@ -1687,8 +1685,6 @@ def render_xblock(request, usage_key_string, check_if_enrolled=True): 'is_learning_mfe': request.META.get('HTTP_REFERER', '').startswith(settings.LEARNING_MICROFRONTEND_URL), 'is_mobile_app': is_request_from_mobile_app(request), 'reset_deadlines_url': reverse(RESET_COURSE_DEADLINES_NAME), - 'reset_deadlines_redirect_url_base': COURSE_DATES_NAME, - 'reset_deadlines_redirect_url_id_dict': {'course_id': str(course.id)} } return render_to_response('courseware/courseware-chromeless.html', context) diff --git a/lms/djangoapps/lms_xblock/runtime.py b/lms/djangoapps/lms_xblock/runtime.py index d1f1b48a98..c488d0c5af 100644 --- a/lms/djangoapps/lms_xblock/runtime.py +++ b/lms/djangoapps/lms_xblock/runtime.py @@ -16,6 +16,7 @@ from lms.djangoapps.lms_xblock.models import XBlockAsidesConfig from lms.djangoapps.teams.services import TeamsService from openedx.core.djangoapps.user_api.course_tag import api as user_course_tag_api from openedx.core.lib.url_utils import quote_slashes +from openedx.core.lib.xblock_services.call_to_action import CallToActionService from openedx.core.lib.xblock_utils import wrap_xblock_aside, xblock_local_resource_url from xmodule.library_tools import LibraryToolsService from xmodule.modulestore.django import ModuleI18nService, modulestore @@ -164,6 +165,7 @@ class LmsModuleSystem(ModuleSystem): # pylint: disable=abstract-method self.request_token = kwargs.pop('request_token', None) services['teams'] = TeamsService() services['teams_configuration'] = TeamsConfigurationService() + services['call_to_action'] = CallToActionService() super(LmsModuleSystem, self).__init__(**kwargs) def handler_url(self, *args, **kwargs): diff --git a/lms/static/sass/_build-course.scss b/lms/static/sass/_build-course.scss index ce6c72b718..8388f836ea 100644 --- a/lms/static/sass/_build-course.scss +++ b/lms/static/sass/_build-course.scss @@ -22,6 +22,7 @@ @import 'course/layout/courseware_header'; @import 'course/layout/courseware_preview'; @import 'course/layout/footer'; +@import 'course/layout/banner_cta'; @import 'course/base/mixins'; @import 'course/base/base'; @import 'course/base/extends'; diff --git a/lms/static/sass/base/_base.scss b/lms/static/sass/base/_base.scss index e572f7c5f1..4cfee159fb 100644 --- a/lms/static/sass/base/_base.scss +++ b/lms/static/sass/base/_base.scss @@ -305,38 +305,3 @@ mark { } } } - -.dates-banner { - border-radius: 4px; - border: solid 1px #9cd2e6; - background-color: #eff8fa; - margin-top: 20px; - margin-bottom: 20px; - padding: 24px; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - max-width: $text-width-readability-max; - - .dates-banner-text { - font-size: 16px; - line-height: 24px; - color: #414141; - - a.mobile-dates-link { - color: #0075b4; - } - } - - &.has-button { - .dates-banner-text { - flex: 1 1 20em; - max-width: 70%; - } - } - - &.on-mobile { - margin-left: 20px; - margin-right: 20px; - } -} diff --git a/lms/static/sass/bootstrap/lms-main.scss b/lms/static/sass/bootstrap/lms-main.scss index 290b2bb84d..893a19a3c7 100644 --- a/lms/static/sass/bootstrap/lms-main.scss +++ b/lms/static/sass/bootstrap/lms-main.scss @@ -15,7 +15,7 @@ $static-path: '../..'; @import 'layouts'; @import 'components'; @import 'course/layout/courseware_preview'; -@import 'course/layout/dates_banner'; +@import 'course/layout/banner_cta'; @import 'shared/modal'; @import 'shared/help-tab'; @import './elements/banners'; diff --git a/lms/static/sass/course/_dates.scss b/lms/static/sass/course/_dates.scss index 5a69f91acc..9fa6b3753f 100644 --- a/lms/static/sass/course/_dates.scss +++ b/lms/static/sass/course/_dates.scss @@ -9,39 +9,6 @@ border-bottom: 0; } - .dates-banner { - border-radius: 4px; - border: solid 1px #9cd2e6; - background-color: #eff8fa; - margin-top: 20px; - margin-bottom: 20px; - padding: 24px; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - max-width: $text-width-readability-max; - - .dates-banner-text { - font-size: 16px; - line-height: 24px; - color: #414141; - } - - .banner-has-button { - flex: 1 1 20em; - max-width: 70%; - } - - .upgrade-button { - align-self: start; - flex: none; - - button { - @include white-button-flat-outline; - } - } - } - .timeline-item { border-left: solid 1px #2d323e; color: #2d323e; diff --git a/lms/static/sass/course/_info.scss b/lms/static/sass/course/_info.scss index ab6f6a4f17..b1331985c6 100644 --- a/lms/static/sass/course/_info.scss +++ b/lms/static/sass/course/_info.scss @@ -58,58 +58,6 @@ div.info-wrapper { width: 100%; display: block; - div.dates-banner { - // This banner uses the Pattern Library's defined variables - @include border-left(0); - - border: 1px solid $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; - } - } - } - } - > p { margin-bottom: lh(); } diff --git a/lms/static/sass/course/base/_base.scss b/lms/static/sass/course/base/_base.scss index c017cd145f..e04763c4e9 100644 --- a/lms/static/sass/course/base/_base.scss +++ b/lms/static/sass/course/base/_base.scss @@ -155,15 +155,18 @@ img { top: 0; left: 0; z-index: 99999; - padding: 0 10px; + padding: ($baseline/2); border-radius: 3px; - background: rgba(0, 0, 0, 0.85); + background: #ffffff; + border-color: #000000; + border-width: 2px; + border-style: solid; font-size: 11px; font-weight: 400; - line-height: 26px; - color: $white; + color: #000000; pointer-events: none; opacity: 0; + max-width: 200px; @include transition(opacity 0.1s linear 0s); @@ -171,11 +174,11 @@ img { content: '▾'; display: block; position: absolute; - bottom: -14px; + bottom: -($baseline - 2); left: 50%; margin-left: -7px; font-size: 20px; - color: rgba(0, 0, 0, 0.85); + color: #000000; } } diff --git a/lms/static/sass/course/base/_mixins.scss b/lms/static/sass/course/base/_mixins.scss index d8fab175eb..ac9156c82b 100644 --- a/lms/static/sass/course/base/_mixins.scss +++ b/lms/static/sass/course/base/_mixins.scss @@ -45,23 +45,6 @@ } } -@mixin white-button-flat-outline { - display: block; - border-radius: 2px; - border: solid 1px #0175b4; - background: white; - color: #2d323e; - font-size: 14px; - font-weight: bold; - line-height: 24px; - - &:hover, - &:focus, - &:active { - box-shadow: 0 2px 1px $shadow; - } -} - @mixin dark-grey-button { display: block; height: 35px; diff --git a/lms/static/sass/course/layout/_banner_cta.scss b/lms/static/sass/course/layout/_banner_cta.scss new file mode 100644 index 0000000000..b87454fa2e --- /dev/null +++ b/lms/static/sass/course/layout/_banner_cta.scss @@ -0,0 +1,73 @@ + +@mixin white-button-flat-outline { + display: block; + border-radius: 2px; + border: solid 1px #0175b4; + font-size: 14px; + font-weight: bold; + line-height: 24px; + padding: 7px 18px; + + // if you change colors here, modify them in hover block below too + background: white; + color: #2d323e; + + &:hover, + &:focus, + &:active { + cursor: pointer; + box-shadow: 0 2px 1px $shadow; + + // re-specify colors to overwrite any styling from our reset styles + background: white; + color: #2d323e; + } + } + +.banner-cta { + border-radius: 4px; + border: solid 1px #9cd2e6; + background-color: #eff8fa; + margin-top: 20px; + margin-bottom: 20px; + padding: 24px; + display: flex; + flex-wrap: wrap; + justify-content: space-between; + max-width: $text-width-readability-max; + + .banner-cta-text { + font-size: 16px; + line-height: 24px; + color: #414141; + + a.mobile-dates-link { + color: #0075b4; + } + } + + &.has-button { + .banner-cta-text { + flex: 1 1 20em; + max-width: 70%; + } + } + + &.on-mobile { + margin-left: 20px; + margin-right: 20px; + } + + .banner-cta-button { + align-self: start; + flex: none; + + a { + text-decoration: none; + } + + button { + @include white-button-flat-outline; + } + } + } diff --git a/lms/static/sass/course/layout/_dates_banner.scss b/lms/static/sass/course/layout/_dates_banner.scss deleted file mode 100644 index 813d87fb02..0000000000 --- a/lms/static/sass/course/layout/_dates_banner.scss +++ /dev/null @@ -1,53 +0,0 @@ -.dates-banner { - border-radius: 4px; - border: solid 1px #9cd2e6; - background-color: #eff8fa; - margin-top: 20px; - margin-bottom: 20px; - padding: 24px; - display: flex; - flex-wrap: wrap; - justify-content: space-between; - max-width: $text-width-readability-max; - - .dates-banner-text { - font-size: 16px; - line-height: 24px; - color: #414141; - } - - &.has-button { - .dates-banner-text { - flex: 1 1 20em; - max-width: 70%; - } - } - - .upgrade-button { - align-self: start; - flex: none; - - a { - text-decoration: none; - } - - button { - display: block; - border-radius: 2px; - border: solid 1px #0175b4; - background: white; - color: #2d323e; - font-size: 14px; - font-weight: bold; - line-height: 24px; - padding: 7px 18px; - - &:hover, - &:focus, - &:active { - cursor: pointer; - box-shadow: 0 2px 1px $shadow; - } - } - } -} diff --git a/lms/templates/dates_banner.html b/lms/templates/dates_banner.html index 34c382711d..232b45ff11 100644 --- a/lms/templates/dates_banner.html +++ b/lms/templates/dates_banner.html @@ -13,8 +13,8 @@ additional_styling_class = 'on-mobile' if is_mobile_app and not is_learning_mfe %> <%def name="reset_dates_banner()"> -
-
+