Commit 06fa13f4 by Douglas Hall Committed by Douglas Hall

Allow for customization of course email template at the organization level

parent aeb76a5b
...@@ -30,7 +30,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey ...@@ -30,7 +30,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from opaque_keys.edx.locator import UsageKey from opaque_keys.edx.locator import UsageKey
from xmodule.modulestore import ModuleStoreEnum from xmodule.modulestore import ModuleStoreEnum
from bulk_email.models import BulkEmailFlag from bulk_email.models import BulkEmailFlag, CourseEmail, CourseEmailTemplate
from course_modes.models import CourseMode from course_modes.models import CourseMode
from courseware.models import StudentModule from courseware.models import StudentModule
from courseware.tests.factories import ( from courseware.tests.factories import (
...@@ -70,6 +70,7 @@ from certificates.models import CertificateStatuses ...@@ -70,6 +70,7 @@ from certificates.models import CertificateStatuses
from openedx.core.djangoapps.course_groups.cohorts import set_course_cohort_settings from openedx.core.djangoapps.course_groups.cohorts import set_course_cohort_settings
from openedx.core.lib.xblock_utils import grade_histogram from openedx.core.lib.xblock_utils import grade_histogram
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.site_configuration.tests.mixins import SiteMixin
from .test_tools import msk_from_problem_urlname from .test_tools import msk_from_problem_urlname
...@@ -3558,7 +3559,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE ...@@ -3558,7 +3559,7 @@ class TestEntranceExamInstructorAPIRegradeTask(SharedModuleStoreTestCase, LoginE
@attr(shard=1) @attr(shard=1)
@patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message', autospec=True)) @patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message', autospec=True))
class TestInstructorSendEmail(SharedModuleStoreTestCase, LoginEnrollmentTestCase): class TestInstructorSendEmail(SiteMixin, SharedModuleStoreTestCase, LoginEnrollmentTestCase):
""" """
Checks that only instructors have access to email endpoints, and that Checks that only instructors have access to email endpoints, and that
these endpoints are only accessible with courses that actually exist, these endpoints are only accessible with courses that actually exist,
...@@ -3645,6 +3646,43 @@ class TestInstructorSendEmail(SharedModuleStoreTestCase, LoginEnrollmentTestCase ...@@ -3645,6 +3646,43 @@ class TestInstructorSendEmail(SharedModuleStoreTestCase, LoginEnrollmentTestCase
}) })
self.assertEqual(response.status_code, 400) self.assertEqual(response.status_code, 400)
def test_send_email_with_site_template_and_from_addr(self):
site_email = self.site_configuration.values.get('course_email_from_addr')
site_template = self.site_configuration.values.get('course_email_template_name')
CourseEmailTemplate.objects.create(name=site_template)
url = reverse('send_email', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.post(url, self.full_test_message)
self.assertEqual(response.status_code, 200)
self.assertEqual(1, CourseEmail.objects.filter(
course_id=self.course.id,
sender=self.instructor,
subject=self.full_test_message['subject'],
html_message=self.full_test_message['message'],
template_name=site_template,
from_addr=site_email
).count())
def test_send_email_with_org_template_and_from_addr(self):
org_email = 'fake_org@example.com'
org_template = 'fake_org_email_template'
CourseEmailTemplate.objects.create(name=org_template)
self.site_configuration.values.update({
'course_email_from_addr': {self.course.id.org: org_email},
'course_email_template_name': {self.course.id.org: org_template}
})
self.site_configuration.save()
url = reverse('send_email', kwargs={'course_id': self.course.id.to_deprecated_string()})
response = self.client.post(url, self.full_test_message)
self.assertEqual(response.status_code, 200)
self.assertEqual(1, CourseEmail.objects.filter(
course_id=self.course.id,
sender=self.instructor,
subject=self.full_test_message['subject'],
html_message=self.full_test_message['message'],
template_name=org_template,
from_addr=org_email
).count())
class MockCompletionInfo(object): class MockCompletionInfo(object):
"""Mock for get_task_completion_info""" """Mock for get_task_completion_info"""
......
...@@ -110,6 +110,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey ...@@ -110,6 +110,7 @@ from opaque_keys.edx.locations import SlashSeparatedCourseKey
from opaque_keys import InvalidKeyError from opaque_keys import InvalidKeyError
from openedx.core.djangoapps.course_groups.cohorts import is_course_cohorted from openedx.core.djangoapps.course_groups.cohorts import is_course_cohorted
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
from openedx.core.djangoapps.content.course_overviews.models import CourseOverview
log = logging.getLogger(__name__) log = logging.getLogger(__name__)
...@@ -2516,7 +2517,7 @@ def send_email(request, course_id): ...@@ -2516,7 +2517,7 @@ def send_email(request, course_id):
- 'subject' specifies email's subject - 'subject' specifies email's subject
- 'message' specifies email's content - 'message' specifies email's content
""" """
course_id = SlashSeparatedCourseKey.from_deprecated_string(course_id) course_id = CourseKey.from_string(course_id)
if not BulkEmailFlag.feature_enabled(course_id): if not BulkEmailFlag.feature_enabled(course_id):
return HttpResponseForbidden("Email is not enabled for this course.") return HttpResponseForbidden("Email is not enabled for this course.")
...@@ -2530,8 +2531,22 @@ def send_email(request, course_id): ...@@ -2530,8 +2531,22 @@ def send_email(request, course_id):
# #
# If these are None (there is no site configuration enabled for the current site) than # If these are None (there is no site configuration enabled for the current site) than
# the system will use normal system defaults # the system will use normal system defaults
template_name = configuration_helpers.get_value('course_email_template_name') course_overview = CourseOverview.get_from_id(course_id)
from_addr = configuration_helpers.get_value('course_email_from_addr') from_addr = configuration_helpers.get_value('course_email_from_addr')
if isinstance(from_addr, dict):
# If course_email_from_addr is a dict, we are customizing
# the email template for each organization that has courses
# on the site. The dict maps from addresses by org allowing
# us to find the correct from address to use here.
from_addr = from_addr.get(course_overview.display_org_with_default)
template_name = configuration_helpers.get_value('course_email_template_name')
if isinstance(template_name, dict):
# If course_email_template_name is a dict, we are customizing
# the email template for each organization that has courses
# on the site. The dict maps template names by org allowing
# us to find the correct template to use here.
template_name = template_name.get(course_overview.display_org_with_default)
# Create the CourseEmail object. This is saved immediately, so that # Create the CourseEmail object. This is saved immediately, so that
# any transaction that has been pending up to this point will also be # any transaction that has been pending up to this point will also be
......
...@@ -16,7 +16,9 @@ class SiteMixin(object): ...@@ -16,7 +16,9 @@ class SiteMixin(object):
site=self.site, site=self.site,
values={ values={
"SITE_NAME": self.site.domain, "SITE_NAME": self.site.domain,
"course_org_filter": "fakeX", "course_email_from_addr": "fake@example.com",
"course_email_template_name": "fake_email_template",
"course_org_filter": "fakeX"
} }
) )
......
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