Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit bbbfc33

Browse files
committed
Merge pull request openedx#3552 from edx/usman/lms2565-bulk-email-display
When sending bulk email check if course is authorized.
2 parents 6e4cea7 + 8ee682d commit bbbfc33

File tree

9 files changed

+118
-33
lines changed

9 files changed

+118
-33
lines changed

lms/djangoapps/bulk_email/tests/test_course_optout.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ def navigate_to_email_view(self):
5555

5656
# Select the Email view of the instructor dash
5757
session = self.client.session
58-
session['idash_mode'] = 'Email'
58+
session[u'idash_mode:{0}'.format(self.course.location.course_id)] = 'Email'
5959
session.save()
6060
response = self.client.get(url)
6161
selected_email_link = '<a href="#" onclick="goto(\'Email\')" class="selectedmode">Email</a>'

lms/djangoapps/bulk_email/tests/test_email.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def mock_update_subtask_status(entry_id, current_task_id, new_subtask_status):
4141

4242

4343
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
44+
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
4445
class TestEmailSendFromDashboard(ModuleStoreTestCase):
4546
"""
4647
Test that emails send correctly.
@@ -76,7 +77,7 @@ def setUp(self):
7677

7778
# Select the Email view of the instructor dash
7879
session = self.client.session
79-
session['idash_mode'] = 'Email'
80+
session[u'idash_mode:{0}'.format(self.course.location.course_id)] = 'Email'
8081
session.save()
8182
response = self.client.get(self.url)
8283
selected_email_link = '<a href="#" onclick="goto(\'Email\')" class="selectedmode">Email</a>'
@@ -88,6 +89,20 @@ def tearDown(self):
8889
"""
8990
patch.stopall()
9091

92+
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': True})
93+
def test_email_disabled(self):
94+
"""
95+
Test response when email is disabled for course.
96+
"""
97+
test_email = {
98+
'action': 'Send email',
99+
'to_option': 'myself',
100+
'subject': 'test subject for myself',
101+
'message': 'test message for myself'
102+
}
103+
response = self.client.post(self.url, test_email)
104+
self.assertContains(response, "Email is not enabled for this course.")
105+
91106
def test_send_to_self(self):
92107
"""
93108
Make sure email send to myself goes to myself.

lms/djangoapps/bulk_email/tests/test_err_handling.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class EmailTestException(Exception):
3838

3939

4040
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
41+
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
4142
class TestEmailErrors(ModuleStoreTestCase):
4243
"""
4344
Test that errors from sending email are handled properly.

lms/djangoapps/instructor/tests/test_api.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
from django.test import TestCase
1212
from nose.tools import raises
1313
from mock import Mock, patch
14+
from django.conf import settings
1415
from django.test.utils import override_settings
1516
from django.core.urlresolvers import reverse
1617
from django.http import HttpRequest, HttpResponse
@@ -99,6 +100,7 @@ def test_alreadyrunningerror_ajax(self):
99100

100101

101102
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
103+
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
102104
class TestInstructorAPIDenyLevels(ModuleStoreTestCase, LoginEnrollmentTestCase):
103105
"""
104106
Ensure that users cannot access endpoints they shouldn't be able to.
@@ -1570,6 +1572,7 @@ def test_rescore_problem_all(self, act):
15701572

15711573

15721574
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
1575+
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True, 'REQUIRE_COURSE_EMAIL_AUTH': False})
15731576
class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
15741577
"""
15751578
Checks that only instructors have access to email endpoints, and that

lms/djangoapps/instructor/tests/test_legacy_email.py

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ def test_email_flag_true(self):
5050

5151
# Select the Email view of the instructor dash
5252
session = self.client.session
53-
session['idash_mode'] = 'Email'
53+
session[u'idash_mode:{0}'.format(self.course.location.course_id)] = 'Email'
5454
session.save()
5555
response = self.client.get(self.url)
5656

@@ -108,3 +108,38 @@ def test_email_flag_true_xml_store(self):
108108
# Assert that the URL for the email view is not in the response
109109
response = self.client.get(self.url)
110110
self.assertFalse(self.email_link in response.content)
111+
112+
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
113+
def test_send_mail_unauthorized(self):
114+
""" Test 'Send email' action returns an error if course is not authorized to send email. """
115+
116+
response = self.client.post(
117+
self.url, {
118+
'action': 'Send email',
119+
'to_option': 'all',
120+
'subject': "Welcome to the course!",
121+
'message': "Lets start with an introduction!"
122+
}
123+
)
124+
self.assertContains(response, "Email is not enabled for this course.")
125+
126+
@patch.dict(settings.FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
127+
def test_send_mail_authorized(self):
128+
""" Test 'Send email' action when course is authorized to send email. """
129+
130+
course_authorization = CourseAuthorization(course_id=self.course.id, email_enabled=True)
131+
course_authorization.save()
132+
133+
session = self.client.session
134+
session[u'idash_mode:{0}'.format(self.course.location.course_id)] = 'Email'
135+
session.save()
136+
137+
response = self.client.post(
138+
self.url, {
139+
'action': 'Send email',
140+
'to_option': 'all',
141+
'subject': 'Welcome to the course!',
142+
'message': 'Lets start with an introduction!',
143+
}
144+
)
145+
self.assertContains(response, "Your email was successfully queued for sending.")

lms/djangoapps/instructor/views/api.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@
6666
parse_datetime,
6767
set_due_date_extension,
6868
strip_if_string,
69+
bulk_email_is_enabled_for_course,
6970
)
7071
from xmodule.modulestore import Location
7172

@@ -1036,6 +1037,10 @@ def send_email(request, course_id):
10361037
- 'subject' specifies email's subject
10371038
- 'message' specifies email's content
10381039
"""
1040+
1041+
if not bulk_email_is_enabled_for_course(course_id):
1042+
return HttpResponseForbidden("Email is not enabled for this course.")
1043+
10391044
send_to = request.POST.get("send_to")
10401045
subject = request.POST.get("subject")
10411046
message = request.POST.get("message")

lms/djangoapps/instructor/views/instructor_dashboard.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
from bulk_email.models import CourseAuthorization
2626
from class_dashboard.dashboard_data import get_section_display_name, get_array_section_has_problem
2727

28-
from .tools import get_units_with_due_date, title_or_url
28+
from .tools import get_units_with_due_date, title_or_url, bulk_email_is_enabled_for_course
2929

3030

3131
@ensure_csrf_cookie
@@ -60,7 +60,7 @@ def instructor_dashboard_2(request, course_id):
6060
sections.insert(3, _section_extensions(course))
6161

6262
# Gate access to course email by feature flag & by course-specific authorization
63-
if settings.FEATURES['ENABLE_INSTRUCTOR_EMAIL'] and is_studio_course and CourseAuthorization.instructor_email_enabled(course_id):
63+
if bulk_email_is_enabled_for_course(course_id):
6464
sections.append(_section_send_email(course_id, access, course))
6565

6666
# Gate access to Metrics tab by featue flag and staff authorization

lms/djangoapps/instructor/views/legacy.py

Lines changed: 31 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
)
4949
from django_comment_client.utils import has_forum_access
5050
from instructor.offline_gradecalc import student_grades, offline_grades_available
51-
from instructor.views.tools import strip_if_string
51+
from instructor.views.tools import strip_if_string, bulk_email_is_enabled_for_course
5252
from instructor_task.api import (
5353
get_running_instructor_tasks,
5454
get_instructor_task_history,
@@ -110,10 +110,11 @@ def instructor_dashboard(request, course_id):
110110
# the instructor dashboard page is modal: grades, psychometrics, admin
111111
# keep that state in request.session (defaults to grades mode)
112112
idash_mode = request.POST.get('idash_mode', '')
113+
idash_mode_key = u'idash_mode:{0}'.format(course_id)
113114
if idash_mode:
114-
request.session['idash_mode'] = idash_mode
115+
request.session[idash_mode_key] = idash_mode
115116
else:
116-
idash_mode = request.session.get('idash_mode', 'Grades')
117+
idash_mode = request.session.get(idash_mode_key, 'Grades')
117118

118119
enrollment_number = CourseEnrollment.num_enrolled_in(course_id)
119120

@@ -760,33 +761,36 @@ def getdat(u):
760761
email_subject = request.POST.get("subject")
761762
html_message = request.POST.get("message")
762763

763-
try:
764-
# Create the CourseEmail object. This is saved immediately, so that
765-
# any transaction that has been pending up to this point will also be
766-
# committed.
767-
email = CourseEmail.create(course_id, request.user, email_to_option, email_subject, html_message)
764+
if bulk_email_is_enabled_for_course(course_id):
765+
try:
766+
# Create the CourseEmail object. This is saved immediately, so that
767+
# any transaction that has been pending up to this point will also be
768+
# committed.
769+
email = CourseEmail.create(course_id, request.user, email_to_option, email_subject, html_message)
768770

769-
# Submit the task, so that the correct InstructorTask object gets created (for monitoring purposes)
770-
submit_bulk_course_email(request, course_id, email.id) # pylint: disable=E1101
771+
# Submit the task, so that the correct InstructorTask object gets created (for monitoring purposes)
772+
submit_bulk_course_email(request, course_id, email.id) # pylint: disable=E1101
771773

772-
except Exception as err:
773-
# Catch any errors and deliver a message to the user
774-
error_msg = "Failed to send email! ({0})".format(err)
775-
msg += "<font color='red'>" + error_msg + "</font>"
776-
log.exception(error_msg)
774+
except Exception as err:
775+
# Catch any errors and deliver a message to the user
776+
error_msg = "Failed to send email! ({0})".format(err)
777+
msg += "<font color='red'>" + error_msg + "</font>"
778+
log.exception(error_msg)
777779

778-
else:
779-
# If sending the task succeeds, deliver a success message to the user.
780-
if email_to_option == "all":
781-
text = _(
782-
"Your email was successfully queued for sending. "
783-
"Please note that for large classes, it may take up to an hour "
784-
"(or more, if other courses are simultaneously sending email) "
785-
"to send all emails."
786-
)
787780
else:
788-
text = _('Your email was successfully queued for sending.')
789-
email_msg = '<div class="msg msg-confirm"><p class="copy">{text}</p></div>'.format(text=text)
781+
# If sending the task succeeds, deliver a success message to the user.
782+
if email_to_option == "all":
783+
text = _(
784+
"Your email was successfully queued for sending. "
785+
"Please note that for large classes, it may take up to an hour "
786+
"(or more, if other courses are simultaneously sending email) "
787+
"to send all emails."
788+
)
789+
else:
790+
text = _('Your email was successfully queued for sending.')
791+
email_msg = '<div class="msg msg-confirm"><p class="copy">{text}</p></div>'.format(text=text)
792+
else:
793+
msg += "<font color='red'>Email is not enabled for this course.</font>"
790794

791795
elif "Show Background Email Task History" in action:
792796
message, datatable = get_background_task_table(course_id, task_type='bulk_course_email')
@@ -895,8 +899,7 @@ def get_analytics_result(analytics_name):
895899
# 1. Feature flag is on
896900
# 2. We have explicitly enabled email for the given course via django-admin
897901
# 3. It is NOT an XML course
898-
if settings.FEATURES['ENABLE_INSTRUCTOR_EMAIL'] and \
899-
CourseAuthorization.instructor_email_enabled(course_id) and is_studio_course:
902+
if bulk_email_is_enabled_for_course(course_id):
900903
show_email_tab = True
901904

902905
# display course stats only if there is no other table to display:

lms/djangoapps/instructor/views/tools.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,18 @@
44
import dateutil
55
import json
66

7+
from django.conf import settings
78
from django.contrib.auth.models import User
89
from django.http import HttpResponseBadRequest
910
from django.utils.timezone import utc
1011
from django.utils.translation import ugettext as _
1112

1213
from courseware.models import StudentModule
1314
from xmodule.fields import Date
15+
from xmodule.modulestore import XML_MODULESTORE_TYPE
16+
from xmodule.modulestore.django import modulestore
17+
18+
from bulk_email.models import CourseAuthorization
1419

1520
DATE_FIELD = Date()
1621

@@ -45,6 +50,24 @@ def wrapper(request, course_id):
4550
return wrapper
4651

4752

53+
def bulk_email_is_enabled_for_course(course_id):
54+
"""
55+
Staff can only send bulk email for a course if all the following conditions are true:
56+
1. Bulk email feature flag is on.
57+
2. It is a studio course.
58+
3. Bulk email is enabled for the course.
59+
"""
60+
61+
bulk_email_enabled_globally = (settings.FEATURES['ENABLE_INSTRUCTOR_EMAIL'] == True)
62+
is_studio_course = (modulestore().get_modulestore_type(course_id) != XML_MODULESTORE_TYPE)
63+
bulk_email_enabled_for_course = CourseAuthorization.instructor_email_enabled(course_id)
64+
65+
if bulk_email_enabled_globally and is_studio_course and bulk_email_enabled_for_course:
66+
return True
67+
68+
return False
69+
70+
4871
def strip_if_string(value):
4972
if isinstance(value, basestring):
5073
return value.strip()

0 commit comments

Comments
 (0)