diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 3f9abcc671fb..a05b78e8837a 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -17,6 +17,7 @@ lms/djangoapps/instructor_task/ lms/djangoapps/mobile_api/ openedx/core/djangoapps/credentials @openedx/2U-aperture openedx/core/djangoapps/credit @openedx/2U-aperture +openedx/core/djangoapps/enrollments/ @openedx/2U-aperture openedx/core/djangoapps/heartbeat/ openedx/core/djangoapps/oauth_dispatch openedx/core/djangoapps/user_api/ @openedx/2U-aperture @@ -37,8 +38,9 @@ lms/djangoapps/certificates/ @openedx/2U- # Discovery common/djangoapps/course_modes/ common/djangoapps/enrollment/ +lms/djangoapps/branding/ @openedx/2U-aperture lms/djangoapps/commerce/ -lms/djangoapps/experiments/ +lms/djangoapps/experiments/ @openedx/2U-aperture lms/djangoapps/learner_dashboard/ @openedx/2U-aperture lms/djangoapps/learner_home/ @openedx/2U-aperture openedx/features/content_type_gating/ diff --git a/lms/djangoapps/bulk_email/tasks.py b/lms/djangoapps/bulk_email/tasks.py index 2b96af786a97..184dfd0e6869 100644 --- a/lms/djangoapps/bulk_email/tasks.py +++ b/lms/djangoapps/bulk_email/tasks.py @@ -474,6 +474,7 @@ def _send_course_email(entry_id, email_id, to_list, global_email_context, subtas 'course_id': str(course_email.course_id), 'to_list': [user_obj.get('email', '') for user_obj in to_list], 'total_recipients': total_recipients, + 'ace_enabled_for_bulk_email': is_bulk_email_edx_ace_enabled(), } ) # Exclude optouts (if not a retry): diff --git a/lms/djangoapps/discussion/rest_api/discussions_notifications.py b/lms/djangoapps/discussion/rest_api/discussions_notifications.py index 25abcf80d486..f65faf7f2a67 100644 --- a/lms/djangoapps/discussion/rest_api/discussions_notifications.py +++ b/lms/djangoapps/discussion/rest_api/discussions_notifications.py @@ -399,4 +399,18 @@ def clean_thread_html_body(html_body): for match in html_body.find_all(tag): match.unwrap() + # Replace tags that are not allowed in email + tags_to_update = [ + {"source": "button", "target": "span"}, + {"source": "h1", "target": "h4"}, + {"source": "h2", "target": "h4"}, + {"source": "h3", "target": "h4"}, + ] + for tag_dict in tags_to_update: + for source_tag in html_body.find_all(tag_dict['source']): + target_tag = html_body.new_tag(tag_dict['target'], **source_tag.attrs) + if source_tag.string: + target_tag.string = source_tag.string + source_tag.replace_with(target_tag) + return str(html_body) diff --git a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py index f1a71fd1239e..d92e1000feb5 100644 --- a/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py +++ b/lms/djangoapps/discussion/rest_api/tests/test_discussions_notifications.py @@ -168,3 +168,29 @@ def test_only_script_tag(self): result = clean_thread_html_body(html_body) self.assertEqual(result.strip(), expected_output) + + def test_button_tag_replace(self): + """ + Tests that the clean_thread_html_body function replaces the button tag with span tag + """ + # Tests for button replacement tag with text + html_body = '' + expected_output = 'Button' + result = clean_thread_html_body(html_body) + self.assertEqual(result, expected_output) + + # Tests button tag replacement without text + html_body = '' + expected_output = '' + result = clean_thread_html_body(html_body) + self.assertEqual(result, expected_output) + + def test_heading_tag_replace(self): + """ + Tests that the clean_thread_html_body function replaces the h1, h2 and h3 tags with h4 tag + """ + for tag in ['h1', 'h2', 'h3']: + html_body = f'<{tag}>Heading{tag}>' + expected_output = '