Commit 14d6bbbd by Waheed Ahmed

edX staff change role assignment email.

ECOM-6468
parent 1390f980
......@@ -13,8 +13,8 @@ from rest_framework import serializers
from course_discovery.apps.core.models import User
from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.emails import (
send_email_for_studio_instance_created, send_email_preview_accepted, send_email_preview_page_is_available
)
send_change_role_assignment_email, send_email_for_studio_instance_created, send_email_preview_accepted,
send_email_preview_page_is_available)
from course_discovery.apps.publisher.models import CourseRun, CourseRunState, CourseState, CourseUserRole
......@@ -35,6 +35,15 @@ class CourseUserRoleSerializer(serializers.ModelSerializer):
return validated_values
@transaction.atomic
def update(self, instance, validated_data):
former_user = instance.user
instance = super(CourseUserRoleSerializer, self).update(instance, validated_data)
if not instance.role == PublisherUserRole.CourseTeam:
send_change_role_assignment_email(instance, former_user)
return instance
class GroupUserSerializer(serializers.ModelSerializer):
"""Serializer for the `User` model used in OrganizationGroupUserView. """
......
"""Tests API Serializers."""
import mock
from django.core import mail
from django.test import RequestFactory, TestCase
from opaque_keys.edx.keys import CourseKey
......@@ -25,7 +26,7 @@ class CourseUserRoleSerializerTests(TestCase):
def setUp(self):
super(CourseUserRoleSerializerTests, self).setUp()
self.request = RequestFactory()
self.course_user_role = CourseUserRoleFactory()
self.course_user_role = CourseUserRoleFactory(role=PublisherUserRole.MarketingReviewer)
self.request.user = self.course_user_role.user
def get_expected_data(self):
......@@ -41,6 +42,27 @@ class CourseUserRoleSerializerTests(TestCase):
validated_data = serializer.validate(serializer.data)
self.assertEqual(validated_data, self.get_expected_data())
def test_update(self):
"""
Test that user role assignment changed on update.
"""
new_user = UserFactory()
serializer = self.serializer_class(self.course_user_role, context={'request': self.request})
course_role = serializer.update(self.course_user_role, {'user': new_user})
self.assertEqual(course_role.user, new_user)
self.assertEqual(len(mail.outbox), 1)
def test_update_with_error(self):
"""
Test that whole transaction roll backed if error in email sending.
"""
new_user = UserFactory()
serializer = self.serializer_class(self.course_user_role, context={'request': self.request})
with mock.patch('django.core.mail.message.EmailMessage.send', side_effect=TypeError):
with self.assertRaises(Exception):
course_role = serializer.update(self.course_user_role, {'user': new_user})
self.assertNotEqual(course_role.user, new_user)
class GroupUserSerializerTests(TestCase):
......
......@@ -130,6 +130,7 @@ class CourseRoleAssignmentViewTests(TestCase):
}
self.assertDictEqual(response.data, expected)
self.assertEqual(self.internal_user, self.course.course_user_roles.get(role=user_course_role.role).user)
self.assertEqual(len(mail.outbox), 1)
class OrganizationGroupUserViewTests(TestCase):
......
......@@ -449,3 +449,56 @@ def send_course_run_published_email(course_run):
)
logger.exception(error_message)
raise Exception(error_message)
def send_change_role_assignment_email(course_role, former_user):
""" Send email for role assignment changed.
Arguments:
course_role (Object): CourseUserRole object
former_user (Object): User object
"""
txt_template = 'publisher/email/role_assignment_changed.txt'
html_template = 'publisher/email/role_assignment_changed.html'
try:
subject = _('Role assignment changed for role: {role_name} against {course_title}').format( # pylint: disable=no-member
role_name=course_role.get_role_display(),
course_title=course_role.course.title
)
to_addresses = course_role.course.get_course_users_emails()
to_addresses.append(former_user.email)
if course_role.course.course_team_admin:
to_addresses.remove(course_role.course.course_team_admin.email)
from_address = settings.PUBLISHER_FROM_EMAIL
project_coordinator = course_role.course.project_coordinator
page_path = reverse('publisher:publisher_course_detail', kwargs={'pk': course_role.course.id})
context = {
'course_title': course_role.course.title,
'role_name': course_role.get_role_display(),
'former_user_name': former_user.get_full_name() or former_user.username,
'current_user_name': course_role.user.get_full_name() or course_role.user.username,
'contact_us_email': project_coordinator.email if project_coordinator else '',
'course_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)
template = get_template(html_template)
html_content = template.render(context)
email_msg = EmailMultiAlternatives(
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
error_message = 'Failed to send email notifications for change role assignment of role: [{role_id}]'.format(
role_id=course_role.id
)
logger.exception(error_message)
raise Exception(error_message)
......@@ -544,3 +544,56 @@ class CourseRunPublishedEmailTests(TestCase):
with self.assertRaises(Exception) as ex:
emails.send_course_run_published_email(self.course_run)
self.assertEqual(str(ex.exception), message)
class CourseChangeRoleAssignmentEmailTests(TestCase):
"""
Tests email functionality for course role assignment changed.
"""
def setUp(self):
super(CourseChangeRoleAssignmentEmailTests, self).setUp()
self.user = UserFactory()
self.marketing_role = factories.CourseUserRoleFactory(role=PublisherUserRole.MarketingReviewer, user=self.user)
self.course = self.marketing_role.course
factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.Publisher)
factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.ProjectCoordinator)
factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.CourseTeam)
toggle_switch('enable_publisher_email_notifications', True)
def test_change_role_assignment_email(self):
"""
Verify that course role assignment chnage email functionality works fine.
"""
emails.send_change_role_assignment_email(self.marketing_role, self.user)
expected_subject = 'Role assignment changed for role: {role_name} against {course_title}'.format(
role_name=self.marketing_role.get_role_display(),
course_title=self.course.title
)
expected_emails = set(self.course.get_course_users_emails())
expected_emails.remove(self.course.course_team_admin.email)
self.assertEqual(len(mail.outbox), 1)
self.assertEqual(expected_emails, set(mail.outbox[0].to))
self.assertEqual(str(mail.outbox[0].subject), expected_subject)
body = mail.outbox[0].body.strip()
page_path = reverse('publisher:publisher_course_detail', kwargs={'pk': self.course.id})
page_url = 'https://{host}{path}'.format(host=Site.objects.get_current().domain.strip('/'), path=page_path)
self.assertIn(page_url, body)
self.assertIn('has been changed.', body)
def test_change_role_assignment_email_with_error(self):
"""
Verify that email failure raises exception.
"""
message = 'Failed to send email notifications for change role assignment of role: [{role_id}]'.format(
role_id=self.marketing_role.id
)
with mock.patch('django.core.mail.message.EmailMessage.send', side_effect=TypeError):
with self.assertRaises(Exception) as ex:
emails.send_change_role_assignment_email(self.marketing_role, self.user)
self.assertEqual(str(ex.exception), message)
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-09 14:55+0500\n"
"POT-Creation-Date: 2017-03-09 15:51+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
......@@ -521,6 +521,11 @@ msgstr ""
msgid "Publication complete: {course_name} {run_number}"
msgstr ""
#: apps/publisher/emails.py
#, python-brace-format
msgid "Role assignment changed for role: {role_name} against {course_title}"
msgstr ""
#: apps/publisher/forms.py
msgid "Remove Image"
msgstr ""
......@@ -991,7 +996,7 @@ msgid "day in ownership"
msgstr ""
#: templates/publisher/_approval_widget.html
msgid "change owner"
msgid "change"
msgstr ""
#: templates/publisher/_approval_widget.html
......@@ -2688,6 +2693,32 @@ msgid ""
"been declined."
msgstr ""
#: templates/publisher/email/role_assignment_changed.html
#, python-format
msgid ""
"The %(role_name)s for the "
"%(link_start)s%(course_url)s%(link_middle)s%(course_title)s%(link_end)s has "
"been changed."
msgstr ""
#: templates/publisher/email/role_assignment_changed.html
#: templates/publisher/email/role_assignment_changed.txt
#, python-format
msgid "Former %(role_name)s: %(former_user_name)s"
msgstr ""
#: templates/publisher/email/role_assignment_changed.html
#: templates/publisher/email/role_assignment_changed.txt
#, python-format
msgid "Current %(role_name)s: %(current_user_name)s"
msgstr ""
#: templates/publisher/email/role_assignment_changed.txt
#, python-format
msgid ""
"The %(role_name)s for the %(course_title)s: %(course_url)s has been changed."
msgstr ""
#. Translators: course_team_name is course team member name.
#: templates/publisher/email/studio_instance_created.html
#: templates/publisher/email/studio_instance_created.txt
......@@ -2763,33 +2794,6 @@ msgstr ""
msgid "Please create a Studio instance for the following course."
msgstr ""
#: templates/publisher/email/user_role_changed.html
#, python-format
msgid ""
"The %(role_name)s for the "
"%(link_start)s%(course_url)s%(link_middle)s%(course_title)s%(link_end)s "
"course has changed."
msgstr ""
#: templates/publisher/email/user_role_changed.html
#: templates/publisher/email/user_role_changed.txt
#, python-format
msgid "Former %(role_name)s: %(former_user_name)s"
msgstr ""
#: templates/publisher/email/user_role_changed.html
#: templates/publisher/email/user_role_changed.txt
#, python-format
msgid "Current %(role_name)s: %(current_user_name)s"
msgstr ""
#: templates/publisher/email/user_role_changed.txt
#, python-format
msgid ""
"The %(role_name)s for the %(course_title)s: %(course_url)s course has "
"changed."
msgstr ""
#: templates/publisher/seat_form.html
msgid "Seat Form"
msgstr ""
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-09 14:55+0500\n"
"POT-Creation-Date: 2017-03-09 15:51+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-09 14:55+0500\n"
"POT-Creation-Date: 2017-03-09 15:51+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
......@@ -647,6 +647,13 @@ msgstr ""
"Püßlïçätïön çömplété: {course_name} {run_number} Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт "
"αмєт, ¢σηѕє¢#"
#: apps/publisher/emails.py
#, python-brace-format
msgid "Role assignment changed for role: {role_name} against {course_title}"
msgstr ""
"Rölé ässïgnmént çhängéd för rölé: {role_name} ägäïnst {course_title} Ⱡ'σяєм "
"ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: apps/publisher/forms.py
msgid "Remove Image"
msgstr "Rémövé Ìmägé Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
......@@ -1159,8 +1166,8 @@ msgid "day in ownership"
msgstr "däý ïn öwnérshïp Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: templates/publisher/_approval_widget.html
msgid "change owner"
msgstr "çhängé öwnér Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
msgid "change"
msgstr "çhängé Ⱡ'σяєм ιρѕυ#"
#: templates/publisher/_approval_widget.html
msgid "CHANGE"
......@@ -3200,6 +3207,38 @@ msgstr ""
"Prévïéw lïnk %(preview_url)s för thé %(course_title)s: %(course_url)s häs "
"ßéén déçlïnéd. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: templates/publisher/email/role_assignment_changed.html
#, python-format
msgid ""
"The %(role_name)s for the "
"%(link_start)s%(course_url)s%(link_middle)s%(course_title)s%(link_end)s has "
"been changed."
msgstr ""
"Thé %(role_name)s för thé "
"%(link_start)s%(course_url)s%(link_middle)s%(course_title)s%(link_end)s häs "
"ßéén çhängéd. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: templates/publisher/email/role_assignment_changed.html
#: templates/publisher/email/role_assignment_changed.txt
#, python-format
msgid "Former %(role_name)s: %(former_user_name)s"
msgstr "Förmér %(role_name)s: %(former_user_name)s Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт α#"
#: templates/publisher/email/role_assignment_changed.html
#: templates/publisher/email/role_assignment_changed.txt
#, python-format
msgid "Current %(role_name)s: %(current_user_name)s"
msgstr ""
"Çürrént %(role_name)s: %(current_user_name)s Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: templates/publisher/email/role_assignment_changed.txt
#, python-format
msgid ""
"The %(role_name)s for the %(course_title)s: %(course_url)s has been changed."
msgstr ""
"Thé %(role_name)s för thé %(course_title)s: %(course_url)s häs ßéén çhängéd."
" Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя #"
#. Translators: course_team_name is course team member name.
#: templates/publisher/email/studio_instance_created.html
#: templates/publisher/email/studio_instance_created.txt
......@@ -3308,39 +3347,6 @@ msgstr ""
"Pléäsé çréäté ä Stüdïö ïnstänçé för thé föllöwïng çöürsé. Ⱡ'σяєм ιρѕυм ∂σłσя"
" ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: templates/publisher/email/user_role_changed.html
#, python-format
msgid ""
"The %(role_name)s for the "
"%(link_start)s%(course_url)s%(link_middle)s%(course_title)s%(link_end)s "
"course has changed."
msgstr ""
"Thé %(role_name)s för thé "
"%(link_start)s%(course_url)s%(link_middle)s%(course_title)s%(link_end)s "
"çöürsé häs çhängéd. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя α#"
#: templates/publisher/email/user_role_changed.html
#: templates/publisher/email/user_role_changed.txt
#, python-format
msgid "Former %(role_name)s: %(former_user_name)s"
msgstr "Förmér %(role_name)s: %(former_user_name)s Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт α#"
#: templates/publisher/email/user_role_changed.html
#: templates/publisher/email/user_role_changed.txt
#, python-format
msgid "Current %(role_name)s: %(current_user_name)s"
msgstr ""
"Çürrént %(role_name)s: %(current_user_name)s Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#: templates/publisher/email/user_role_changed.txt
#, python-format
msgid ""
"The %(role_name)s for the %(course_title)s: %(course_url)s course has "
"changed."
msgstr ""
"Thé %(role_name)s för thé %(course_title)s: %(course_url)s çöürsé häs "
"çhängéd. Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє¢тєтυя #"
#: templates/publisher/seat_form.html
msgid "Seat Form"
msgstr "Séät Förm Ⱡ'σяєм ιρѕυм ∂σł#"
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-03-09 14:55+0500\n"
"POT-Creation-Date: 2017-03-09 15:51+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
......
......@@ -56,7 +56,7 @@
</span>
{% if role_widget.can_change_role_assignment %}
<a class="change-role-assignment" data-role="{{ role_widget.course_role.role }}" href="#">
{% trans "change owner" %}
{% trans "change" %}
</a>
{% endif %}
</div>
......
{% extends "publisher/email/email_base.html" %}{% load i18n %}
{% extends "publisher/email/email_base.html" %}
{% load i18n %}
{% block body %}
<p>
{% blocktrans trimmed with link_start='<a href="' link_middle='">' link_end='</a>' %}
The {{ role_name }} for the {{ link_start }}{{ course_url }}{{ link_middle }}{{ course_title }}{{ link_end }}
course has changed.
The {{ role_name }} for the {{ link_start }}{{ course_url }}{{ link_middle }}{{ course_title }}{{ link_end }} has been changed.
{% endblocktrans %}
</p>
......
{% load i18n %}
{% blocktrans trimmed %}
The {{ role_name }} for the {{ course_title }}: {{ course_url }} course has changed.
The {{ role_name }} for the {{ course_title }}: {{ course_url }} has been changed.
{% endblocktrans %}
{% blocktrans trimmed %}
......
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