From 1990dc7fd89ef25719e40e9d0a60e7cfba6e9374 Mon Sep 17 00:00:00 2001 From: Brandon DeRosier Date: Thu, 20 Jul 2017 17:56:56 -0400 Subject: [PATCH 1/8] Patch the bulk_enroll request to use a form content type Setting the content type to be form data makes Django Rest Framework v3.6.3 treat all passed JSON data as POST parameters. This is necessary because this request is forwarded on to the student_update_enrollment view, which requires all of the parameters to be passed in via POST parameters. --- .../bulk_enroll/tests/test_views.py | 26 ++++++++++++++----- lms/djangoapps/bulk_enroll/views.py | 6 +++++ 2 files changed, 25 insertions(+), 7 deletions(-) diff --git a/lms/djangoapps/bulk_enroll/tests/test_views.py b/lms/djangoapps/bulk_enroll/tests/test_views.py index 99eaac5234..f805a73bbc 100644 --- a/lms/djangoapps/bulk_enroll/tests/test_views.py +++ b/lms/djangoapps/bulk_enroll/tests/test_views.py @@ -1,6 +1,7 @@ """ Tests for the Bulk Enrollment views. """ +import ddt import json from django.conf import settings from django.contrib.auth.models import User @@ -25,6 +26,7 @@ from xmodule.modulestore.tests.factories import CourseFactory @override_settings(ENABLE_BULK_ENROLLMENT_VIEW=True) +@ddt.ddt class BulkEnrollmentTest(ModuleStoreTestCase, LoginEnrollmentTestCase, APITestCase): """ Test the bulk enrollment endpoint @@ -67,9 +69,13 @@ class BulkEnrollmentTest(ModuleStoreTestCase, LoginEnrollmentTestCase, APITestCa self.about_path = '/courses/{}/about'.format(self.course.id) self.course_path = '/courses/{}/'.format(self.course.id) - def request_bulk_enroll(self, data=None, **extra): + def request_bulk_enroll(self, data=None, use_json=False, **extra): """ Make an authenticated request to the bulk enrollment API. """ - request = self.request_factory.post(self.url, data=data, **extra) + content_type = None + if use_json: + content_type = 'application/json' + data = json.dumps(data) + request = self.request_factory.post(self.url, data=data, content_type=content_type, **extra) force_authenticate(request, user=self.staff) response = self.view(request) response.render() @@ -221,14 +227,15 @@ class BulkEnrollmentTest(ModuleStoreTestCase, LoginEnrollmentTestCase, APITestCa res_json = json.loads(response.content) self.assertEqual(res_json, expected) - def test_enroll_with_email(self): + @ddt.data(False, True) + def test_enroll_with_email(self, use_json): """ Test enrolling using a username as the identifier. """ response = self.request_bulk_enroll({ 'identifiers': self.notenrolled_student.email, 'action': 'enroll', 'email_students': False, 'courses': self.course_key, - }) + }, use_json=use_json) self.assertEqual(response.status_code, 200) # test that the user is now enrolled @@ -274,10 +281,15 @@ class BulkEnrollmentTest(ModuleStoreTestCase, LoginEnrollmentTestCase, APITestCa # Check the outbox self.assertEqual(len(mail.outbox), 0) - def test_unenroll(self): + @ddt.data(False, True) + def test_unenroll(self, use_json): """ Test unenrolling a user. """ - response = self.request_bulk_enroll({'identifiers': self.enrolled_student.email, 'action': 'unenroll', - 'email_students': False, 'courses': self.course_key, }) + response = self.request_bulk_enroll({ + 'identifiers': self.enrolled_student.email, + 'action': 'unenroll', + 'email_students': False, + 'courses': self.course_key, + }, use_json=use_json) self.assertEqual(response.status_code, 200) # test that the user is now unenrolled diff --git a/lms/djangoapps/bulk_enroll/views.py b/lms/djangoapps/bulk_enroll/views.py index fe768853aa..2a2520cad8 100644 --- a/lms/djangoapps/bulk_enroll/views.py +++ b/lms/djangoapps/bulk_enroll/views.py @@ -60,6 +60,12 @@ class BulkEnrollView(APIView): def post(self, request): serializer = BulkEnrollmentSerializer(data=request.data) if serializer.is_valid(): + # Setting the content type to be form data makes Django Rest Framework v3.6.3 treat all passed JSON data as + # POST parameters. This is necessary because this request is forwarded on to the student_update_enrollment + # view, which requires all of the parameters to be passed in via POST parameters. + metadata = request._request.META # pylint: disable=protected-access + metadata['CONTENT_TYPE'] = 'application/x-www-form-urlencoded' + response_dict = { 'auto_enroll': serializer.data.get('auto_enroll'), 'email_students': serializer.data.get('email_students'), From e82053ee432a059bde6fa48814f394e0e51380b4 Mon Sep 17 00:00:00 2001 From: Afzal Wali Date: Fri, 21 Jul 2017 18:25:19 +0500 Subject: [PATCH 2/8] Clearing the cache if the site's configuration was removed. The site's configuration does not contain the API URL, it should not keep on displayed the old cached list of uuids. LEARNER-1146 --- .../djangoapps/catalog/management/commands/cache_programs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/openedx/core/djangoapps/catalog/management/commands/cache_programs.py b/openedx/core/djangoapps/catalog/management/commands/cache_programs.py index fc8c86316d..9276c94a4b 100644 --- a/openedx/core/djangoapps/catalog/management/commands/cache_programs.py +++ b/openedx/core/djangoapps/catalog/management/commands/cache_programs.py @@ -50,6 +50,7 @@ class Command(BaseCommand): site_config = getattr(site, 'configuration', None) if site_config is None or not site_config.get_value('COURSE_CATALOG_API_URL'): logger.info('Skipping site {domain}. No configuration.'.format(domain=site.domain)) + cache.set(SITE_PROGRAM_UUIDS_CACHE_KEY_TPL.format(domain=site.domain), [], None) continue client = create_catalog_api_client(user, site=site) From 038d5a1351bd604f98ea707f1b497c83fc5c50b0 Mon Sep 17 00:00:00 2001 From: Ari Rizzitano Date: Wed, 19 Jul 2017 15:22:54 -0400 Subject: [PATCH 3/8] catch WebpackLoaderBadStatsError to prevent unittest concurrency errors [LEARNER-1938] try also catching WebpackLoaderBadStatsError [LEARNER-1938] catch BaseWebpackLoaderException also do i need to import the exception? add better logging --- .../djangoapps/pipeline_mako/templates/static_content.html | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/common/djangoapps/pipeline_mako/templates/static_content.html b/common/djangoapps/pipeline_mako/templates/static_content.html index 2b23ddbb06..e59b2636e3 100644 --- a/common/djangoapps/pipeline_mako/templates/static_content.html +++ b/common/djangoapps/pipeline_mako/templates/static_content.html @@ -93,6 +93,7 @@ source, template_path = Loader(engine).load_template_source(path) <% from django.template import Template, Context + from webpack_loader.exceptions import WebpackLoaderBadStatsError try: return Template(""" {% load render_bundle from webpack_loader %} @@ -105,9 +106,9 @@ source, template_path = Loader(engine).load_template_source(path) 'entry': entry, 'body': capture(caller.body) })) - except IOError as e: + except (IOError, WebpackLoaderBadStatsError) as e: # Don't break Mako template rendering if the bundle or webpack-stats can't be found, but log it - logger.error(e) + logger.error('[LEARNER-1938] {error}'.format(error=e)) %> From 66fdd2fbd379eb25468d2d197504b3c3983774c6 Mon Sep 17 00:00:00 2001 From: Sanford Student Date: Thu, 20 Jul 2017 15:47:52 -0400 Subject: [PATCH 4/8] EDUCATOR-915: force subsection grades to update when course grade updates --- lms/djangoapps/grades/new/course_grade.py | 12 ++++++-- .../grades/new/course_grade_factory.py | 29 ++++++++++++++----- lms/djangoapps/grades/tasks.py | 2 +- lms/djangoapps/grades/tests/test_new.py | 14 +++++++++ 4 files changed, 46 insertions(+), 11 deletions(-) diff --git a/lms/djangoapps/grades/new/course_grade.py b/lms/djangoapps/grades/new/course_grade.py index 745c6434b4..b438001d07 100644 --- a/lms/djangoapps/grades/new/course_grade.py +++ b/lms/djangoapps/grades/new/course_grade.py @@ -21,7 +21,7 @@ class CourseGradeBase(object): """ Base class for Course Grades. """ - def __init__(self, user, course_data, percent=0, letter_grade=None, passed=False): + def __init__(self, user, course_data, percent=0, letter_grade=None, passed=False, force_update_subsections=False): self.user = user self.course_data = course_data @@ -30,6 +30,7 @@ class CourseGradeBase(object): # Convert empty strings to None when reading from the table self.letter_grade = letter_grade or None + self.force_update_subsections = force_update_subsections def __unicode__(self): return u'Course Grade: percent: {}, letter_grade: {}, passed: {}'.format( @@ -203,7 +204,9 @@ class CourseGrade(CourseGradeBase): def update(self): """ - Updates the grade for the course. + Updates the grade for the course. Also updates subsection grades + if self.force_update_subsections is true, via the lazy call + to self.grader_result. """ grade_cutoffs = self.course_data.course.grade_cutoffs self.percent = self._compute_percent(self.grader_result) @@ -224,7 +227,10 @@ class CourseGrade(CourseGradeBase): def _get_subsection_grade(self, subsection): # Pass read_only here so the subsection grades can be persisted in bulk at the end. - return self._subsection_grade_factory.create(subsection, read_only=True) + if self.force_update_subsections: + return self._subsection_grade_factory.update(subsection) + else: + return self._subsection_grade_factory.create(subsection, read_only=True) @staticmethod def _compute_percent(grader_result): diff --git a/lms/djangoapps/grades/new/course_grade_factory.py b/lms/djangoapps/grades/new/course_grade_factory.py index 9085436068..4ffe091b79 100644 --- a/lms/djangoapps/grades/new/course_grade_factory.py +++ b/lms/djangoapps/grades/new/course_grade_factory.py @@ -66,7 +66,15 @@ class CourseGradeFactory(object): else: return None - def update(self, user, course=None, collected_block_structure=None, course_structure=None, course_key=None): + def update( + self, + user, + course=None, + collected_block_structure=None, + course_structure=None, + course_key=None, + force_update_subsections=False, + ): """ Computes, updates, and returns the CourseGrade for the given user in the course. @@ -75,7 +83,7 @@ class CourseGradeFactory(object): or course_key should be provided. """ course_data = CourseData(user, course, collected_block_structure, course_structure, course_key) - return self._update(user, course_data, read_only=False) + return self._update(user, course_data, read_only=False, force_update_subsections=force_update_subsections) @contextmanager def _course_transaction(self, course_key): @@ -118,10 +126,17 @@ class CourseGradeFactory(object): def _iter_grade_result(self, user, course_data, force_update): try: + kwargs = { + 'user': user, + 'course': course_data.course, + 'collected_block_structure': course_data.collected_structure, + 'course_key': course_data.course_key + } + if force_update: + kwargs['force_update_subsections'] = True + method = CourseGradeFactory().update if force_update else CourseGradeFactory().create - course_grade = method( - user, course_data.course, course_data.collected_structure, course_key=course_data.course_key, - ) + course_grade = method(**kwargs) return self.GradeResult(user, course_grade, None) except Exception as exc: # pylint: disable=broad-except # Keep marching on even if this student couldn't be graded for @@ -165,14 +180,14 @@ class CourseGradeFactory(object): return course_grade, persistent_grade.grading_policy_hash @staticmethod - def _update(user, course_data, read_only): + def _update(user, course_data, read_only, force_update_subsections=False): """ Computes, saves, and returns a CourseGrade object for the given user and course. Sends a COURSE_GRADE_CHANGED signal to listeners and a COURSE_GRADE_NOW_PASSED if learner has passed course. """ - course_grade = CourseGrade(user, course_data) + course_grade = CourseGrade(user, course_data, force_update_subsections=force_update_subsections) course_grade.update() should_persist = ( diff --git a/lms/djangoapps/grades/tasks.py b/lms/djangoapps/grades/tasks.py index 3bb8d9e3f9..3e4df7605e 100644 --- a/lms/djangoapps/grades/tasks.py +++ b/lms/djangoapps/grades/tasks.py @@ -106,7 +106,7 @@ def compute_grades_for_course_v2(self, **kwargs): @task(base=_BaseTask) def compute_grades_for_course(course_key, offset, batch_size, **kwargs): # pylint: disable=unused-argument """ - Compute grades for a set of students in the specified course. + Compute and save grades for a set of students in the specified course. The set of students will be determined by the order of enrollment date, and limited to at most students, starting from the specified diff --git a/lms/djangoapps/grades/tests/test_new.py b/lms/djangoapps/grades/tests/test_new.py index d5a942df0c..fb7feccbf0 100644 --- a/lms/djangoapps/grades/tests/test_new.py +++ b/lms/djangoapps/grades/tests/test_new.py @@ -246,6 +246,20 @@ class TestCourseGradeFactory(GradeTestBase): else: self.assertIsNone(course_grade) + @ddt.data(True, False) + def test_iter_force_update(self, force_update): + base_string = 'lms.djangoapps.grades.new.subsection_grade_factory.SubsectionGradeFactory.{}' + desired_method_name = base_string.format('update' if force_update else 'create') + undesired_method_name = base_string.format('create' if force_update else 'update') + with patch(desired_method_name) as desired_call: + with patch(undesired_method_name) as undesired_call: + set(CourseGradeFactory().iter( + users=[self.request.user], course=self.course, force_update=force_update + )) + + self.assertTrue(desired_call.called) + self.assertFalse(undesired_call.called) + @ddt.ddt class TestSubsectionGradeFactory(ProblemSubmissionTestMixin, GradeTestBase): From c8250fd573f5f1f8141addeb5d5fd4d7472ece10 Mon Sep 17 00:00:00 2001 From: Matthew Piatetsky Date: Wed, 19 Jul 2017 17:38:44 -0400 Subject: [PATCH 5/8] Implement track selection v3 test LEARNER-1726 --- lms/static/sass/_experiments.scss | 426 ++++++++++++------ .../lms/templates/course_modes/choose.html | 31 +- 2 files changed, 305 insertions(+), 152 deletions(-) diff --git a/lms/static/sass/_experiments.scss b/lms/static/sass/_experiments.scss index fee87f3911..cb1e952aea 100644 --- a/lms/static/sass/_experiments.scss +++ b/lms/static/sass/_experiments.scss @@ -4,22 +4,26 @@ // Please list the ticket number of the experiment // -------------------- -// LEARNER-1312 Track Selection V2 -/* This css was added as part of the LEARNER-1312 experiment */ +// LEARNER-1726 Track Selection V3 +/* This css was added as part of the LEARNER-1726 experiment */ .v2.register-choice { margin: 0 2% 20px 0 !important } + .v2.register-choice-certificate .list-actions { text-align: left !important; } + .v2.register-choice-donate .list-actions { margin-bottom: 0 !important; } + .v2.register-choice-donate .action-select { display: inline-block !important; list-style-type: none !important; width: 100% !important; } + .v2.register-choice-donate .donation-link { display: inline-block !important; padding: 10px 15px !important; @@ -30,219 +34,361 @@ text-align: center !important; color: #D7548E !important; float: left !important; + font-size: 15px; + font-weight: 500 !important; } + +@media (min-width: 375px) { + .donation-link { + font-size: 16px; + } +} + .v2.register-choice-v2-audit { - height: 250px !important; + height: 300px; background: none !important; border-top-color: grey !important; border-top-width: 1px !important; } + +@media screen and (min-width: 375px) { + .v2.register-choice-v2-audit { + height: 250px; + } +} + .v2.register-choice-v2-audit .list-actions { margin-bottom: 0 !important; } + .v2.register-choice-v2-audit .list-actions input { background: transparent !important; color: #0075b4 !important; box-shadow: none !important; text-decoration: underline !important; border: none !important; + white-space: normal; } + .v2.register-choice-v2-audit .wrapper-copy-inline { height: 70px !important; width: 100% !important; display: flex !important; } + .v2.register-choice-v2-audit .wrapper-copy { width: 70% !important; height: auto !important; } .v2.page-header { - padding-bottom: 0; + padding: 0; } + .v2 img { margin-top: 20px; margin-left: 5px; } + .v2 .donation-link { font-weight: bold !important; } -@media (min-width: 320px) { - .v2.register-choice-certificate, - .v2.register-choice-donate, - .v2.register-choice-view { - width: 100%; - } - .v2 .wrapper-copy-inline { - max-height: 115px; - } - .v2.register-choice-v2-audit .wrapper-copy-inline { - display: block !important; - } - .v2.register-choice-v2-audit .copy-inline { - width: 100% !important; - } - .v2.register-choice-v2-audit .list-actions { - width: 100% !important; - margin-top: 20px !important; - text-align: center !important; - } - .v2 .wrapper-copy-inline .wrapper-copy { - width: 100% !important; - } - .v2 .donation-link, .v2 input { - width: 100% !important; - font-size: 15px !important; - } - .v2 img { - display: none; + +.v2.register-choice-certificate, +.v2.register-choice-donate, +.v2.register-choice-view { + width: 100%; +} + +.v2.register-choice-donate { + border-color: #D7548E !important; +} + +.v2 .wrapper-copy-inline { + max-height: 115px; +} + +.v2.register-choice-v2-audit .wrapper-copy-inline { + display: block !important; +} + +.v2.register-choice-v2-audit .copy-inline { + width: 100% !important; +} + +.v2.register-choice-v2-audit .list-actions { + width: 100% !important; + margin-top: 20px !important; + text-align: center !important; +} + +.v2 .wrapper-copy-inline .wrapper-copy { + width: 100% !important; +} + +.v2 input{ + font-size: 15px !important; +} + +.v2 button { + background-color: rgb(0, 103, 0); + border-color: rgb(0, 103, 0); + border-radius: 2px; + box-shadow: rgb(0, 77, 0) 0px 2px 1px 0px; + cursor: pointer; + font-family: "Open Sans"; + height: auto; + margin-right: 4px; + margin-top: 0px; + padding: 10px 15px; + width: initial; + background-image: none !important; + font-size: 14px !important; + font-weight: 500 !important; + + &:hover, &:focus { + background-color: #009b00 !important; + border-color: #009b00; + box-shadow: #004d00 0px 2px 1px 0px; } } + +.savings-message { + margin-top: 10px; + font-size: 11px; +} +@media screen and (min-width: 375px) { + .savings-message { + font-size: 13px; + margin-left: 16px; + } +} + +.v2 .donation-link, .v2 input, .v2 button { + width: 100%; +} + +.v2 img { + display: none; +} + +.v2 .deco-divider { + display: none; +} + +.v2 .visual-reference { + width: 38%; +} + +@media (min-width: 420px) { + .v2 button { + height: 45px; + font-size: 16px !important; + } +} + @media (min-width: 768px) { .v2.register-choice-certificate, .v2.register-choice-donate { - width: 48% !important; + width: 46.5% !important; display: inline-block; - min-height: 250px; + min-height: 270px; } + .v2.register-choice-v2-audit .wrapper-copy-inline { display: flex !important; } + .v2.register-choice-v2-audit .copy-inline { width: 40% !important; } + .v2.register-choice-v2-audit .list-actions { margin-top: 0 !important; text-align: right !important; } + .v2 .wrapper-copy-inline .wrapper-copy { - width: 58% !important; + width: 100% !important; } - .v2 .donation-link, .v2 input { + + .v2 input { font-size: 15px !important; - width: 55% !important; } + + .v2 .donation-link, .v2.register-choice-certificate button { + margin-top: 20px; + width: initial; + } + + .v2.register-choice-v2-audit input { + width: 100% !important; + } + .v2.register-choice-view { height: 250px; } + .v2 img { display: initial; } + .v2.register-choice { margin: 0 2% 20px 0; } + .v2.register-choice-donate .wrapper-copy-inline .wrapper-copy, .v2.register-choice-certificate .wrapper-copy-inline .wrapper-copy { + width: 60%; + } + + .v2.register-choice-view .wrapper-copy-inline .wrapper-copy { + width: 100%; + } + + .v2.register-choice { + padding: 15px !important; + } + + .v2.register-choice-donate .wrapper-copy-inline .wrapper-copy, .v2.register-choice-certificate .wrapper-copy-inline .wrapper-copy { + width: 60%; + } + + .v2.register-choice { + padding: 20px !important; + } + + .v2.register-choice.register-choice-view { + margin-right: 0; + } + + .v2.register-choice .list-actions:last-child { + float: left; + width: 100%; + margin-top: 0px; + } + + .v2.register-choice .action-select { + width: 100% !important; + } + + .v2 .donation-link:hover, + .v2 .donation-link:focus { + background-color: #D7548E !important; + color: white !important; + text-decoration: none; + } + + .v2 .donation-link:hover { + cursor: pointer; + } + + .v2 .copy li { + margin-bottom: 5px; + } + + .v2.register-choice .copy-inline { + width: 100%; + } + + .v2 .register-choice-view { + border-color: #2991c3 !important; + } + + .v2 .visual-reference { + vertical-align: top; + } + + .v2 .wrapper-copy-inline .wrapper-copy ul { + margin-top: 0px; + padding-left: 30px; + } + + .v2 .img-certificate { + border: 2px solid #009b00 !important; + float: right; + height: 120px; + width: auto; + margin-top: 0 !important; + display: none; + } + + .v2 .img-donate { + margin-top: 0; + float: right; + border: 2px solid #D7548E !important; + display: none; + } + + .v2 .img-view { + border: 2px solid #2991c3 !important; + } + + .v2.register-choice .title { + width: 100%; + margin-bottom: 20px; + } + + .v2.register-choice.register-choice-view .action-select { + border: 1px solid transparent !important; + border-radius: 3px; + } + + .v2.register-choice.register-choice-view .action-select button { + border: 1px solid transparent !important; + } + + .v2.register-choice.register-choice-view .action-select:hover { + border: 1px solid #0075b4 !important; + } + .v2.deco-divider { width: 3% !important; box-sizing: border-box; float: left; display: inline-block; - height: 400px; + height: 250px; margin: 0px 0 40px 0 !important; - border-left: 4px solid #f5f5f5 !important; - border-top: none !important; + border-left: 4px solid #f5f5f5 !important; border-top:none !important; + + .copy { + position: absolute; + top: 110px !important; + left: calc(50% - 40px) !important; + margin-left: 20px; + background: white; + text-align: center; + color: #474747; + width: 10px; + padding: 0 !important; + } } } -@media (min-width: 320px) { - .v2 .visual-reference { - width: 38%; - } -} -@media (min-width: 768px) { - - @media (min-width: 320px) { - .v2.register-choice-donate .wrapper-copy-inline .wrapper-copy, .v2.register-choice-certificate .wrapper-copy-inline .wrapper-copy { - width: 60%; - } - } - @media (min-width: 768px) { - .v2.register-choice-donate .wrapper-copy-inline .wrapper-copy, .v2.register-choice-certificate .wrapper-copy-inline .wrapper-copy { - width: 60%; - } - } - @media (min-width: 320px) { - .v2.register-choice-view .wrapper-copy-inline .wrapper-copy { - width: 100%; - } - } - @media (min-width: 320px) { - .v2.register-choice { - padding: 15px !important; - } - } - @media (min-width: 768px) { - .v2.register-choice { - padding: 20px !important; - } - .v2.register-choice.register-choice-view { - margin-right: 0; - } - } - @media screen and (min-width: 769px) { - .v2.register-choice .list-actions:last-child { - float: left; - width: 100%; - margin-top: 0px; - } - } - @media screen and (min-width: 769px) { - .v2.register-choice .action-select { - width: 100% !important; - } - } - .v2 .donation-link:hover, - .v2 .donation-link:focus { - background-color: #D7548E !important; - color: white !important; - text-decoration: none; - } - .v2 .donation-link:hover { - cursor: pointer; - } - .v2 .copy li { - margin-bottom: 5px; - } - .v2.register-choice .copy-inline { - width: 100%; - } +@media (min-width: 835px) { + .v2.register-choice-certificate, .v2.register-choice-donate { - border-color: #D7548E !important; - } - .v2 .register-choice-view { - border-color: #2991c3 !important; - } - .v2 .visual-reference { - vertical-align: top; - } - .v2 .wrapper-copy-inline .wrapper-copy ul { - margin-top: 0px; - padding-left: 30px; - } - .v2 .img-certificate { - border: 2px solid #009b00 !important; - } - .v2 .img-donate { - border: 2px solid #D7548E !important; - } - .v2 .img-view { - border: 2px solid #2991c3 !important; - } - .v2.register-choice .title { - width: 100%; - margin-bottom: 20px; - } - .v2.register-choice.register-choice-view .action-select { - border: 1px solid transparent !important; - border-radius: 3px; - } - .v2.register-choice.register-choice-view .action-select input { - border: 1px solid transparent !important; - } - .v2.register-choice.register-choice-view .action-select:hover { - border: 1px solid #0075b4 !important; - } - .v2.deco-divider { - display: none !important; + min-height: 250px; } } + +@media (min-width: 1024px) { + .v2 .donation-link { + width: 55%; + } + .v2.deco-divider .copy { + margin-left: 15px; + } +} + +@media (min-width: 1064px) { + .v2.register-choice-certificate, + .v2.register-choice-donate { + min-height: 260px; + } + .v2 .img-certificate, .v2 .img-donate { + display: initial; + } + .v2 .donation-link, .v2.register-choice-certificate button { + margin-top: -22px !important; + } +} \ No newline at end of file diff --git a/themes/edx.org/lms/templates/course_modes/choose.html b/themes/edx.org/lms/templates/course_modes/choose.html index dd57a3f62e..a8ab708436 100644 --- a/themes/edx.org/lms/templates/course_modes/choose.html +++ b/themes/edx.org/lms/templates/course_modes/choose.html @@ -75,7 +75,7 @@ from openedx.core.djangolib.markup import HTML, Text

${title_content}

- + @@ -86,7 +86,7 @@ from openedx.core.djangolib.markup import HTML, Text b_tag_kwargs = {'b_start': HTML(''), 'b_end': HTML('')} %> % if "verified" in modes: - +