diff --git a/common/lib/html_to_text.py b/common/lib/html_to_text.py
new file mode 100644
index 0000000000..b4b2295509
--- /dev/null
+++ b/common/lib/html_to_text.py
@@ -0,0 +1,23 @@
+"""Provides a function to convert html to plaintext."""
+from subprocess import Popen, PIPE
+
+def html_to_text(html_message):
+ """
+ Converts an html message to plaintext.
+ Currently uses lynx in a subprocess; should be refactored to
+ use something more pythonic.
+ """
+ process = Popen(
+ ['lynx', '-stdin', '-display_charset=UTF-8', '-assume_charset=UTF-8', '-dump'],
+ stdin=PIPE,
+ stdout=PIPE
+ )
+ # use lynx to get plaintext
+ (plaintext, err_from_stderr) = process.communicate(
+ input=html_message.encode('utf-8')
+ )
+
+ if err_from_stderr:
+ log.info(err_from_stderr)
+
+ return plaintext
diff --git a/lms/djangoapps/bulk_email/models.py b/lms/djangoapps/bulk_email/models.py
index 1eed87791e..ef06e83d13 100644
--- a/lms/djangoapps/bulk_email/models.py
+++ b/lms/djangoapps/bulk_email/models.py
@@ -14,6 +14,7 @@ class Email(models.Model):
hash = models.CharField(max_length=128, db_index=True)
subject = models.CharField(max_length=128, blank=True)
html_message = models.TextField(null=True, blank=True)
+ text_message = models.TextField(null=True, blank=True)
created = models.DateTimeField(auto_now_add=True)
modified = models.DateTimeField(auto_now=True)
diff --git a/lms/djangoapps/bulk_email/tasks.py b/lms/djangoapps/bulk_email/tasks.py
index c2cd02da8a..b34c3983b6 100644
--- a/lms/djangoapps/bulk_email/tasks.py
+++ b/lms/djangoapps/bulk_email/tasks.py
@@ -8,7 +8,6 @@ import re
import time
from smtplib import SMTPServerDisconnected, SMTPDataError, SMTPConnectError
-from subprocess import Popen, PIPE
from django.conf import settings
from django.contrib.auth.models import User, Group
@@ -98,15 +97,9 @@ def course_email(hash_for_msg, to_list, course_title, course_url, throttle=False
subject = "[" + course_title + "] " + msg.subject
- process = Popen(['lynx', '-stdin', '-display_charset=UTF-8', '-assume_charset=UTF-8', '-dump'], stdin=PIPE, stdout=PIPE)
- (plaintext, err_from_stderr) = process.communicate(input=msg.html_message.encode('utf-8')) # use lynx to get plaintext
-
course_title_no_quotes = re.sub(r'"', '', course_title)
from_addr = '"{0}" Course Staff <{1}>'.format(course_title_no_quotes, settings.DEFAULT_BULK_FROM_EMAIL)
- if err_from_stderr:
- log.info(err_from_stderr)
-
try:
connection = get_connection()
connection.open()
@@ -136,14 +129,15 @@ def course_email(hash_for_msg, to_list, course_title, course_url, throttle=False
email_msg = EmailMultiAlternatives(
subject,
- plaintext + plain_footer.encode('utf-8'),
+ msg.text_message + plain_footer.encode('utf-8'),
from_addr,
[email],
connection=connection
)
email_msg.attach_alternative(msg.html_message + html_footer.encode('utf-8'), 'text/html')
- if throttle or current_task.request.retries > 0: # throttle if we tried a few times and got the rate limiter
+ # Throttle if we tried a few times and got the rate limiter
+ if throttle or current_task.request.retries > 0:
time.sleep(0.2)
try:
diff --git a/lms/djangoapps/bulk_email/tests/test_course_optout.py b/lms/djangoapps/bulk_email/tests/test_course_optout.py
index 297e5806f7..499ccb0b95 100644
--- a/lms/djangoapps/bulk_email/tests/test_course_optout.py
+++ b/lms/djangoapps/bulk_email/tests/test_course_optout.py
@@ -31,6 +31,12 @@ class TestOptoutCourseEmails(ModuleStoreTestCase):
self.client.login(username=self.student.username, password="test")
+ def tearDown(self):
+ """
+ Undo all patches.
+ """
+ patch.stopall()
+
def navigate_to_email_view(self):
"""Navigate to the instructor dash's email view"""
# Pull up email view on instructor dashboard
@@ -54,6 +60,8 @@ class TestOptoutCourseEmails(ModuleStoreTestCase):
Make sure student does not receive course email after opting out.
"""
url = reverse('change_email_settings')
+ # This is a checkbox, so on the post of opting out (that is, an Un-check of the box),
+ # the Post that is sent will not contain 'receive_emails'
response = self.client.post(url, {'course_id': self.course.id})
self.assertEquals(json.loads(response.content), {'success': True})
diff --git a/lms/djangoapps/bulk_email/tests/test_email.py b/lms/djangoapps/bulk_email/tests/test_email.py
index a82756f9f3..6f5c09add9 100644
--- a/lms/djangoapps/bulk_email/tests/test_email.py
+++ b/lms/djangoapps/bulk_email/tests/test_email.py
@@ -64,6 +64,12 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
selected_email_link = 'Email'
self.assertTrue(selected_email_link in response.content)
+ def tearDown(self):
+ """
+ Undo all patches.
+ """
+ patch.stopall()
+
def test_send_to_self(self):
"""
Make sure email send to myself goes to myself.
@@ -185,7 +191,7 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
self.assertIn(
u'ẗëṡẗ ṁëṡṡäġë ḟöṛ äḷḷ イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ fоѓ аll',
- mail.outbox[0].body.decode('utf-8')
+ mail.outbox[0].body
)
def test_unicode_students_send_to_all(self):
diff --git a/lms/djangoapps/bulk_email/tests/test_err_handling.py b/lms/djangoapps/bulk_email/tests/test_err_handling.py
index 9a5555b749..2715804079 100644
--- a/lms/djangoapps/bulk_email/tests/test_err_handling.py
+++ b/lms/djangoapps/bulk_email/tests/test_err_handling.py
@@ -40,8 +40,10 @@ class TestEmailErrors(ModuleStoreTestCase):
self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id})
+
def tearDown(self):
self.smtp_server_thread.stop()
+ patch.stopall()
@patch('bulk_email.tasks.course_email.retry')
def test_data_err_retry(self, retry):
diff --git a/lms/djangoapps/bulk_email/tests/tests.py b/lms/djangoapps/bulk_email/tests/tests.py
index a5dc69f385..66151806f0 100644
--- a/lms/djangoapps/bulk_email/tests/tests.py
+++ b/lms/djangoapps/bulk_email/tests/tests.py
@@ -32,6 +32,12 @@ class TestInstructorDashboardEmailView(ModuleStoreTestCase):
# URL for email view
self.email_link = 'Email'
+ def tearDown(self):
+ """
+ Undo all patches.
+ """
+ patch.stopall()
+
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true(self):
# Assert that the URL for the email view is in the response
diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py
index 514c64df98..4143639781 100644
--- a/lms/djangoapps/instructor/views/legacy.py
+++ b/lms/djangoapps/instructor/views/legacy.py
@@ -55,6 +55,7 @@ from mitxmako.shortcuts import render_to_string
from bulk_email.models import CourseEmail
+from html_to_text import html_to_text
import datetime
from hashlib import md5
from bulk_email import tasks
@@ -706,12 +707,14 @@ def instructor_dashboard(request, course_id):
to_option = request.POST.get("to_option")
subject = request.POST.get("subject")
html_message = request.POST.get("message")
+ text_message = html_to_text(html_message)
email = CourseEmail(course_id=course_id,
sender=request.user,
to=to_option,
subject=subject,
html_message=html_message,
+ text_message=text_message,
hash=md5((html_message + subject + datetime.datetime.isoformat(datetime.datetime.now())).encode('utf-8')).hexdigest())
email.save()