Commit 8a30e9ba by Julia Hansbrough Committed by Brian Wilson

Legacy email tests, removed duplicate code, updated comments, fixed CSS

parent fd54b060
......@@ -49,19 +49,6 @@ def register_by_course_id(course_id, username='robot', password='test', is_staff
u.save()
CourseEnrollment.enroll(u, course_id)
@world.absorb
def add_to_course_staff(username, course_num):
"""
Add the user with `username` to the course staff group
for `course_num`.
"""
# Based on code in lms/djangoapps/courseware/access.py
group_name = "instructor_{}".format(course_num)
group, _ = Group.objects.get_or_create(name=group_name)
group.save()
user = User.objects.get(username=username)
user.groups.add(group)
@world.absorb
def add_to_course_staff(username, course_num):
......
......@@ -71,6 +71,23 @@ def css_has_text(css_selector, text, index=0, strip=False):
@world.absorb
def css_has_value(css_selector, value, index=0):
"""
Return a boolean indicating whether the element with
`css_selector` has the specified `value`.
If there are multiple elements matching the css selector,
use `index` to indicate which one.
"""
# If we're expecting a non-empty string, give the page
# a chance to fill in values
if value:
world.wait_for(lambda _: world.css_value(css_selector, index=index))
return world.css_value(css_selector, index=index) == value
@world.absorb
def wait_for(func, timeout=5):
WebDriverWait(
driver=world.browser.driver,
......
@shard_2
Feature: Bulk Email
As an instructor,
As an instructor or course staff,
In order to communicate with students and staff
I want to send email to staff and students in a course.
Scenario: Send bulk email
Given I am an instructor for a course
Given I am "<Role>" for a course
When I send email to "<Recipient>"
Then Email is sent to "<Recipient>"
Examples:
| Recipient |
| myself |
| course staff |
| students, staff, and instructors |
| Role | Recipient |
| instructor | myself |
| instructor | course staff |
| instructor | students, staff, and instructors |
| staff | myself |
| staff | course staff |
| staff | students, staff, and instructors |
......@@ -2,25 +2,22 @@
Define steps for bulk email acceptance test.
"""
#pylint: disable=C0111
#pylint: disable=W0621
from lettuce import world, step
from lettuce.django import mail
from nose.tools import assert_in, assert_true, assert_equal
from nose.tools import assert_in, assert_true, assert_equal # pylint: disable=E0611
from django.core.management import call_command
from django.conf import settings
@step(u'I am an instructor for a course')
def i_am_an_instructor(step):
def i_am_an_instructor(step): # pylint: disable=W0613
# Clear existing courses to avoid conflicts
world.clear_courses()
# Create a new course
course = world.CourseFactory.create(
org='edx',
number='999',
display_name='Test Course'
)
# Register the instructor as staff for the course
world.register_by_course_id(
'edx/999/Test_Course',
......@@ -59,14 +56,14 @@ def i_am_an_instructor(step):
# Dictionary mapping a description of the email recipient
# to the corresponding <option> value in the UI.
SEND_TO_OPTIONS = {
'myself': 'myself',
'course staff': 'staff',
'students, staff, and instructors': 'all'
'myself': 'myself',
'course staff': 'staff',
'students, staff, and instructors': 'all'
}
@step(u'I send email to "([^"]*)"')
def when_i_send_an_email(step, recipient):
def when_i_send_an_email(recipient):
# Check that the recipient is valid
assert_in(
......@@ -99,10 +96,9 @@ def when_i_send_an_email(step, recipient):
world.css_click('input[name="send"]')
# Expect to see a message that the email was sent
# TODO -- identify the message by CSS ID instead of index
expected_msg = "Your email was successfully queued for sending."
assert_true(
world.css_has_text('div.request-response', expected_msg, index=1, allow_blank=False),
world.css_has_text('div.request-response', expected_msg, '#request-response', allow_blank=False),
msg="Could not find email success message."
)
......@@ -117,8 +113,9 @@ EXPECTED_ADDRESSES = {
UNSUBSCRIBE_MSG = 'To stop receiving email like this'
@step(u'Email is sent to "([^"]*)"')
def then_the_email_is_sent(step, recipient):
def then_the_email_is_sent(recipient):
# Check that the recipient is valid
assert_in(
......@@ -129,8 +126,8 @@ def then_the_email_is_sent(step, recipient):
# Retrieve messages. Because we are using celery in "always eager"
# mode, we expect all messages to be sent by this point.
messages = []
while not mail.queue.empty():
messages.append(mail.queue.get())
while not mail.queue.empty(): # pylint: disable=E1101
messages.append(mail.queue.get()) # pylint: disable=E1101
# Check that we got the right number of messages
assert_equal(
......@@ -143,8 +140,8 @@ def then_the_email_is_sent(step, recipient):
# Check that the message properties were correct
recipients = []
for msg in messages:
assert_equal(msg.subject, u'[Test Course] Hello')
assert_equal(msg.from_email, u'"Test Course" Course Staff <course-updates@edx.org>')
assert_in('Hello', msg.subject)
assert_in(settings.DEFAULT_BULK_FROM_EMAIL, msg.from_email)
# Message body should have the message we sent
# and an unsubscribe message
......@@ -153,7 +150,7 @@ def then_the_email_is_sent(step, recipient):
# Should have alternative HTML form
assert_equal(len(msg.alternatives), 1)
content, mime_type = msg.alternatives[0]
content = msg.alternatives[0]
assert_in('test message', content)
assert_in(UNSUBSCRIBE_MSG, content)
......
......@@ -7,6 +7,7 @@ import json
import requests
from urllib import quote
from django.test import TestCase
from nose.tools import raises
from mock import Mock, patch
from django.test.utils import override_settings
from django.core.urlresolvers import reverse
......@@ -695,51 +696,44 @@ class TestInstructorAPIRegradeTask(ModuleStoreTestCase, LoginEnrollmentTestCase)
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
fill this out
Checks that only instructors have access to email endpoints, and that
these endpoints are only accessible with courses that actually exist,
only with valid email messages.
"""
def setUp(self):
self.instructor = AdminFactory.create()
self.course = CourseFactory.create()
self.client.login(username=self.instructor.username, password='test')
test_subject = u'\u1234 test subject'
test_message = u'\u6824 test message'
self.full_test_message = {
'send_to': 'staff',
'subject': test_subject,
'message': test_message,
}
def test_send_email_as_logged_in_instructor(self):
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.post(url,{
'send_to': 'staff',
'subject': 'test subject',
'message': 'test message',
})
response = self.client.post(url, self.full_test_message)
self.assertEqual(response.status_code, 200)
def test_send_email_but_not_logged_in(self):
self.client.logout()
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.post(url, {
'send_to': 'staff',
'subject': 'test subject',
'message': 'test message',
})
response = self.client.post(url, self.full_test_message)
self.assertEqual(response.status_code, 403)
def test_send_email_but_not_staff(self):
self.client.logout()
self.student = UserFactory()
self.client.login(username=self.student.username, password='test')
student = UserFactory()
self.client.login(username=student.username, password='test')
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.post(url, {
'send_to': 'staff',
'subject': 'test subject',
'message': 'test message',
})
response = self.client.post(url, self.full_test_message)
self.assertEqual(response.status_code, 403)
def test_send_email_but_course_not_exist(self):
url = reverse('send_email', kwargs={'course_id': 'GarbageCourse/DNE/NoTerm'})
response = self.client.post(url, {
'send_to': 'staff',
'subject': 'test subject',
'message': 'test message',
})
response = self.client.post(url, self.full_test_message)
self.assertNotEqual(response.status_code, 200)
def test_send_email_no_sendto(self):
......@@ -747,7 +741,7 @@ class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
response = self.client.post(url, {
'subject': 'test subject',
'message': 'test message',
})
})
self.assertEqual(response.status_code, 400)
def test_send_email_no_subject(self):
......@@ -755,7 +749,7 @@ class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
response = self.client.post(url, {
'send_to': 'staff',
'message': 'test message',
})
})
self.assertEqual(response.status_code, 400)
def test_send_email_no_message(self):
......@@ -763,81 +757,7 @@ class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
response = self.client.post(url, {
'send_to': 'staff',
'subject': 'test subject',
})
self.assertEqual(response.status_code, 400)
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
class TestInstructorSendEmail(ModuleStoreTestCase, LoginEnrollmentTestCase):
"""
fill this out
"""
def setUp(self):
self.instructor = AdminFactory.create()
self.course = CourseFactory.create()
self.client.login(username=self.instructor.username, password='test')
def test_send_email_as_logged_in_instructor(self):
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.get(url,{
'send_to': 'staff',
'subject': 'test subject',
'message': 'test message',
})
self.assertEqual(response.status_code, 200)
def test_send_email_but_not_logged_in(self):
self.client.logout()
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'send_to': 'staff',
'subject': 'test subject',
'message': 'test message',
})
self.assertEqual(response.status_code, 403)
def test_send_email_but_not_staff(self):
self.client.logout()
self.student = UserFactory()
self.client.login(username=self.student.username, password='test')
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'send_to': 'staff',
'subject': 'test subject',
'message': 'test message',
})
self.assertEqual(response.status_code, 403)
def test_send_email_but_course_not_exist(self):
url = reverse('send_email', kwargs={'course_id': 'GarbageCourse/DNE/NoTerm'})
response = self.client.get(url, {
'send_to': 'staff',
'subject': 'test subject',
'message': 'test message',
})
self.assertNotEqual(response.status_code, 200)
def test_send_email_no_sendto(self):
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'subject': 'test subject',
'message': 'test message',
})
self.assertEqual(response.status_code, 400)
def test_send_email_no_subject(self):
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'send_to': 'staff',
'message': 'test message',
})
self.assertEqual(response.status_code, 400)
def test_send_email_no_message(self):
url = reverse('send_email', kwargs={'course_id': self.course.id})
response = self.client.get(url, {
'send_to': 'staff',
'subject': 'test subject',
})
})
self.assertEqual(response.status_code, 400)
......@@ -1043,8 +963,7 @@ class TestInstructorAPIHelpers(TestCase):
output = 'i4x://MITx/6.002x/problem/L2Node1'
self.assertEqual(_msk_from_problem_urlname(*args), output)
# TODO add this back in as soon as i know where the heck "raises" comes from
#@raises(ValueError)
#def test_msk_from_problem_urlname_error(self):
# args = ('notagoodcourse', 'L2Node1')
# _msk_from_problem_urlname(*args)
@raises(ValueError)
def test_msk_from_problem_urlname_error(self):
args = ('notagoodcourse', 'L2Node1')
_msk_from_problem_urlname(*args)
"""
Unit tests for email feature flag in instructor dashboard
and student dashboard. Additionally tests that bulk email
is always disabled for non-Mongo backed courses, regardless
of email feature flag.
Unit tests for email feature flag in new instructor dashboard.
Additionally tests that bulk email is always disabled for
non-Mongo backed courses, regardless of email feature flag.
"""
from django.test.utils import override_settings
......@@ -10,28 +9,19 @@ from django.conf import settings
from django.core.urlresolvers import reverse
from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
from student.tests.factories import AdminFactory, UserFactory, CourseEnrollmentFactory
from student.tests.factories import AdminFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore import XML_MODULESTORE_TYPE
from courseware.tests.modulestore_config import TEST_DATA_MIXED_MODULESTORE
from mock import patch
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class TestNewInstructorDashboardEmailView(ModuleStoreTestCase):
"""
Check for email view displayed with flag
"""
# will need to check for Mongo vs XML, ENABLED vs not enabled,
# is studio course vs not studio course
# section_data
# what is html_module?
# which are API lines
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class TestInstructorDashboardEmailView(ModuleStoreTestCase):
class TestNewInstructorDashboardEmailViewMongoBacked(ModuleStoreTestCase):
"""
Check for email view displayed with flag
Check for email view on the new instructor dashboard
for Mongo-backed courses
"""
def setUp(self):
self.course = CourseFactory.create()
......@@ -51,64 +41,52 @@ class TestInstructorDashboardEmailView(ModuleStoreTestCase):
"""
patch.stopall()
# Enabled and IS mongo
# In order for bulk email to work, we must have both the ENABLE_INSTRUCTOR_EMAIL_FLAG
# set to True and for the course to be Mongo-backed.
# The flag is enabled and the course is Mongo-backed (should work)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true(self):
def test_email_flag_true_mongo_true(self):
# Assert that the URL for the email view is in the response
response = self.client.get(self.url)
self.assertTrue(self.email_link in response.content)
self.assertIn(self.email_link, response.content)
send_to_label = '<label for="id_to">Send to:</label>'
self.assertTrue(send_to_label in response.content)
self.assertEqual(response.status_code,200)
self.assertEqual(response.status_code, 200)
# Disabled but IS mongo
# The course is Mongo-backed but the flag is disabled (should not work)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': False})
def test_email_flag_false(self):
def test_email_flag_false_mongo_true(self):
# Assert that the URL for the email view is not in the response
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
# Enabled but NOT mongo
@patch.dict(settings.MITX_FEATURES,{'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_false(self):
with patch('xmodule.modulestore.mongo.base.MongoModuleStore.get_modulestore_type') as mock_modulestore:
mock_modulestore.return_value = XML_MODULESTORE_TYPE
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true_xml_store(self):
# If the enable email setting is enabled, but this is an XML backed course,
# the email view shouldn't be available on the instructor dashboard.
@override_settings(MODULESTORE=TEST_DATA_MIXED_MODULESTORE)
class TestNewInstructorDashboardEmailViewXMLBacked(ModuleStoreTestCase):
"""
Check for email view on the new instructor dashboard
"""
def setUp(self):
self.course_name = 'edX/toy/2012_Fall'
# The course factory uses a MongoModuleStore backing, so patch the
# `get_modulestore_type` method to pretend to be XML-backed.
# This is OK; we're simply testing that the `is_mongo_modulestore_type` flag
# in `instructor/views/legacy.py` is doing the correct thing.
# Create instructor account
instructor = AdminFactory.create()
self.client.login(username=instructor.username, password="test")
with patch('xmodule.modulestore.mongo.base.MongoModuleStore.get_modulestore_type') as mock_modulestore:
mock_modulestore.return_value = XML_MODULESTORE_TYPE
# URL for instructor dash
self.url = reverse('instructor_dashboard_2', kwargs={'course_id': self.course_name})
# URL for email view
self.email_link = '<a href="" data-section="send_email">Email</a>'
# Assert that the URL for the email view is not in the response
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
# The flag is enabled but the course is not Mongo-backed (should not work)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true_mongo_false(self):
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
# Disabled and IS Mongo
# The flag is disabled and the course is not Mongo-backed (should not work)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': False})
def test_email_flag_true_xml_store(self):
# If the enable email setting is enabled, but this is an XML backed course,
# the email view shouldn't be available on the instructor dashboard.
# The course factory uses a MongoModuleStore backing, so patch the
# `get_modulestore_type` method to pretend to be XML-backed.
# This is OK; we're simply testing that the `is_mongo_modulestore_type` flag
# in `instructor/views/legacy.py` is doing the correct thing.
with patch('xmodule.modulestore.mongo.base.MongoModuleStore.get_modulestore_type') as mock_modulestore:
mock_modulestore.return_value = XML_MODULESTORE_TYPE
# Assert that the URL for the email view is not in the response
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
def test_email_flag_false_mongo_false(self):
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
"""
Unit tests for email feature flag in legacy instructor dashboard
and student dashboard. Additionally tests that bulk email
is always disabled for non-Mongo backed courses, regardless
of email feature flag.
"""
from django.test.utils import override_settings
from django.conf import settings
from django.core.urlresolvers import reverse
from courseware.tests.tests import TEST_DATA_MONGO_MODULESTORE
from student.tests.factories import AdminFactory, UserFactory, CourseEnrollmentFactory
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
from xmodule.modulestore.tests.factories import CourseFactory
from xmodule.modulestore import XML_MODULESTORE_TYPE
from mock import patch
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class TestInstructorDashboardEmailView(ModuleStoreTestCase):
"""
Check for email view displayed with flag
"""
def setUp(self):
self.course = CourseFactory.create()
# Create instructor account
instructor = AdminFactory.create()
self.client.login(username=instructor.username, password="test")
# URL for instructor dash
self.url = reverse('instructor_dashboard', kwargs={'course_id': self.course.id})
# URL for email view
self.email_link = '<a href="#" onclick="goto(\'Email\')" class="None">Email</a>'
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true(self):
# Assert that the URL for the email view is in the response
response = self.client.get(self.url)
self.assertTrue(self.email_link in response.content)
# Select the Email view of the instructor dash
session = self.client.session
session['idash_mode'] = 'Email'
session.save()
response = self.client.get(self.url)
# Ensure we've selected the view properly and that the send_to field is present.
selected_email_link = '<a href="#" onclick="goto(\'Email\')" class="selectedmode">Email</a>'
self.assertTrue(selected_email_link in response.content)
send_to_label = '<label for="id_to">Send to:</label>'
self.assertTrue(send_to_label in response.content)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': False})
def test_email_flag_false(self):
# Assert that the URL for the email view is not in the response
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true_xml_store(self):
# If the enable email setting is enabled, but this is an XML backed course,
# the email view shouldn't be available on the instructor dashboard.
# The course factory uses a MongoModuleStore backing, so patch the
# `get_modulestore_type` method to pretend to be XML-backed.
# This is OK; we're simply testing that the `is_mongo_modulestore_type` flag
# in `instructor/views/legacy.py` is doing the correct thing.
with patch('xmodule.modulestore.mongo.base.MongoModuleStore.get_modulestore_type') as mock_modulestore:
mock_modulestore.return_value = XML_MODULESTORE_TYPE
# Assert that the URL for the email view is not in the response
response = self.client.get(self.url)
self.assertFalse(self.email_link in response.content)
@override_settings(MODULESTORE=TEST_DATA_MONGO_MODULESTORE)
class TestStudentDashboardEmailView(ModuleStoreTestCase):
"""
Check for email view displayed with flag
"""
def setUp(self):
self.course = CourseFactory.create()
# Create student account
student = UserFactory.create()
CourseEnrollmentFactory.create(user=student, course_id=self.course.id)
self.client.login(username=student.username, password="test")
# URL for dashboard
self.url = reverse('dashboard')
# URL for email settings modal
self.email_modal_link = (('<a href="#email-settings-modal" class="email-settings" rel="leanModal" '
'data-course-id="{0}/{1}/{2}" data-course-number="{1}" '
'data-optout="False">Email Settings</a>')
.format(self.course.org,
self.course.number,
self.course.display_name.replace(' ', '_')))
def tearDown(self):
"""
Undo all patches.
"""
patch.stopall()
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true(self):
# Assert that the URL for the email view is in the response
response = self.client.get(self.url)
self.assertTrue(self.email_modal_link in response.content)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': False})
def test_email_flag_false(self):
# Assert that the URL for the email view is not in the response
response = self.client.get(self.url)
self.assertFalse(self.email_modal_link in response.content)
@patch.dict(settings.MITX_FEATURES, {'ENABLE_INSTRUCTOR_EMAIL': True})
def test_email_flag_true_xml_store(self):
# If the enable email setting is enabled, but this is an XML backed course,
# the email view shouldn't be available on the instructor dashboard.
# The course factory uses a MongoModuleStore backing, so patch the
# `get_modulestore_type` method to pretend to be XML-backed.
# This is OK; we're simply testing that the `is_mongo_modulestore_type` flag
# in `instructor/views/legacy.py` is doing the correct thing.
with patch('xmodule.modulestore.mongo.base.MongoModuleStore.get_modulestore_type') as mock_modulestore:
mock_modulestore.return_value = XML_MODULESTORE_TYPE
# Assert that the URL for the email view is not in the response
response = self.client.get(self.url)
self.assertFalse(self.email_modal_link in response.content)
......@@ -98,7 +98,7 @@ def require_query_params(*args, **kwargs):
for (param, extra) in required_params:
default = object()
if request.GET.get(param, default) == default:
error_response_data['parameters'] += [param]
error_response_data['parameters'].append(param)
error_response_data['info'][param] = extra
if len(error_response_data['parameters']) > 0:
......@@ -108,14 +108,14 @@ def require_query_params(*args, **kwargs):
return wrapped
return decorator
def require_post_params(*args, **kwargs):
"""
Checks for required paremters or renders a 400 error.
Checks for required parameters or renders a 400 error.
(decorator with arguments)
`args` is a *list of required GET parameter names.
`kwargs` is a **dict of required GET parameter names
to string explanations of the parameter
Functions like 'require_query_params', but checks for
POST parameters rather than GET parameters.
"""
required_params = []
required_params += [(arg, None) for arg in args]
......@@ -135,7 +135,7 @@ def require_post_params(*args, **kwargs):
for (param, extra) in required_params:
default = object()
if request.POST.get(param, default) == default:
error_response_data['parameters'] += [param]
error_response_data['parameters'].append(param)
error_response_data['info'][param] = extra
if len(error_response_data['parameters']) > 0:
......@@ -755,46 +755,11 @@ def send_email(request, course_id):
Send an email to self, staff, or everyone involved in a course.
Query Parameters:
- 'send_to' specifies what group the email should be sent to
Options are defined by the Email model in
lms/djangoapps/bulk_email/models.py
- 'subject' specifies email's subject
- 'message' specifies email's content
"""
course = get_course_by_id(course_id)
send_to = request.POST.get("send_to")
subject = request.POST.get("subject")
message = request.POST.get("message")
text_message = html_to_text(message)
email = CourseEmail(
course_id=course_id,
sender=request.user,
to_option=send_to,
subject=subject,
html_message=message,
text_message=text_message
)
email.save()
tasks.delegate_email_batches.delay(
email.id,
request.user.id
)
response_payload = {
'course_id': course_id,
}
return JsonResponse(response_payload)
@ensure_csrf_cookie
@cache_control(no_cache=True, no_store=True, must_revalidate=True)
@require_level('staff')
@require_post_params(send_to="sending to whom", subject="subject line", message="message text")
def send_email(request, course_id):
"""
Send an email to self, staff, or everyone involved in a course.
Query Parameters:
- 'send_to' specifies what group the email should be sent to
- 'subject' specifies email's subject
- 'message' specifies email's content
"""
course = get_course_by_id(course_id)
send_to = request.POST.get("send_to")
subject = request.POST.get("subject")
message = request.POST.get("message")
......@@ -805,16 +770,11 @@ def send_email(request, course_id):
to_option=send_to,
subject=subject,
html_message=message,
text_message=text_message
text_message=text_message,
)
email.save()
tasks.delegate_email_batches.delay(
email.id,
request.user.id
)
response_payload = {
'course_id': course_id,
}
tasks.delegate_email_batches.delay(email.id, request.user.id) # pylint: disable=E1101
response_payload = {'course_id': course_id}
return JsonResponse(response_payload)
......
......@@ -47,11 +47,10 @@ def instructor_dashboard_2(request, course_id):
_section_membership(course_id, access),
_section_student_admin(course_id, access),
_section_data_download(course_id),
_section_analytics(course_id)
_section_analytics(course_id),
]
enrollment_count = sections[0]['enrollment_count']
disable_buttons = False
max_enrollment_for_buttons = settings.MITX_FEATURES.get("MAX_ENROLLMENT_INSTR_BUTTONS")
if max_enrollment_for_buttons is not None:
......@@ -160,14 +159,14 @@ def _section_data_download(course_id):
def _section_send_email(course_id, access, course):
""" Provide data for the corresponding bulk email section """
html_module = HtmlDescriptor(course.system, DictFieldData({'data': ''}), ScopeIds(None, None, None, None))
fragment = course.system.render(html_module, None, 'studio_view')
fragment = course.system.render(html_module, 'studio_view')
fragment = wrap_xmodule('xmodule_edit.html', html_module, 'studio_view', fragment, None)
email_editor = fragment.content
section_data = {
'section_key': 'send_email',
'section_display_name': _('Email'),
'access': access,
'send_email': reverse('send_email',kwargs={'course_id': course_id}),
'access': access,
'send_email': reverse('send_email', kwargs={'course_id': course_id}),
'editor': email_editor
}
return section_data
......
......@@ -74,6 +74,14 @@ DATABASES = {
}
}
TRACKING_BACKENDS.update({
'mongo': {
'ENGINE': 'track.backends.mongodb.MongoBackend'
}
})
DEFAULT_BULK_FROM_EMAIL = "test@test.org"
# Forums are disabled in test.py to speed up unit tests, but we do not have
# per-test control for acceptance tests
MITX_FEATURES['ENABLE_DISCUSSION_SERVICE'] = True
......@@ -111,7 +119,7 @@ FEEDBACK_SUBMISSION_EMAIL = 'dummy@example.com'
# Include the lettuce app for acceptance testing, including the 'harvest' django-admin command
INSTALLED_APPS += ('lettuce.django',)
LETTUCE_APPS = ('courseware',)
LETTUCE_APPS = ('courseware', 'instructor',)
LETTUCE_BROWSER = os.environ.get('LETTUCE_BROWSER', 'chrome')
# Where to run: local, saucelabs, or grid
......
......@@ -114,7 +114,7 @@ MITX_FEATURES = {
# analytics experiments
'ENABLE_INSTRUCTOR_ANALYTICS': False,
'ENABLE_INSTRUCTOR_EMAIL': True,
'ENABLE_INSTRUCTOR_EMAIL': False,
# enable analytics server.
# WARNING: THIS SHOULD ALWAYS BE SET TO FALSE UNDER NORMAL
......
......@@ -27,9 +27,15 @@ class SendEmail
else if @$emailEditor.save()['data'] == ""
alert gettext("Your message cannot be blank.")
else
success_message = gettext("Your email was successfully queued for sending.")
send_to = @$send_to.val().toLowerCase()
if send_to == "myself"
send_to = gettext("yourself")
else if send_to == "staff"
send_to = gettext("everyone who is staff or instructor on this course")
else
send_to = gettext("ALL (everyone who is enrolled in this course as student, staff, or instructor)")
success_message = gettext("Your email was successfully queued for sending. Please note that for large public classes (~10k), it may take 1-2 hours to send all emails.")
subject = gettext(@$subject.val())
confirm_message = gettext("You are about to send an email titled \"#{subject}\" to #{send_to}. Is this OK?")
if confirm confirm_message
......@@ -46,21 +52,27 @@ class SendEmail
url: @$btn_send.data 'endpoint'
data: send_data
success: (data) =>
@display_response gettext('Your email was successfully queued for sending.')
$(".msg-confirm").css({"display":"block"})
error: std_ajax_err => @fail_with_error gettext('Error sending email.')
$(".msg-confirm").css({"display":"none"})
@display_response success_message
error: std_ajax_err =>
@fail_with_error gettext('Error sending email.')
else
@$task_response.empty()
@$request_response_error.empty()
fail_with_error: (msg) ->
console.warn msg
@$task_response.empty()
@$request_response_error.empty()
@$request_response_error.text gettext(msg)
$(".msg-confirm").css({"display":"none"})
display_response: (data_from_server) ->
@$task_response.empty()
@$request_response_error.empty()
@$task_response.text(gettext('Your email was successfully queued for sending.'))
@$task_response.text(data_from_server)
$(".msg-confirm").css({"display":"block"})
# Email Section
......
......@@ -22,8 +22,9 @@
// system feedback - messages
.msg {
border-radius: 1px;
padding: 10px 15px;
margin-bottom: 20px;
padding: $baseline/2 $baseline*0.75;
margin-bottom: $baseline;
font-weight: 600;
.copy {
font-weight: 600;
......@@ -44,10 +45,8 @@
.msg-confirm {
border-top: 2px solid $confirm-color;
background: tint($confirm-color,95%);
.copy {
color: $confirm-color;
}
display: none;
color: $confirm-color;
}
// TYPE: confirm
......@@ -76,7 +75,7 @@
.list-advice {
list-style: none;
padding: 0;
margin: 20px 0;
margin: $baseline 0;
.item {
font-weight: 600;
......@@ -249,7 +248,7 @@ section.instructor-dashboard-content-2 {
padding: 0;
.field {
margin-bottom: 20px;
margin-bottom: $baseline;
padding: 0;
&:last-child {
......@@ -257,43 +256,6 @@ section.instructor-dashboard-content-2 {
}
}
}
// system feedback - messages
.msg {
border-radius: 1px;
padding: 10px 15px;
margin-bottom: 20px;
font-weight: 600;
color: green;
.copy {
font-weight: 600;
}
}
.msg-confirm {
background: tint(green,90%);
display: none;
}
.list-advice {
list-style: none;
padding: 0;
margin: 20px 0;
.item {
font-weight: 600;
margin-bottom: 10px;
&:last-child {
margin-bottom: 0;
}
}
}
.copy {
font-weight: 600; }
.msg-confirm {
background: #e5f2e5; }
}
......
......@@ -174,7 +174,7 @@ function goto( mode)
<p>
<input type="submit" name="action" value="Download CSV of answer distributions" class="${'is-disabled' if disable_buttons else ''}">
<input type="submit" name="action" value="Dump description of graded assignments configuration" class="${'is-disabled' if disable_buttons else ''}">
<input type="submit" name="action" value="Dump description of graded assignments configuration">
</p>
<hr width="40%" style="align:left">
......
<%! from django.utils.translation import ugettext as _ %>
<%page args="section_data"/>
<script language="JavaScript" type="text/javascript">
</script>
<script type="text/javascript" src="jsi18n/"></script>
<div class="vert-left send-email">
<div class="vert-left send-email" id="section-send-email">
<h2> ${_("Send Email")} </h2>
<div class="request-response msg msg-confirm copy" id="request-response"></div>
<ul class="list-fields">
......
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