diff --git a/cms/envs/common.py b/cms/envs/common.py index a9ea495b0841..b1b8c2cab05e 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -1082,7 +1082,7 @@ } DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' -DEFAULT_HASHING_ALGORITHM = 'sha1' +DEFAULT_HASHING_ALGORITHM = 'sha256' #################### Python sandbox ############################################ diff --git a/lms/djangoapps/certificates/tests/test_webview_views.py b/lms/djangoapps/certificates/tests/test_webview_views.py index 96832e3cb694..5489f28e0f7e 100644 --- a/lms/djangoapps/certificates/tests/test_webview_views.py +++ b/lms/djangoapps/certificates/tests/test_webview_views.py @@ -49,6 +49,7 @@ ) from openedx.core.djangolib.js_utils import js_escaped_string from openedx.core.djangolib.testing.utils import CacheIsolationTestCase +from openedx.core.lib.courses import course_image_url from openedx.core.lib.tests.assertions.events import assert_event_matches from openedx.features.name_affirmation_api.utils import get_name_affirmation_service from xmodule.data import CertificatesDisplayBehaviors # lint-amnesty, pylint: disable=wrong-import-order @@ -351,6 +352,68 @@ def test_linkedin_share_url_site(self): js_escaped_string(self.linkedin_url.format(params=urlencode(params))), ) + @patch.dict("django.conf.settings.SOCIAL_SHARING_SETTINGS", { + "CERTIFICATE_FACEBOOK": True, + "CERTIFICATE_FACEBOOK_TEXT": "test FB text" + }) + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + def test_render_certificate_html_view_with_facebook_meta_tags(self): + """ + Test view html certificate if share to FB is enabled. + If 'facebook_share_enabled=True', tags with property="og:..." + must be enabled to pass parameters to FB. + """ + self._add_course_certificates(count=1, signatory_count=1, is_active=True) + self.course.cert_html_view_enabled = True + self.course.save() + self.update_course(self.course, self.user.id) + test_url = get_certificate_url( + user_id=self.user.id, + course_id=str(self.course.id), + uuid=self.cert.verify_uuid + ) + platform_name = settings.PLATFORM_NAME + share_url = f'http://testserver{test_url}' + full_course_image_url = f'http://testserver{course_image_url(self.course)}' + document_title = f'{self.course.org} {self.course.number} Certificate | {platform_name}' + response = self.client.get(test_url) + + assert response.status_code == 200 + self.assertContains(response, f'') + self.assertContains(response, f'') + self.assertContains(response, '') + self.assertContains(response, f'') + self.assertContains(response, '') + + @patch.dict("django.conf.settings.SOCIAL_SHARING_SETTINGS", { + "CERTIFICATE_FACEBOOK": False, + }) + @override_settings(FEATURES=FEATURES_WITH_CERTS_ENABLED) + def test_render_certificate_html_view_without_facebook_meta_tags(self): + """ + Test view html certificate if share to FB is disabled. + If 'facebook_share_enabled=False', html certificate view + should not contain tags with parameters property="og:..." + """ + self._add_course_certificates(count=1, signatory_count=1, is_active=True) + self.course.cert_html_view_enabled = True + self.course.save() + self.update_course(self.course, self.user.id) + + test_url = get_certificate_url( + user_id=self.user.id, + course_id=str(self.course.id), + uuid=self.cert.verify_uuid + ) + response = self.client.get(test_url) + + assert response.status_code == 200 + self.assertNotContains(response, '') + self.assertNotContains(response, 'This is a test thread body with some text.

"} + ) + serialized = self.serialize(thread_data) + assert serialized['preview_body'] == "This is a test thread body with some text." + @ddt.ddt class CommentSerializerTest(SerializerTestMixin, SharedModuleStoreTestCase): diff --git a/lms/djangoapps/discussion/rest_api/urls.py b/lms/djangoapps/discussion/rest_api/urls.py index b861bd7848d6..f54637dc3724 100644 --- a/lms/djangoapps/discussion/rest_api/urls.py +++ b/lms/djangoapps/discussion/rest_api/urls.py @@ -64,7 +64,11 @@ CourseView.as_view(), name="discussion_course" ), - path('v1/accounts/retire_forum', RetireUserView.as_view(), name="retire_discussion_user"), + re_path( + r"^v1/accounts/retire_forum/?$", + RetireUserView.as_view(), + name="retire_discussion_user" + ), path('v1/accounts/replace_username', ReplaceUsernamesView.as_view(), name="replace_discussion_username"), re_path( fr"^v1/course_topics/{settings.COURSE_ID_PATTERN}", diff --git a/lms/envs/common.py b/lms/envs/common.py index c0182d47355a..c5cbe633f387 100644 --- a/lms/envs/common.py +++ b/lms/envs/common.py @@ -1671,7 +1671,7 @@ def _make_mako_template_dirs(settings): DEFAULT_AUTO_FIELD = 'django.db.models.AutoField' -DEFAULT_HASHING_ALGORITHM = 'sha1' +DEFAULT_HASHING_ALGORITHM = 'sha256' #################### Python sandbox ############################################ diff --git a/lms/static/sass/views/_homepage.scss b/lms/static/sass/views/_homepage.scss index bb7963fba6c3..b8f19cc326cf 100644 --- a/lms/static/sass/views/_homepage.scss +++ b/lms/static/sass/views/_homepage.scss @@ -3,6 +3,13 @@ // TO-DO: combine this with _home.scss as a cleanup story $learn-more-horizontal-position: calc(50% - 100px); // calculate the left position for "LEARN MORE" content +$height-course-info: ($baseline*5.5); +$padding-course-org: ($baseline*0.4); +$font-course-org: ($baseline*0.55); +$margin-course-title: ($baseline*0.75); +$bottom-course: ($baseline*0.45); +$horizontal-padding-course: ($baseline*0.65); +$font-course-date: ($baseline*0.5); .courses-container { @include outer-container; @@ -28,7 +35,6 @@ $learn-more-horizontal-position: calc(50% - 100px); // calculate the left positi @include transition(all $tmg-f3 linear 0s); position: relative; - border-bottom: 3px solid $action-primary-bg; box-shadow: 0 1px 10px 0 $black-t0, inset 0 0 0 1px $white-t3; background: $body-bg; width: 100%; @@ -81,7 +87,7 @@ $learn-more-horizontal-position: calc(50% - 100px); // calculate the left positi } .course-info { - height: $course-info-height; + height: $height-course-info; font-family: $font-family-sans-serif; h2 { @@ -89,30 +95,29 @@ $learn-more-horizontal-position: calc(50% - 100px); // calculate the left positi } .course-organization, - .course-code, - .course-date { + .course-code { @extend %t-icon6; - color: $gray-d2; - } - - .course-organization, - .course-code, - .course-title { - display: block; + color: #e85351; + display: inline; text-transform: none; } .course-organization { @include line-height(11); - padding: ($baseline/2) ($baseline*0.75) ($baseline/10) ($baseline*0.75); + padding: 0 $padding-course-org 0 $padding-course-org; + background-color: #FEEBEE; + margin: $margin-course-title; + font-size: $font-course-org; } .course-code { @include line-height(16); - padding: 0 ($baseline*0.75); + padding: ($baseline*0.1) $margin-course-title; + float: right; + font-size: ($baseline*0.5); } .course-title { @@ -120,16 +125,58 @@ $learn-more-horizontal-position: calc(50% - 100px); // calculate the left positi @extend %t-icon4; - margin: ($baseline*0.25) 0 ($baseline*1.75) 0; - padding: 0 ($baseline*0.75); - height: $course-title-height; - color: $link-color; + margin: $margin-course-title 0 $margin-course-title 0; + text-align: left; + padding: 0 $margin-course-title; + height: ($baseline*1.3); + color: black; + display: block; + text-transform: none; + font-size: 75%; + } + + .date_instructor_container { + border: 1px solid #E9EAF0; + position: absolute; + bottom: 0; + width: 100%; + padding: ($baseline*0.3) 0; } .course-date { @include line-height(14); + padding-left: ($baseline*1.5); + display: inline; + font-size: $font-course-date; + color: #777986; + } + + .course-instructor { + @include line-height(14); + + padding: (3*$baseline/10) $horizontal-padding-course (3*$baseline/10) 0; + font-size: $font-course-date; + color: #777986; + } - padding: ($baseline/10) ($baseline*0.75); + .instructor-wrapper { + float: right; + display: inline; + } + + .date-clock { + color: #EB999A; + padding-left: $horizontal-padding-course; + position: absolute; + bottom: $bottom-course; + } + + .instructor-icon { + color: #EB999A; + position: absolute; + bottom: $bottom-course; + margin-right: ($baseline*2); + right: ($baseline*1.1); } } diff --git a/lms/templates/certificates/_accomplishment-banner.html b/lms/templates/certificates/_accomplishment-banner.html index 9e9b1c2cd3e6..b25932503a67 100644 --- a/lms/templates/certificates/_accomplishment-banner.html +++ b/lms/templates/certificates/_accomplishment-banner.html @@ -51,7 +51,7 @@

${_("Print or share your certificate:")}

% if facebook_share_enabled: ${_("Post on Facebook")} diff --git a/lms/templates/certificates/accomplishment-base.html b/lms/templates/certificates/accomplishment-base.html index 158c7698e80a..92c58dc138ff 100644 --- a/lms/templates/certificates/accomplishment-base.html +++ b/lms/templates/certificates/accomplishment-base.html @@ -15,7 +15,15 @@ - + % if facebook_share_enabled: + ## OG (Open Graph) url, title, type, image and description added below to give social media info to display + ## (https://developers.facebook.com/docs/opengraph/howtos/maximizing-distribution-media-content#tags) + + + + + + %endif ${document_title} <%static:css group='style-certificates'/> diff --git a/lms/templates/course.html b/lms/templates/course.html index db765a62abc2..7f5637bd8b8d 100644 --- a/lms/templates/course.html +++ b/lms/templates/course.html @@ -20,17 +20,26 @@

${course.display_number_with_default} ${course.display_name_with_default}

- <% - if course.start is not None: - course_date_string = course.start.strftime('%Y-%m-%dT%H:%M:%S%z') - else: - course_date_string = '' - %> - % if course.advertised_start is not None: - - % else: - - % endif +
+ + <% + if course.start is not None: + course_date_string = course.start.strftime('%Y-%m-%dT%H:%M:%S%z') + else: + course_date_string = 'end_date_unknown' + %> + % if course.advertised_start is not None: + + % else: + + % endif +
+ + instructor +
+ +
+