diff --git a/CHANGELOG.rst b/CHANGELOG.rst index 68308980adca..51a98f2de770 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -5,6 +5,10 @@ These are notable changes in edx-platform. This is a rolling list of changes, in roughly chronological order, most recent first. Add your entries at or near the top. Include a label indicating the component affected. +Studio: Send e-mails to new Studio users (on edge only) when their course creator +status has changed. This will not be in use until the course creator table +is enabled. + LMS: Added user preferences (arbitrary user/key/value tuples, for which which user/key is unique) and a REST API for reading users and preferences. Access to the REST API is restricted by use of the diff --git a/cms/djangoapps/contentstore/tests/test_users.py b/cms/djangoapps/contentstore/tests/test_users.py index 701dd246d867..8fea4004dd7f 100644 --- a/cms/djangoapps/contentstore/tests/test_users.py +++ b/cms/djangoapps/contentstore/tests/test_users.py @@ -42,7 +42,7 @@ def setUp(self): self.disable_course_creation = { "DISABLE_COURSE_CREATION": True, "ENABLE_CREATOR_GROUP": True, - 'STAFF_EMAIL': 'mark@marky.mark', + 'STUDIO_REQUEST_EMAIL': 'mark@marky.mark', } self.enable_creator_group = {"ENABLE_CREATOR_GROUP": True} diff --git a/cms/djangoapps/course_creators/admin.py b/cms/djangoapps/course_creators/admin.py index 7518946270f2..65473d8bde6d 100644 --- a/cms/djangoapps/course_creators/admin.py +++ b/cms/djangoapps/course_creators/admin.py @@ -6,7 +6,13 @@ from course_creators.views import update_course_creator_group from django.contrib import admin +from django.conf import settings from django.dispatch import receiver +from mitxmako.shortcuts import render_to_string + +import logging + +log = logging.getLogger("studio.coursecreatoradmin") def get_email(obj): @@ -60,4 +66,25 @@ def update_creator_group_callback(sender, **kwargs): """ Callback for when the model's creator status has changed. """ - update_course_creator_group(kwargs['caller'], kwargs['user'], kwargs['add']) + user = kwargs['user'] + updated_state = kwargs['state'] + update_course_creator_group(kwargs['caller'], user, updated_state == CourseCreator.GRANTED) + + studio_request_email = settings.MITX_FEATURES.get('STUDIO_REQUEST_EMAIL','') + context = {'studio_request_email': studio_request_email} + + subject = render_to_string('emails/course_creator_subject.txt', context) + subject = ''.join(subject.splitlines()) + if updated_state == CourseCreator.GRANTED: + message_template = 'emails/course_creator_granted.txt' + elif updated_state == CourseCreator.DENIED: + message_template = 'emails/course_creator_denied.txt' + else: + # changed to unrequested or pending + message_template = 'emails/course_creator_revoked.txt' + message = render_to_string(message_template, context) + + try: + user.email_user(subject, message, studio_request_email) + except: + log.warning("Unable to send course creator status e-mail to %s", user.email) diff --git a/cms/djangoapps/course_creators/models.py b/cms/djangoapps/course_creators/models.py index 4975705407ff..ba434c9140b3 100644 --- a/cms/djangoapps/course_creators/models.py +++ b/cms/djangoapps/course_creators/models.py @@ -68,7 +68,7 @@ def post_save_callback(sender, **kwargs): sender=sender, caller=instance.admin, user=instance.user, - add=instance.state == CourseCreator.GRANTED + state=instance.state ) instance.state_changed = timezone.now() diff --git a/cms/djangoapps/course_creators/tests/test_admin.py b/cms/djangoapps/course_creators/tests/test_admin.py index 6ef48746e713..91a28d77ae24 100644 --- a/cms/djangoapps/course_creators/tests/test_admin.py +++ b/cms/djangoapps/course_creators/tests/test_admin.py @@ -13,6 +13,11 @@ from auth.authz import is_user_in_creator_group +def mock_render_to_string(template_name, context): + """Return a string that encodes template_name and context""" + return str((template_name, context)) + + class CourseCreatorAdminTest(TestCase): """ Tests for course creator admin. @@ -32,17 +37,40 @@ def setUp(self): self.creator_admin = CourseCreatorAdmin(self.table_entry, AdminSite()) - def test_change_status(self): + @mock.patch('course_creators.admin.render_to_string', mock.Mock(side_effect=mock_render_to_string, autospec=True)) + @mock.patch('django.contrib.auth.models.User.email_user') + def test_change_status(self, email_user): """ - Tests that updates to state impact the creator group maintained in authz.py. + Tests that updates to state impact the creator group maintained in authz.py and that e-mails are sent. """ + STUDIO_REQUEST_EMAIL = 'mark@marky.mark' + def change_state(state, is_creator): """ Helper method for changing state """ self.table_entry.state = state self.creator_admin.save_model(self.request, self.table_entry, None, True) self.assertEqual(is_creator, is_user_in_creator_group(self.user)) + + context = {'studio_request_email': STUDIO_REQUEST_EMAIL} + if state == CourseCreator.GRANTED: + template = 'emails/course_creator_granted.txt' + elif state == CourseCreator.DENIED: + template = 'emails/course_creator_denied.txt' + else: + template = 'emails/course_creator_revoked.txt' + email_user.assert_called_with( + mock_render_to_string('emails/course_creator_subject.txt', context), + mock_render_to_string(template, context), + STUDIO_REQUEST_EMAIL + ) + + with mock.patch.dict( + 'django.conf.settings.MITX_FEATURES', + { + "ENABLE_CREATOR_GROUP": True, + "STUDIO_REQUEST_EMAIL": STUDIO_REQUEST_EMAIL + }): - with mock.patch.dict('django.conf.settings.MITX_FEATURES', {"ENABLE_CREATOR_GROUP": True}): # User is initially unrequested. self.assertFalse(is_user_in_creator_group(self.user)) diff --git a/cms/envs/common.py b/cms/envs/common.py index 0ca0d1b65fbd..f5baa3211b65 100644 --- a/cms/envs/common.py +++ b/cms/envs/common.py @@ -42,8 +42,8 @@ # do not display video when running automated acceptance tests 'STUB_VIDEO_FOR_TESTING': False, - # email address for staff (eg to request course creation) - 'STAFF_EMAIL': '', + # email address for studio staff (eg to request course creation) + 'STUDIO_REQUEST_EMAIL': '', 'STUDIO_NPS_SURVEY': True, diff --git a/cms/templates/emails/course_creator_denied.txt b/cms/templates/emails/course_creator_denied.txt new file mode 100644 index 000000000000..739ece6b6f27 --- /dev/null +++ b/cms/templates/emails/course_creator_denied.txt @@ -0,0 +1,5 @@ +<%! from django.utils.translation import ugettext as _ %> + +${_("Your request for course creation rights to edX Studio have been denied. If you believe this was in error, please contact: ")} + +${ studio_request_email } diff --git a/cms/templates/emails/course_creator_granted.txt b/cms/templates/emails/course_creator_granted.txt new file mode 100644 index 000000000000..a867f94334cc --- /dev/null +++ b/cms/templates/emails/course_creator_granted.txt @@ -0,0 +1,9 @@ +<%! from django.utils.translation import ugettext as _ %> + +${_("Your request for course creation rights to edX Studio have been granted. To create your first course, visit:")} + +% if is_secure: +https://${ site } +% else: +http://${ site } +% endif diff --git a/cms/templates/emails/course_creator_revoked.txt b/cms/templates/emails/course_creator_revoked.txt new file mode 100644 index 000000000000..839c5a0d78d4 --- /dev/null +++ b/cms/templates/emails/course_creator_revoked.txt @@ -0,0 +1,5 @@ +<%! from django.utils.translation import ugettext as _ %> + +${_("Your course creation rights to edX Studio have been revoked. If you believe this was in error, please contact: ")} + +${ studio_request_email } diff --git a/cms/templates/emails/course_creator_subject.txt b/cms/templates/emails/course_creator_subject.txt new file mode 100644 index 000000000000..35ec0ecc4885 --- /dev/null +++ b/cms/templates/emails/course_creator_subject.txt @@ -0,0 +1,2 @@ +<%! from django.utils.translation import ugettext as _ %> +${_("Your course creator status for edX Studio")} diff --git a/cms/templates/index.html b/cms/templates/index.html index df3e4672001d..53c744c78037 100644 --- a/cms/templates/index.html +++ b/cms/templates/index.html @@ -79,8 +79,8 @@

${_("Page Actions")}

% if course_creator_status=='granted': ${_("New Course")} - % elif course_creator_status=='disallowed_for_this_site' and settings.MITX_FEATURES.get('STAFF_EMAIL',''): - ${_("Email staff to create course")} + % elif course_creator_status=='disallowed_for_this_site' and settings.MITX_FEATURES.get('STUDIO_REQUEST_EMAIL',''): + ${_("Email staff to create course")} % endif @@ -252,10 +252,10 @@

${_('Need help?')}

- % if course_creator_status=='disallowed_for_this_site' and settings.MITX_FEATURES.get('STAFF_EMAIL',''): + % if course_creator_status=='disallowed_for_this_site' and settings.MITX_FEATURES.get('STUDIO_REQUEST_EMAIL',''):

${_('Can I create courses in Studio?')}

-

${_('In order to create courses in Studio, you must')} ${_("contact edX staff to help you create a course")}

+

${_('In order to create courses in Studio, you must')} ${_("contact edX staff to help you create a course")}

% endif