Commit be0d3f2a by Waheed Ahmed

Added legal team role and its group.

ECOM-7792
parent 3149be97
......@@ -4,9 +4,10 @@ from django.contrib.auth.models import Group
from guardian.admin import GuardedModelAdmin
from guardian.shortcuts import assign_perm
from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.constants import (PARTNER_MANAGER_GROUP_NAME, PROJECT_COORDINATOR_GROUP_NAME,
PUBLISHER_GROUP_NAME, REVIEWER_GROUP_NAME)
from course_discovery.apps.publisher.choices import InternalUserRole
from course_discovery.apps.publisher.constants import (LEGAL_TEAM_GROUP_NAME, PARTNER_MANAGER_GROUP_NAME,
PROJECT_COORDINATOR_GROUP_NAME, PUBLISHER_GROUP_NAME,
REVIEWER_GROUP_NAME)
from course_discovery.apps.publisher.forms import (CourseRunAdminForm, CourseUserRoleForm, OrganizationUserRoleForm,
PublisherUserCreationForm, UserAttributesAdminForm)
from course_discovery.apps.publisher.models import (Course, CourseRun, CourseRunState, CourseState, CourseUserRole,
......@@ -51,6 +52,13 @@ class OrganizationExtensionAdmin(GuardedModelAdmin):
]
self.assign_permissions(obj, Group.objects.get(name=PROJECT_COORDINATOR_GROUP_NAME), pc_permissions)
# Assign view permissions to Legal Team group.
legal_team_permissions = [
OrganizationExtension.VIEW_COURSE,
OrganizationExtension.VIEW_COURSE_RUN
]
self.assign_permissions(obj, Group.objects.get(name=LEGAL_TEAM_GROUP_NAME), legal_team_permissions)
def assign_permissions(self, obj, group, permissions):
for permission in permissions:
assign_perm(permission, group, obj)
......@@ -65,10 +73,10 @@ class UserAttributesAdmin(admin.ModelAdmin):
class OrganizationUserRoleAdmin(admin.ModelAdmin):
form = OrganizationUserRoleForm
role_groups_dict = {
PublisherUserRole.MarketingReviewer: REVIEWER_GROUP_NAME,
PublisherUserRole.ProjectCoordinator: PROJECT_COORDINATOR_GROUP_NAME,
PublisherUserRole.Publisher: PUBLISHER_GROUP_NAME,
PublisherUserRole.PartnerManager: PARTNER_MANAGER_GROUP_NAME
InternalUserRole.MarketingReviewer: REVIEWER_GROUP_NAME,
InternalUserRole.ProjectCoordinator: PROJECT_COORDINATOR_GROUP_NAME,
InternalUserRole.Publisher: PUBLISHER_GROUP_NAME,
InternalUserRole.PartnerManager: PARTNER_MANAGER_GROUP_NAME
}
def save_model(self, request, obj, form, change):
......
......@@ -10,7 +10,7 @@ from django.core.urlresolvers import reverse
from django.db import IntegrityError
from django.test import TestCase
from guardian.shortcuts import assign_perm
from mock import patch
from mock import mock, patch
from opaque_keys.edx.keys import CourseKey
from testfixtures import LogCapture
......@@ -484,7 +484,8 @@ class ChangeCourseStateViewTests(TestCase):
subject = 'Review requested: {title}'.format(title=self.course.title)
self._assert_email_sent(course_team_user, subject)
def test_change_course_state_with_course_team(self):
@mock.patch('course_discovery.apps.publisher.emails.send_email_for_seo_review')
def test_change_course_state_with_course_team(self, mocked_seo_review_email):
""" Verify that if course team admin can change course workflow state,
owner role will be changed to `MarketingReviewer`.
"""
......@@ -519,6 +520,7 @@ class ChangeCourseStateViewTests(TestCase):
subject = 'Review requested: {title}'.format(title=self.course.title)
self._assert_email_sent(marketing_user, subject)
self.assertTrue(mocked_seo_review_email.called)
def _assert_email_sent(self, user, subject):
"""Helper method to assert sent email data."""
......
......@@ -7,6 +7,7 @@ PARTNER_MANAGER_GROUP_NAME = 'Partner Managers'
PROJECT_COORDINATOR_GROUP_NAME = 'Project Coordinators'
REVIEWER_GROUP_NAME = 'Marketing Reviewers'
PUBLISHER_GROUP_NAME = 'Publishers'
LEGAL_TEAM_GROUP_NAME = 'Legal Team Members'
# Being used in old migration `0019_create_user_groups`.
PARTNER_COORDINATOR_GROUP_NAME = 'Partner Coordinators'
......@@ -8,7 +8,9 @@ from django.template.loader import get_template
from django.utils.translation import ugettext_lazy as _
from opaque_keys.edx.keys import CourseKey
from course_discovery.apps.core.models import User
from course_discovery.apps.publisher.choices import PublisherUserRole
from course_discovery.apps.publisher.constants import LEGAL_TEAM_GROUP_NAME
from course_discovery.apps.publisher.utils import is_email_notification_enabled
logger = logging.getLogger(__name__)
......@@ -568,3 +570,46 @@ def send_change_role_assignment_email(course_role, former_user):
)
logger.exception(error_message)
raise Exception(error_message)
def send_email_for_seo_review(course):
""" Send email when course is submitted for seo review.
Arguments:
course (Object): Course object
"""
txt_template = 'publisher/email/course/seo_review.txt'
html_template = 'publisher/email/course/seo_review.html'
subject = _('Legal review requested: {title}').format(title=course.title) # pylint: disable=no-member
try:
legal_team_users = User.objects.filter(groups__name=LEGAL_TEAM_GROUP_NAME)
project_coordinator = course.project_coordinator
to_addresses = [user.email for user in legal_team_users]
from_address = settings.PUBLISHER_FROM_EMAIL
course_page_path = reverse('publisher:publisher_course_detail', kwargs={'pk': course.id})
context = {
'course_name': course.title,
'sender_team': _('Course team'),
'recipient_name': _('Legal Team'),
'org_name': course.organizations.all().first().name,
'contact_us_email': project_coordinator.email,
'course_page_url': 'https://{host}{path}'.format(
host=Site.objects.get_current().domain.strip('/'), path=course_page_path
)
}
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_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 legal review requested of course %s', course.id)
# -*- coding: utf-8 -*-
# Generated by Django 1.9.13 on 2017-05-18 10:17
from __future__ import unicode_literals
from django.db import migrations
from course_discovery.apps.publisher.constants import LEGAL_TEAM_GROUP_NAME
def create_legal_team_group(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
Group.objects.get_or_create(name=LEGAL_TEAM_GROUP_NAME)
def remove_legal_team_group(apps, schema_editor):
Group = apps.get_model('auth', 'Group')
Group.objects.filter(name=LEGAL_TEAM_GROUP_NAME).delete()
class Migration(migrations.Migration):
dependencies = [
('publisher', '0048_auto_20170511_1059'),
]
operations = [
migrations.RunPython(create_legal_team_group, remove_legal_team_group)
]
......@@ -524,6 +524,7 @@ class CourseState(TimeStampedModel, ChangedByMixin):
"""
Change course workflow state and ownership also send emails if required.
"""
is_notifications_enabled = waffle.switch_is_active('enable_publisher_email_notifications')
if state == CourseStateChoices.Draft:
self.draft()
elif state == CourseStateChoices.Review:
......@@ -533,10 +534,12 @@ class CourseState(TimeStampedModel, ChangedByMixin):
self.marketing_reviewed = True
elif user_role.role == PublisherUserRole.CourseTeam:
self.change_owner_role(PublisherUserRole.MarketingReviewer)
if is_notifications_enabled:
emails.send_email_for_seo_review(self.course)
self.review()
if waffle.switch_is_active('enable_publisher_email_notifications'):
if is_notifications_enabled:
emails.send_email_for_send_for_review(self.course, user)
elif state == CourseStateChoices.Approved:
......@@ -544,7 +547,7 @@ class CourseState(TimeStampedModel, ChangedByMixin):
self.approved_by_role = user_role.role
self.approved()
if waffle.switch_is_active('enable_publisher_email_notifications'):
if is_notifications_enabled:
emails.send_email_for_mark_as_reviewed(self.course, user)
self.save()
......
# pylint: disable=no-member
import mock
from django.contrib.auth.models import Group
from django.contrib.sites.models import Site
from django.core import mail
from django.core.urlresolvers import reverse
......@@ -8,11 +9,13 @@ from django.test import TestCase
from opaque_keys.edx.keys import CourseKey
from testfixtures import LogCapture
from course_discovery.apps.core.models import User
from course_discovery.apps.core.tests.factories import UserFactory
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.constants import LEGAL_TEAM_GROUP_NAME
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
......@@ -602,3 +605,53 @@ class CourseChangeRoleAssignmentEmailTests(TestCase):
with self.assertRaises(Exception) as ex:
emails.send_change_role_assignment_email(self.marketing_role, self.user)
self.assertEqual(str(ex.exception), message)
class SEOReviewEmailTests(TestCase):
""" Tests for the seo review email functionality. """
def setUp(self):
super(SEOReviewEmailTests, self).setUp()
self.user = UserFactory()
self.course_state = factories.CourseStateFactory()
self.course = self.course_state.course
self.course.organizations.add(OrganizationFactory())
factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.CourseTeam, user=self.user)
self.legal_user = UserFactory()
self.legal_user.groups.add(Group.objects.get(name=LEGAL_TEAM_GROUP_NAME))
UserAttributeFactory(user=self.user, enable_email_notification=True)
def test_email_with_error(self):
""" Verify that email failure logs error message."""
with LogCapture(emails.logger.name) as l:
emails.send_email_for_seo_review(self.course)
l.check(
(
emails.logger.name,
'ERROR',
'Failed to send email notifications for legal review requested of course {}'.format(
self.course.id
)
)
)
def test_seo_review_email(self):
"""
Verify that seo review email functionality works fine.
"""
factories.CourseUserRoleFactory(course=self.course, role=PublisherUserRole.ProjectCoordinator)
emails.send_email_for_seo_review(self.course)
expected_subject = 'Legal review requested: {title}'.format(title=self.course.title)
self.assertEqual(len(mail.outbox), 1)
legal_team_users = User.objects.filter(groups__name=LEGAL_TEAM_GROUP_NAME)
expected_addresses = [user.email for user in legal_team_users]
self.assertEqual(expected_addresses, 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('determine OFAC status', body)
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-17 15:46+0500\n"
"POT-Creation-Date: 2017-05-19 19:30+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
......@@ -545,6 +545,19 @@ msgstr ""
msgid "{role_name} changed for {course_title}"
msgstr ""
#: apps/publisher/emails.py
#, python-brace-format
msgid "Legal review requested: {title}"
msgstr ""
#: apps/publisher/emails.py
msgid "Course team"
msgstr ""
#: apps/publisher/emails.py
msgid "Legal Team"
msgstr ""
#: apps/publisher/forms.py
msgid "Remove Image"
msgstr ""
......@@ -2664,6 +2677,8 @@ msgstr ""
#: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course/seo_review.html
#: templates/publisher/email/course/seo_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
......@@ -2703,6 +2718,8 @@ msgstr ""
#: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course/seo_review.html
#: templates/publisher/email/course/seo_review.txt
#: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt
#: templates/publisher/email/course_run/mark_as_reviewed.html
......@@ -2724,6 +2741,7 @@ msgstr ""
#: templates/publisher/email/course/mark_as_reviewed.html
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/seo_review.html
#: templates/publisher/email/course_run/mark_as_reviewed.html
#: templates/publisher/email/course_run/preview_accepted.html
#: templates/publisher/email/course_run/preview_available.html
......@@ -2738,6 +2756,7 @@ msgstr ""
#: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course/seo_review.txt
#: templates/publisher/email/course_run/mark_as_reviewed.txt
#: templates/publisher/email/course_run/preview_accepted.txt
#: templates/publisher/email/course_run/preview_available.txt
......@@ -2766,6 +2785,21 @@ msgid ""
" suggest edits."
msgstr ""
#: templates/publisher/email/course/seo_review.html
#, python-format
msgid ""
"%(sender_team)s from %(org_name)s has submitted %(course_name)s for review. "
"%(link_start)s%(course_page_url)s%(link_middle)sView this course in "
"Publisher%(link_end)s to determine OFAC status."
msgstr ""
#: templates/publisher/email/course/seo_review.txt
#, python-format
msgid ""
"%(sender_team)s from %(org_name)s has submitted %(course_name)s for review. "
"%(course_page_url)s View this course in Publisher to determine OFAC status."
msgstr ""
#. Translators: project_coordinator_name is a member name.
#: templates/publisher/email/course_created.html
#, python-format
......
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-17 15:46+0500\n"
"POT-Creation-Date: 2017-05-19 19:30+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-05-17 15:46+0500\n"
"POT-Creation-Date: 2017-05-19 19:30+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
......@@ -670,6 +670,19 @@ msgstr ""
msgid "{role_name} changed for {course_title}"
msgstr "{role_name} çhängéd för {course_title} Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт,#"
#: apps/publisher/emails.py
#, python-brace-format
msgid "Legal review requested: {title}"
msgstr "Légäl révïéw réqüéstéd: {title} Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αмєт, ¢σηѕє#"
#: apps/publisher/emails.py
msgid "Course team"
msgstr "Çöürsé téäm Ⱡ'σяєм ιρѕυм ∂σłσя #"
#: apps/publisher/emails.py
msgid "Legal Team"
msgstr "Légäl Téäm Ⱡ'σяєм ιρѕυм ∂σłσ#"
#: apps/publisher/forms.py
msgid "Remove Image"
msgstr "Rémövé Ìmägé Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
......@@ -3153,6 +3166,8 @@ msgstr ""
#: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course/seo_review.html
#: templates/publisher/email/course/seo_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
......@@ -3203,6 +3218,8 @@ msgstr ""
#: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course/seo_review.html
#: templates/publisher/email/course/seo_review.txt
#: templates/publisher/email/course_created.html
#: templates/publisher/email/course_created.txt
#: templates/publisher/email/course_run/mark_as_reviewed.html
......@@ -3224,6 +3241,7 @@ msgstr "Thänks, Ⱡ'σяєм ιρѕυм #"
#: templates/publisher/email/course/mark_as_reviewed.html
#: templates/publisher/email/course/send_for_review.html
#: templates/publisher/email/course/seo_review.html
#: templates/publisher/email/course_run/mark_as_reviewed.html
#: templates/publisher/email/course_run/preview_accepted.html
#: templates/publisher/email/course_run/preview_available.html
......@@ -3240,6 +3258,7 @@ msgstr ""
#: templates/publisher/email/course/mark_as_reviewed.txt
#: templates/publisher/email/course/send_for_review.txt
#: templates/publisher/email/course/seo_review.txt
#: templates/publisher/email/course_run/mark_as_reviewed.txt
#: templates/publisher/email/course_run/preview_accepted.txt
#: templates/publisher/email/course_run/preview_available.txt
......@@ -3282,6 +3301,27 @@ msgstr ""
"%(page_url)s Vïéw thïs çöürsé ïn Püßlïshér tö märk thé çöürsé äs révïéwéd ör"
" süggést édïts. Ⱡ'σяєм #"
#: templates/publisher/email/course/seo_review.html
#, python-format
msgid ""
"%(sender_team)s from %(org_name)s has submitted %(course_name)s for review. "
"%(link_start)s%(course_page_url)s%(link_middle)sView this course in "
"Publisher%(link_end)s to determine OFAC status."
msgstr ""
"%(sender_team)s fröm %(org_name)s häs süßmïttéd %(course_name)s för révïéw. "
"%(link_start)s%(course_page_url)s%(link_middle)sVïéw thïs çöürsé ïn "
"Püßlïshér%(link_end)s tö détérmïné ÖFÀÇ stätüs. Ⱡ'σяєм ιρѕυм ∂σłσя ѕ#"
#: templates/publisher/email/course/seo_review.txt
#, python-format
msgid ""
"%(sender_team)s from %(org_name)s has submitted %(course_name)s for review. "
"%(course_page_url)s View this course in Publisher to determine OFAC status."
msgstr ""
"%(sender_team)s fröm %(org_name)s häs süßmïttéd %(course_name)s för révïéw. "
"%(course_page_url)s Vïéw thïs çöürsé ïn Püßlïshér tö détérmïné ÖFÀÇ stätüs. "
"Ⱡ'σяєм ιρѕυм ∂σłσя ѕιт αм#"
#. Translators: project_coordinator_name is a member name.
#: templates/publisher/email/course_created.html
#, python-format
......
......@@ -7,14 +7,14 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
"POT-Creation-Date: 2017-05-17 15:46+0500\n"
"POT-Creation-Date: 2017-05-19 19:30+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
......
{% extends "publisher/email/email_base.html" %}
{% load i18n %}
{% block body %}
<!-- Message Body -->
<p>
{% blocktrans trimmed %}
Dear {{ recipient_name }},
{% endblocktrans %}
<p>
{% blocktrans with link_start='<a href="' link_middle='">' link_end='</a>' trimmed %}
{{ sender_team }} from {{ org_name }} has submitted {{ course_name }} for review. {{ link_start }}{{ course_page_url }}{{ link_middle }}View this course in Publisher{{ link_end }} to determine OFAC status.
{% endblocktrans %}
</p>
{% comment %}Translators: It's closing of mail.{% endcomment %}
{% trans "Thanks," %}<br>
{{ sender_team }}
{% blocktrans trimmed %}
<p>Note: This email address is unable to receive replies. For questions or comments, contact {{ contact_us_email }}.</p>
{% endblocktrans %}
<!-- End Message Body -->
{% endblock body %}
{% load i18n %}
{% blocktrans trimmed %}
Dear {{ recipient_name }},
{% endblocktrans %}
{% blocktrans trimmed %}
{{ sender_team }} from {{ org_name }} has submitted {{ course_name }} for review. {{ course_page_url }} View this course in Publisher to determine OFAC status.
{% endblocktrans %}
{% trans "Thanks," %}
{{ sender_team }}
{% blocktrans trimmed %}
Note: This email address is unable to receive replies. For questions or comments, contact {{ contact_us_email }}.
{% endblocktrans %}
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