Commit 5e176bea by Waheed Ahmed

Preview available email.

ECOM-6084
parent 862dd472
......@@ -87,6 +87,7 @@ class CourseRunSerializer(serializers.ModelSerializer):
return validated_values
@transaction.atomic
def update(self, instance, validated_data):
instance = super(CourseRunSerializer, self).update(instance, validated_data)
preview_url = validated_data.get('preview_url')
......
......@@ -107,9 +107,13 @@ class CourseRunSerializerTests(TestCase):
""" Verify that course 'owner_role' will be changed to course_team after updating
course run with preview url.
"""
self.course_run.preview_url = ''
self.course_run.save()
serializer = self.serializer_class(self.course_run)
serializer.update(self.course_run, serializer.data)
serializer.update(self.course_run, {'preview_url': 'https://example.com/abc/course'})
self.assertEqual(self.course_state.owner_role, PublisherUserRole.CourseTeam)
self.assertEqual(self.course_run.preview_url, serializer.data['preview_url'])
def test_update_lms_course_id(self):
""" Verify that 'changed_by' also updated after updating course_run's lms_course_id."""
......@@ -122,6 +126,36 @@ class CourseRunSerializerTests(TestCase):
self.assertEqual(self.course_run.lms_course_id, serializer.data['lms_course_id'])
self.assertEqual(self.course_run.changed_by, self.user)
def test_update_with_transaction_rollback(self):
"""
Verify that transaction roll backed if an error occurred.
"""
self.course_run.preview_url = ''
self.course_run.save()
serializer = self.serializer_class(self.course_run)
with self.assertRaises(Exception):
serializer.update(self.course_run, {'preview_url': 'invalid_url'})
self.assertFalse(self.course_run.preview_url)
def test_transaction_roll_back_with_error_on_email(self):
"""
Verify that transaction is roll backed if error occurred during email sending.
"""
toggle_switch('enable_publisher_email_notifications', True)
self.course_run.preview_url = ''
self.course_run.save()
serializer = self.serializer_class(self.course_run)
self.assertEqual(self.course_run.course_run_state.owner_role, PublisherUserRole.Publisher)
with self.assertRaises(Exception):
serializer.update(self.course_run, {'preview_url': 'https://example.com/abc/course'})
self.course_run = CourseRun.objects.get(id=self.course_run.id)
self.assertFalse(self.course_run.preview_url)
# Verify that owner role not changed.
self.assertEqual(self.course_run.course_run_state.owner_role, PublisherUserRole.Publisher)
class CourseRevisionSerializerTests(TestCase):
def test_course_revision_serializer(self):
......
......@@ -305,14 +305,31 @@ class UpdateCourseRunViewTests(TestCase):
def test_update_preview_url(self):
"""Verify the user can update course preview url."""
self.course_run.lms_course_id = 'course-v1:testX+TC167+2018T1'
self.course_run.save()
preview_url = 'https://example.com/abc/course'
factories.CourseRunStateFactory.create(course_run=self.course_run, owner_role=PublisherUserRole.Publisher)
factories.CourseUserRoleFactory(
course=self.course_run.course, role=PublisherUserRole.Publisher
)
course_team_role = factories.CourseUserRoleFactory(
course=self.course_run.course, role=PublisherUserRole.CourseTeam
)
response = self._make_request(preview_url)
self.assertEqual(response.status_code, 200)
course_run = CourseRun.objects.get(id=self.course_run.id)
self.assertEqual(course_run.preview_url, preview_url)
course_key = CourseKey.from_string(course_run.lms_course_id)
subject = 'Review requested: Preview for {course_name} {run_number}'.format(
course_name=self.course_run.course.title,
run_number=course_key.run
)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual([course_team_role.user.email], mail.outbox[0].to)
self.assertEqual(str(mail.outbox[0].subject), subject)
def test_update_with_invalid_preview_url(self):
"""Verify the user can't update course preview url if url has invalid format."""
preview_url = 'invalid_url_format'
......@@ -327,6 +344,25 @@ class UpdateCourseRunViewTests(TestCase):
content_type=JSON_CONTENT_TYPE
)
def test_update_preview_url_with_notification_disabled(self):
"""
Verify that no email sent on update course preview url if
notification disabled by user.
"""
preview_url = 'https://example.com/abc/course'
factories.CourseRunStateFactory.create(course_run=self.course_run, owner_role=PublisherUserRole.Publisher)
course_team_role = factories.CourseUserRoleFactory(
course=self.course_run.course, role=PublisherUserRole.CourseTeam
)
factories.UserAttributeFactory(user=course_team_role.user, enable_email_notification=False)
response = self._make_request(preview_url)
self.assertEqual(response.status_code, 200)
course_run = CourseRun.objects.get(id=self.course_run.id)
self.assertEqual(course_run.preview_url, preview_url)
self.assertEqual(len(mail.outbox), 0)
class CourseRevisionDetailViewTests(TestCase):
......
......@@ -58,7 +58,9 @@ def send_email_for_studio_instance_created(course_run, updated_text=_('created')
email_msg.attach_alternative(html_content, 'text/html')
email_msg.send()
except Exception: # pylint: disable=broad-except
logger.exception('Failed to send email notifications for course_run [%s]', course_run.id)
error_message = 'Failed to send email notifications for course_run [{run_id}]'.format(run_id=course_run.id)
logger.exception(error_message)
raise Exception(error_message)
def send_email_for_course_creation(course, course_run):
......@@ -292,16 +294,15 @@ def send_email_preview_accepted(course_run):
html_template = 'publisher/email/course_run/preview_accepted.html'
course = course_run.course
course_key = CourseKey.from_string(course_run.lms_course_id)
subject = _('Publication requested: {course_name} {run_number}').format( # pylint: disable=no-member
course_name=course.title,
run_number=course_key.run)
publisher_user = course_run.course.publisher
publisher_user = course.publisher
try:
if is_email_notification_enabled(publisher_user):
project_coordinator = course_run.course.project_coordinator
course_key = CourseKey.from_string(course_run.lms_course_id)
subject = _('Publication requested: {course_name} {run_number}').format( # pylint: disable=no-member
course_name=course.title,
run_number=course_key.run)
project_coordinator = course.project_coordinator
to_addresses = [publisher_user.email]
if is_email_notification_enabled(project_coordinator):
to_addresses.append(project_coordinator.email)
......@@ -344,26 +345,30 @@ def send_email_preview_page_is_available(course_run):
"""
txt_template = 'publisher/email/course_run/preview_available.txt'
html_template = 'publisher/email/course_run/preview_available.html'
run_name = '{pacing_type}: {start_date}'.format(
pacing_type=course_run.get_pacing_type_display(),
start_date=course_run.start.strftime("%B %d, %Y")
)
subject = _('Preview for {run_name} is available').format(run_name=run_name) # pylint: disable=no-member
course_team_user = course_run.course.course_team_admin
try:
if is_email_notification_enabled(course_team_user):
course_key = CourseKey.from_string(course_run.lms_course_id)
subject = _('Review requested: Preview for {course_name} {run_number}').format( # pylint: disable=no-member
course_name=course_run.course.title,
run_number=course_key.run
)
to_addresses = [course_team_user.email]
from_address = settings.PUBLISHER_FROM_EMAIL
project_coordinator = course_run.course.project_coordinator
page_path = reverse('publisher:publisher_course_run_detail', kwargs={'pk': course_run.id})
context = {
'course_name': run_name,
'sender_role': PublisherUserRole.Publisher,
'recipient_name': course_team_user.get_full_name() or course_team_user.username,
'course_name': course_run.course.title,
'course_run_number': course_key.run,
'preview_link': course_run.preview_url,
'contact_us_email': project_coordinator.email if project_coordinator else '',
'page_url': 'https://{host}{path}'.format(
host=Site.objects.get_current().domain.strip('/'), path=page_path
)
),
'platform_name': settings.PLATFORM_NAME
}
template = get_template(txt_template)
plain_content = template.render(context)
......@@ -371,13 +376,17 @@ def send_email_preview_page_is_available(course_run):
html_content = template.render(context)
email_msg = EmailMultiAlternatives(
subject, plain_content, from_address, to=[from_address], bcc=to_addresses
subject, plain_content, from_address, to=to_addresses
)
email_msg.attach_alternative(html_content, 'text/html')
email_msg.send()
except Exception: # pylint: disable=broad-except
logger.exception('Failed to send email notifications for preview available of course-run %s', course_run.id)
error_message = 'Failed to send email notifications for preview available of course-run {run_id}'.format(
run_id=course_run.id
)
logger.exception(error_message)
raise Exception(error_message)
def send_course_run_published_email(course_run):
......
......@@ -14,6 +14,7 @@ from course_discovery.apps.course_metadata.tests import toggle_switch
from course_discovery.apps.course_metadata.tests.factories import OrganizationFactory
from course_discovery.apps.publisher import emails
from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.models import UserAttributes
from course_discovery.apps.publisher.tests import factories
from course_discovery.apps.publisher.tests.factories import UserAttributeFactory
......@@ -42,17 +43,12 @@ class StudioInstanceCreatedEmailTests(TestCase):
@mock.patch('django.core.mail.message.EmailMessage.send', mock.Mock(side_effect=TypeError))
def test_email_with_error(self):
""" Verify that emails for studio instance created."""
""" Verify that emails failure raise exception."""
with LogCapture(emails.logger.name) as l:
with self.assertRaises(Exception) as ex:
emails.send_email_for_studio_instance_created(self.course_run)
l.check(
(
emails.logger.name,
'ERROR',
'Failed to send email notifications for course_run [{}]'.format(self.course_run.id)
)
)
error_message = 'Failed to send email notifications for course_run [{}]'.format(self.course_run.id)
self.assertEqual(ex.message, error_message)
def test_email_sent_successfully(self):
""" Verify that emails sent successfully for studio instance created."""
......@@ -143,6 +139,15 @@ class CourseCreatedEmailTests(TestCase):
self.assertIn('Please create a Studio instance for this course', body)
self.assertIn('Thanks', body)
def test_email_not_sent_with_notification_disabled(self):
""" Verify that emails not sent if notification disabled by user."""
user_attribute = UserAttributes.objects.get(user=self.user)
user_attribute.enable_email_notification = False
user_attribute.save()
emails.send_email_for_course_creation(self.course_run.course, self.course_run)
self.assertEqual(len(mail.outbox), 0)
class SendForReviewEmailTests(TestCase):
""" Tests for the email functionality for send for review. """
......@@ -429,38 +434,49 @@ class CourseRunPreviewEmailTests(TestCase):
"""
Verify that preview available email functionality works fine.
"""
emails.send_email_preview_page_is_available(self.run_state.course_run)
run_name = '{pacing_type}: {start_date}'.format(
pacing_type=self.run_state.course_run.get_pacing_type_display(),
start_date=self.run_state.course_run.start.strftime("%B %d, %Y")
)
subject = 'Preview for {run_name} is available'.format(
run_name=run_name
course_run = self.run_state.course_run
course_run.lms_course_id = 'course-v1:testX+testX1.0+2017T1'
course_run.save()
emails.send_email_preview_page_is_available(course_run)
course_key = CourseKey.from_string(course_run.lms_course_id)
subject = 'Review requested: Preview for {course_name} {run_number}'.format(
course_name=self.course.title,
run_number=course_key.run
)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual([self.course.course_team_admin.email], mail.outbox[0].bcc)
self.assertEqual([self.course.course_team_admin.email], mail.outbox[0].to)
self.assertEqual(str(mail.outbox[0].subject), subject)
body = mail.outbox[0].body.strip()
page_path = reverse('publisher:publisher_course_run_detail', kwargs={'pk': self.run_state.course_run.id})
page_path = reverse('publisher:publisher_course_run_detail', kwargs={'pk': course_run.id})
page_url = 'https://{host}{path}'.format(host=Site.objects.get_current().domain.strip('/'), path=page_path)
self.assertIn(page_url, body)
self.assertIn('is available for review.', body)
self.assertIn('A preview is now available for the', body)
def test_preview_available_email_with_error(self):
""" Verify that email failure log error message."""
""" Verify that exception raised on email failure."""
with mock.patch('django.core.mail.message.EmailMessage.send', side_effect=TypeError):
with LogCapture(emails.logger.name) as l:
emails.send_email_preview_page_is_available(self.run_state.course_run)
l.check(
(
emails.logger.name,
'ERROR',
'Failed to send email notifications for preview available of course-run {}'.format(
self.run_state.course_run.id
)
)
)
with self.assertRaises(Exception) as ex:
emails.send_email_preview_page_is_available(self.run_state.course_run)
error_message = 'Failed to send email notifications for preview available of course-run {}'.format(
self.run_state.course_run.id
)
self.assertEqual(ex.message, error_message)
def test_preview_available_email_with_notification_disabled(self):
""" Verify that email not sent if notification disabled by user."""
factories.UserAttributeFactory(user=self.course.course_team_admin, enable_email_notification=False)
emails.send_email_preview_page_is_available(self.run_state.course_run)
self.assertEqual(len(mail.outbox), 0)
def test_preview_accepted_email_with_notification_disabled(self):
""" Verify that preview accepted email not sent if notification disabled by user."""
factories.UserAttributeFactory(user=self.course.publisher, enable_email_notification=False)
emails.send_email_preview_accepted(self.run_state.course_run)
self.assertEqual(len(mail.outbox), 0)
class CourseRunPublishedEmailTests(TestCase):
......
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-07 15:35+0500\n"
"POT-Creation-Date: 2017-03-07 18:14+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
#: apps/api/filters.py
#, python-brace-format
......@@ -508,7 +508,7 @@ msgstr ""
#: apps/publisher/emails.py
#, python-brace-format
msgid "Preview for {run_name} is available"
msgid "Review requested: Preview for {course_name} {run_number}"
msgstr ""
#: apps/publisher/emails.py
......@@ -2422,8 +2422,6 @@ msgstr ""
#: templates/publisher/email/comment.txt
#: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt
#: templates/publisher/email/course_run/preview_available.html
#: templates/publisher/email/course_run/preview_available.txt
#: templates/publisher/email/decline_preview.html
#: templates/publisher/email/decline_preview.txt
msgid "The edX team"
......@@ -2457,6 +2455,8 @@ msgstr ""
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course_run/mark_as_reviewed.html
#: templates/publisher/email/course_run/mark_as_reviewed.txt
#: templates/publisher/email/course_run/preview_available.html
#: templates/publisher/email/course_run/preview_available.txt
#: templates/publisher/email/course_run/published.html
#: templates/publisher/email/course_run/published.txt
#: templates/publisher/email/course_run/send_for_review.html
......@@ -2618,20 +2618,20 @@ msgid ""
msgstr ""
#: templates/publisher/email/course_run/preview_available.html
#: templates/publisher/email/course_run/preview_available.txt
msgid "Dear member,"
msgstr ""
#: templates/publisher/email/course_run/preview_available.html
#, python-format
msgid ""
"Preview for %(link_start)s%(page_url)s%(link_middle)s %(course_name)s "
"%(link_end)s is available for review."
"A preview is now available for the %(link_start)s%(page_url)s%(link_middle)s"
" %(course_run_number)s course run %(link_end)s of %(course_name)s. Visit "
"%(link_start)s%(preview_link)s%(link_middle)s link to preview %(link_end)s "
"to approve or decline the preview for this course run."
msgstr ""
#: templates/publisher/email/course_run/preview_available.txt
#, python-format
msgid "Preview for %(course_name)s is available for review. %(page_url)s"
msgid ""
"A preview is now available for the %(course_run_number)s course run of "
"%(course_name)s. Visit %(page_url)s to approve or decline the preview for "
"this course run."
msgstr ""
#: templates/publisher/email/course_run/published.html
......
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-07 15:35+0500\n"
"POT-Creation-Date: 2017-03-07 18:14+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
#: static/js/catalogs-change-form.js
msgid "Preview"
......
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-07 15:35+0500\n"
"POT-Creation-Date: 2017-03-07 18:14+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: apps/api/filters.py
......@@ -629,9 +629,10 @@ msgstr ""
#: apps/publisher/emails.py
#, python-brace-format
msgid "Preview for {run_name} is available"
msgid "Review requested: Preview for {course_name} {run_number}"
msgstr ""
"Prévïéw för {run_name} ïs äväïläßlé Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢#"
"Révïéw réqüéstéd: Prévïéw för {course_name} {run_number} Ⱡ'σяєм ιρѕυм ∂σłσя "
"ѕιт αмєт, ¢σηѕє¢тєтυ#"
#: apps/publisher/emails.py
#, python-brace-format
......@@ -2844,8 +2845,6 @@ msgstr "Vïéw Çöürsé Ⱡ'σяєм ιρѕυм ∂σłσя #"
#: templates/publisher/email/comment.txt
#: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt
#: templates/publisher/email/course_run/preview_available.html
#: templates/publisher/email/course_run/preview_available.txt
#: templates/publisher/email/decline_preview.html
#: templates/publisher/email/decline_preview.txt
msgid "The edX team"
......@@ -2881,6 +2880,8 @@ msgstr "Vïéw çömmént: Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт#"
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course_run/mark_as_reviewed.html
#: templates/publisher/email/course_run/mark_as_reviewed.txt
#: templates/publisher/email/course_run/preview_available.html
#: templates/publisher/email/course_run/preview_available.txt
#: templates/publisher/email/course_run/published.html
#: templates/publisher/email/course_run/published.txt
#: templates/publisher/email/course_run/send_for_review.html
......@@ -3084,26 +3085,34 @@ msgstr ""
" för püßlïçätïön. Ⱡ'σяєм ιρѕυм ∂σ#"
#: templates/publisher/email/course_run/preview_available.html
#: templates/publisher/email/course_run/preview_available.txt
msgid "Dear member,"
msgstr "Déär mémßér, Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/email/course_run/preview_available.html
#, python-format
msgid ""
"Preview for %(link_start)s%(page_url)s%(link_middle)s %(course_name)s "
"%(link_end)s is available for review."
"A preview is now available for the %(link_start)s%(page_url)s%(link_middle)s"
" %(course_run_number)s course run %(link_end)s of %(course_name)s. Visit "
"%(link_start)s%(preview_link)s%(link_middle)s link to preview %(link_end)s "
"to approve or decline the preview for this course run."
msgstr ""
"Prévïéw för %(link_start)s%(page_url)s%(link_middle)s %(course_name)s "
"%(link_end)s ïs äväïläßlé för révïéw. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, "
"¢σηѕє¢тєтυя α#"
"À prévïéw ïs nöw äväïläßlé för thé %(link_start)s%(page_url)s%(link_middle)s"
" %(course_run_number)s çöürsé rün %(link_end)s öf %(course_name)s. Vïsït "
"%(link_start)s%(preview_link)s%(link_middle)s lïnk tö prévïéw %(link_end)s "
"tö äpprövé ör déçlïné thé prévïéw för thïs çöürsé rün. Ⱡ'σяєм ιρѕυм ∂σłσя "
"ѕιт αмєт, ¢σηѕє¢тєтυя α∂ιριѕι¢ιηg єłιт, ѕє∂ ∂σ єιυѕмσ∂ тємρσя ιη¢ι∂ι∂υηт υт "
"łαвσяє єт ∂σłσяє мαgηα αłιqυα. υт єηιм α∂ мιηιм νєηιαм, qυιѕ ησѕтяυ∂ "
"єχєя¢ιтαтιση υłłαм¢σ łαвσяιѕ ηιѕι υт αłιqυιρ єχ єα ¢σммσ∂σ ¢σηѕєqυαт. ∂υιѕ "
"αυтє ιяυяє ∂σłσя ιη яєρяєнєη∂єяιт ιη νσłυρтαтє νєłιт єѕѕє ¢ιłłυм ∂σłσяє єυ "
"ƒυgιαт ηυłłα ραяιαтυя. єχ¢єρтєυя ѕιηт σ¢¢αє¢αт ¢υρι∂αтαт ηση ρяσι∂єηт, ѕυηт "
"ιη ¢υłρα qυι σƒƒι¢ια ∂єѕ#"
#: templates/publisher/email/course_run/preview_available.txt
#, python-format
msgid "Preview for %(course_name)s is available for review. %(page_url)s"
msgid ""
"A preview is now available for the %(course_run_number)s course run of "
"%(course_name)s. Visit %(page_url)s to approve or decline the preview for "
"this course run."
msgstr ""
"Prévïéw för %(course_name)s ïs äväïläßlé för révïéw. %(page_url)s Ⱡ'σяєм "
"ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя #"
"À prévïéw ïs nöw äväïläßlé för thé %(course_run_number)s çöürsé rün öf "
"%(course_name)s. Vïsït %(page_url)s tö äpprövé ör déçlïné thé prévïéw för "
"thïs çöürsé rün. Ⱡ'σяєм ιρѕ#"
#: templates/publisher/email/course_run/published.html
#, python-format
......
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-07 15:35+0500\n"
"POT-Creation-Date: 2017-03-07 18:14+0500\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
"Language: \n"
"MIME-Version: 1.0\n"
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Language: \n"
"Plural-Forms: nplurals=2; plural=(n != 1);\n"
#: static/js/catalogs-change-form.js
......
......@@ -4,17 +4,17 @@
<!-- Message Body -->
<p>
{% blocktrans trimmed %}
Dear member,
Dear {{ recipient_name }},
{% endblocktrans %}
<p>
{% blocktrans with link_start='<a href="' link_middle='">' link_end='</a>' trimmed %}
Preview for {{ link_start }}{{ page_url }}{{ link_middle }} {{ course_name }} {{ link_end }} is available for review.
A preview is now available for the {{ link_start }}{{ page_url }}{{ link_middle }} {{ course_run_number }} course run {{ link_end }} of {{ course_name }}. Visit {{ link_start }}{{ preview_link }}{{ link_middle }} link to preview {{ link_end }} to approve or decline the preview for this course run.
{% endblocktrans %}
</p>
{% comment %}Translators: It's closing of mail.{% endcomment %}
{% trans "Thanks," %}<br>
{% trans "The edX team" %}
{{ platform_name }} {{ sender_role }}
{% blocktrans trimmed %}
......
{% load i18n %}
{% blocktrans trimmed %}
Dear member,
Dear {{ recipient_name }},
{% endblocktrans %}
{% blocktrans trimmed %}
Preview for {{ course_name }} is available for review. {{ page_url }}
A preview is now available for the {{ course_run_number }} course run of {{ course_name }}. Visit {{ page_url }} to approve or decline the preview for this course run.
{% endblocktrans %}
{% trans "Thanks," %}
{% trans "The edX team" %}
{{ platform_name }} {{ sender_role }}
{% blocktrans trimmed %}
Note: This email address is unable to receive replies. For questions or comments, contact {{ contact_us_email }}.
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment