diff --git a/lms/djangoapps/bulk_email/tasks.py b/lms/djangoapps/bulk_email/tasks.py
index 4facbc8655..c2cd02da8a 100644
--- a/lms/djangoapps/bulk_email/tasks.py
+++ b/lms/djangoapps/bulk_email/tasks.py
@@ -128,6 +128,7 @@ def course_email(hash_for_msg, to_list, course_title, course_url, throttle=False
'emails/email_footer.html',
email_context
)
+
plain_footer = render_to_string(
'emails/email_footer.txt',
email_context
@@ -173,7 +174,7 @@ def course_email(hash_for_msg, to_list, course_title, course_url, throttle=False
course_title,
course_url,
current_task.request.retries > 0
- ],
+ ],
exc=exc,
countdown=(2 ** current_task.request.retries) * 15
)
diff --git a/lms/djangoapps/bulk_email/tests/test_email.py b/lms/djangoapps/bulk_email/tests/test_email.py
index 57918936cf..a82756f9f3 100644
--- a/lms/djangoapps/bulk_email/tests/test_email.py
+++ b/lms/djangoapps/bulk_email/tests/test_email.py
@@ -1,3 +1,4 @@
+# -*- coding: utf-8 -*-
"""
Unit tests for sending course email
"""
@@ -82,7 +83,10 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(len(mail.outbox[0].to), 1)
self.assertEquals(mail.outbox[0].to[0], self.instructor.email)
- self.assertEquals(mail.outbox[0].subject, '[' + self.course.display_name + ']' + ' test subject for myself')
+ self.assertEquals(
+ mail.outbox[0].subject,
+ '[' + self.course.display_name + ']' + ' test subject for myself'
+ )
def test_send_to_staff(self):
"""
@@ -101,7 +105,10 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
self.assertContains(response, "Your email was successfully queued for sending.")
self.assertEquals(len(mail.outbox), 1 + len(self.staff))
- self.assertItemsEqual([e.to[0] for e in mail.outbox], [self.instructor.email] + [s.email for s in self.staff])
+ self.assertItemsEqual(
+ [e.to[0] for e in mail.outbox],
+ [self.instructor.email] + [s.email for s in self.staff]
+ )
def test_send_to_all(self):
"""
@@ -121,7 +128,94 @@ class TestEmailSendFromDashboard(ModuleStoreTestCase):
self.assertContains(response, "Your email was successfully queued for sending.")
self.assertEquals(len(mail.outbox), 1 + len(self.staff) + len(self.students))
- self.assertItemsEqual([e.to[0] for e in mail.outbox], [self.instructor.email] + [s.email for s in self.staff] + [s.email for s in self.students])
+ self.assertItemsEqual(
+ [e.to[0] for e in mail.outbox],
+ [self.instructor.email] + [s.email for s in self.staff] + [s.email for s in self.students]
+ )
+
+ def test_unicode_subject_send_to_all(self):
+ """
+ Make sure email (with Unicode characters) send to all goes there.
+ """
+ # Now we know we have pulled up the instructor dash's email view
+ # (in the setUp method), we can test sending an email.
+
+ test_email = {
+ 'action': 'Send email',
+ 'to_option': 'all',
+ 'subject': u'téśt śúbjéćt főŕ áĺĺ',
+ 'message': 'test message for all'
+ }
+ response = self.client.post(self.url, test_email)
+
+ self.assertContains(response, "Your email was successfully queued for sending.")
+
+ self.assertEquals(len(mail.outbox), 1 + len(self.staff) + len(self.students))
+ self.assertItemsEqual(
+ [e.to[0] for e in mail.outbox],
+ [self.instructor.email] + [s.email for s in self.staff] + [s.email for s in self.students]
+ )
+ self.assertEquals(
+ mail.outbox[0].subject,
+ '[' + self.course.display_name + ']' + u' téśt śúbjéćt főŕ áĺĺ'
+ )
+
+ def test_unicode_message_send_to_all(self):
+ """
+ Make sure email (with Unicode characters) send to all goes there.
+ """
+ # Now we know we have pulled up the instructor dash's email view
+ # (in the setUp method), we can test sending an email.
+
+ test_email = {
+ 'action': 'Send email',
+ 'to_option': 'all',
+ 'subject': 'test subject for all',
+ 'message': u'ẗëṡẗ ṁëṡṡäġë ḟöṛ äḷḷ イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ fоѓ аll'
+ }
+ response = self.client.post(self.url, test_email)
+
+ self.assertContains(response, "Your email was successfully queued for sending.")
+
+ self.assertEquals(len(mail.outbox), 1 + len(self.staff) + len(self.students))
+ self.assertItemsEqual(
+ [e.to[0] for e in mail.outbox],
+ [self.instructor.email] + [s.email for s in self.staff] + [s.email for s in self.students]
+ )
+
+ self.assertIn(
+ u'ẗëṡẗ ṁëṡṡäġë ḟöṛ äḷḷ イ乇丂イ ᄊ乇丂丂ムg乇 キo尺 ムレレ тэѕт мэѕѕаБэ fоѓ аll',
+ mail.outbox[0].body.decode('utf-8')
+ )
+
+ def test_unicode_students_send_to_all(self):
+ """
+ Make sure email (with Unicode characters) send to all goes there.
+ """
+ # Now we know we have pulled up the instructor dash's email view
+ # (in the setUp method), we can test sending an email.
+
+ # Create a student with Unicode in their first & last names
+ unicode_user = UserFactory(first_name=u'Ⓡⓞⓑⓞⓣ', last_name=u'ՇﻉรՇ')
+ CourseEnrollmentFactory.create(user=unicode_user, course_id=self.course.id)
+ self.students.append(unicode_user)
+
+ test_email = {
+ 'action': 'Send email',
+ 'to_option': 'all',
+ 'subject': 'test subject for all',
+ 'message': 'test message for all'
+ }
+ response = self.client.post(self.url, test_email)
+
+ self.assertContains(response, "Your email was successfully queued for sending.")
+
+ self.assertEquals(len(mail.outbox), 1 + len(self.staff) + len(self.students))
+
+ self.assertItemsEqual(
+ [e.to[0] for e in mail.outbox],
+ [self.instructor.email] + [s.email for s in self.staff] + [s.email for s in self.students]
+ )
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
diff --git a/lms/djangoapps/bulk_email/tests/tests.py b/lms/djangoapps/bulk_email/tests/tests.py
index 1d1d7bec19..a5dc69f385 100644
--- a/lms/djangoapps/bulk_email/tests/tests.py
+++ b/lms/djangoapps/bulk_email/tests/tests.py
@@ -10,6 +10,7 @@ from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
from student.tests.factories import AdminFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
+from xmodule.modulestore import XML_MODULESTORE_TYPE
from mock import patch
@@ -33,6 +34,7 @@ class TestInstructorDashboardEmailView(ModuleStoreTestCase):
@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
response = self.client.get(self.url)
self.assertTrue(self.email_link in response.content)
@@ -50,5 +52,23 @@ class TestInstructorDashboardEmailView(ModuleStoreTestCase):
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': False})
def test_email_flag_false(self):
+ # Assert that the URL for the email view is not in the response
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
+
+ @patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
+ def test_email_flag_true_xml_store(self):
+ # If the enable email setting is enabled, but this is an XML backed course,
+ # the email view shouldn't be available on the instructor dashboard.
+
+ # The course factory uses a MongoModuleStore backing, so patch the
+ # `get_modulestore_type` method to pretend to be XML-backed.
+ # This is OK; we're simply testing that the `is_mongo_modulestore_type` flag
+ # in `instructor/views/legacy.py` is doing the correct thing.
+
+ with patch('xmodule.modulestore.mongo.base.MongoModuleStore.get_modulestore_type') as mock_modulestore:
+ mock_modulestore.return_value = XML_MODULESTORE_TYPE
+
+ # Assert that the URL for the email view is not in the response
+ response = self.client.get(self.url)
+ self.assertFalse(self.email_link in response.content)
diff --git a/lms/djangoapps/instructor/views/legacy.py b/lms/djangoapps/instructor/views/legacy.py
index 5f3e9a6e01..514c64df98 100644
--- a/lms/djangoapps/instructor/views/legacy.py
+++ b/lms/djangoapps/instructor/views/legacy.py
@@ -25,6 +25,7 @@ from django.utils import timezone
from xmodule_modifiers import wrap_xmodule
import xmodule.graders as xmgraders
+from xmodule.modulestore import MONGO_MODULESTORE_TYPE
from xmodule.modulestore.django import modulestore
from xmodule.modulestore.exceptions import ItemNotFoundError
from xmodule.html_module import HtmlDescriptor
@@ -794,6 +795,12 @@ def instructor_dashboard(request, course_id):
else:
editor = None
+ # Flag for what backing store this course is (Mongo vs. XML)
+ if modulestore().get_modulestore_type(course_id) == MONGO_MODULESTORE_TYPE:
+ is_mongo_modulestore_type = True
+ else:
+ is_mongo_modulestore_type = False
+
# display course stats only if there is no other table to display:
course_stats = None
if not datatable:
@@ -809,11 +816,11 @@ def instructor_dashboard(request, course_id):
'datatable': datatable,
'course_stats': course_stats,
'msg': msg,
- 'email_msg': email_msg,
'modeflag': {idash_mode: 'selectedmode'},
- 'to_option': to_option, # email
- 'subject': subject, # email
- 'editor': editor, # email
+ 'to_option': to_option, # email
+ 'subject': subject, # email
+ 'editor': editor, # email
+ 'email_msg': email_msg, # email
'problems': problems, # psychometrics
'plots': plots, # psychometrics
'course_errors': modulestore().get_item_errors(course.location),
@@ -822,6 +829,7 @@ def instructor_dashboard(request, course_id):
'cohorts_ajax_url': reverse('cohorts', kwargs={'course_id': course_id}),
'analytics_results': analytics_results,
+ 'is_mongo_modulestore_type': is_mongo_modulestore_type,
}
if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_BETA_DASHBOARD'):
diff --git a/lms/templates/courseware/instructor_dashboard.html b/lms/templates/courseware/instructor_dashboard.html
index ec5abe6906..82067d4261 100644
--- a/lms/templates/courseware/instructor_dashboard.html
+++ b/lms/templates/courseware/instructor_dashboard.html
@@ -124,7 +124,7 @@ function goto( mode)
${_("Enrollment")} |
${_("DataDump")} |
${_("Manage Groups")}
- %if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_EMAIL'):
+ %if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_EMAIL') and is_mongo_modulestore_type:
| Email
%endif
%if settings.MITX_FEATURES.get('ENABLE_INSTRUCTOR_ANALYTICS'):