Unsubscribe link added in bulk emails
This commit is contained in:
@@ -12,6 +12,11 @@ from bulk_email.models_api import (
|
||||
is_user_opted_out_for_course
|
||||
)
|
||||
|
||||
from django.conf import settings
|
||||
from django.urls import reverse
|
||||
|
||||
from lms.djangoapps.discussion.notification_prefs.views import UsernameCipher
|
||||
|
||||
|
||||
def get_emails_enabled(user, course_id):
|
||||
"""
|
||||
@@ -28,3 +33,16 @@ def get_emails_enabled(user, course_id):
|
||||
if is_bulk_email_feature_enabled(course_id=course_id):
|
||||
return not is_user_opted_out_for_course(user=user, course_id=course_id)
|
||||
return None
|
||||
|
||||
|
||||
def get_unsubscribed_link(username, course_id):
|
||||
"""
|
||||
|
||||
:param username: username
|
||||
:param course_id:
|
||||
:return: AES encrypted token based on the user email
|
||||
"""
|
||||
token = UsernameCipher.encrypt(username)
|
||||
optout_url = reverse('bulk_email_opt_out', kwargs={'token': token, 'course_id': course_id})
|
||||
url = '{base_url}{optout_url}'.format(base_url=settings.LMS_ROOT_URL, optout_url=optout_url)
|
||||
return url
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -24,25 +24,25 @@
|
||||
<tbody class='mcnTextBlockOuter'>
|
||||
<tr>
|
||||
<td valign='top' class='mcnTextBlockInner' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
|
||||
|
||||
<table align='left' border='0' cellpadding='0' cellspacing='0' width='366' class='mcnTextContentContainer' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
<tbody><tr>
|
||||
|
||||
|
||||
<td valign='top' class='mcnTextContent' style='padding-top: 9px;padding-left: 18px;padding-bottom: 9px;padding-right: 0;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;color: #606060;font-family: Helvetica;font-size: 11px;line-height: 125%;text-align: left;'>
|
||||
|
||||
|
||||
<br>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table></td>
|
||||
</tr>
|
||||
</table>
|
||||
</td>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
<!-- // END PREHEADER -->
|
||||
@@ -63,11 +63,11 @@
|
||||
<table align='left' width='100%' border='0' cellpadding='0' cellspacing='0' class='mcnImageContentContainer' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
<tbody><tr>
|
||||
<td class='mcnImageContent' valign='top' style='padding-right: 9px;padding-left: 9px;padding-top: 0;padding-bottom: 0;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
|
||||
|
||||
<a href='http://edx.org' title='' class='' target='_self' style='word-wrap: break-word !important;'>
|
||||
<img align='left' alt='edX' src='http://courses.edx.org/static/images/bulk_email/edXHeaderImage.jpg' width='564.0000152587891' style='max-width: 600px;padding-bottom: 0;display: inline !important;vertical-align: bottom;border: 0;line-height: 100%;outline: none;text-decoration: none;height: auto !important;' class='mcnImage'>
|
||||
</a>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
@@ -78,19 +78,19 @@
|
||||
<tbody class='mcnTextBlockOuter'>
|
||||
<tr>
|
||||
<td valign='top' class='mcnTextBlockInner' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
|
||||
|
||||
<table align='left' border='0' cellpadding='0' cellspacing='0' width='599' class='mcnTextContentContainer' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
<tbody><tr>
|
||||
|
||||
|
||||
<td valign='top' class='mcnTextContent' style='padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;color: #606060;font-family: Helvetica;font-size: 15px;line-height: 150%;text-align: left;'>
|
||||
|
||||
|
||||
<div style='text-align: right;'>
|
||||
<span style='font-size:11px;'><span style='color:#00a0e3;'>Connect with edX:</span></span> <a href='http://facebook.com/edxonline' target='_blank' style='color: #6DC6DD;font-weight: normal;text-decoration: underline;word-wrap: break-word !important;'><img align='none' height='16' src='http://courses.edx.org/static/images/bulk_email/FacebookIcon.png' style='width: 16px;height: 16px;border: 0;line-height: 100%;outline: none;text-decoration: none;' width='16'></a> <a href='http://twitter.com/edxonline' target='_blank' style='color: #6DC6DD;font-weight: normal;text-decoration: underline;word-wrap: break-word !important;'><img align='none' height='16' src='http://courses.edx.org/static/images/bulk_email/TwitterIcon.png' style='width: 16px;height: 16px;border: 0;line-height: 100%;outline: none;text-decoration: none;' width='16'></a> <a href='https://plus.google.com/108235383044095082735' target='_blank' style='color: #6DC6DD;font-weight: normal;text-decoration: underline;word-wrap: break-word !important;'><img align='none' height='16' src='http://courses.edx.org/static/images/bulk_email/GooglePlusIcon.png' style='width: 16px;height: 16px;border: 0;line-height: 100%;outline: none;text-decoration: none;' width='16'></a> <a href='http://www.meetup.com/edX-Communities/' target='_blank' style='color: #6DC6DD;font-weight: normal;text-decoration: underline;word-wrap: break-word !important;'><img align='none' height='16' src='http://courses.edx.org/static/images/bulk_email/MeetupIcon.png' style='width: 16px;height: 16px;border: 0;line-height: 100%;outline: none;text-decoration: none;' width='16'></a></div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -115,7 +115,7 @@
|
||||
<tbody class='mcnCaptionBlockOuter'>
|
||||
<tr>
|
||||
<td class='mcnCaptionBlockInner' valign='top' style='padding: 9px;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
|
||||
|
||||
|
||||
<table border='0' cellpadding='0' cellspacing='0' class='mcnCaptionLeftContentOuter' width='100%' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
<tbody><tr>
|
||||
@@ -123,11 +123,11 @@
|
||||
<table align='right' border='0' cellpadding='0' cellspacing='0' class='mcnCaptionLeftImageContentContainer' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
<tbody><tr>
|
||||
<td class='mcnCaptionLeftImageContent' valign='top' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
|
||||
|
||||
|
||||
|
||||
<img alt='' src='{course_image_url}' width='176' style='max-width: 180px;border: 0;line-height: 100%;outline: none;text-decoration: none;vertical-align: bottom;height: auto !important;' class='mcnImage'>
|
||||
|
||||
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
@@ -156,17 +156,17 @@
|
||||
<tbody class='mcnTextBlockOuter'>
|
||||
<tr>
|
||||
<td valign='top' class='mcnTextBlockInner' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
|
||||
|
||||
<table align='left' border='0' cellpadding='0' cellspacing='0' width='600' class='mcnTextContentContainer' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
<tbody><tr>
|
||||
|
||||
|
||||
<td valign='top' class='mcnTextContent' style='padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;color: #606060;font-family: Helvetica;font-size: 14px;line-height: 150%;text-align: left;'>
|
||||
{{message_body}}
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -188,19 +188,19 @@
|
||||
<tbody class='mcnTextBlockOuter'>
|
||||
<tr>
|
||||
<td valign='top' class='mcnTextBlockInner' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
|
||||
|
||||
<table align='left' border='0' cellpadding='0' cellspacing='0' width='600' class='mcnTextContentContainer' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
<tbody><tr>
|
||||
|
||||
|
||||
<td valign='top' class='mcnTextContent' style='padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;color: #606060;font-family: Helvetica;font-size: 14px;line-height: 150%;text-align: left;'>
|
||||
|
||||
|
||||
<div style='text-align: right;'>
|
||||
<a href='http://facebook.com/edxonline' target='_blank' style='color: #2f73bc;font-weight: normal;text-decoration: underline;word-wrap: break-word !important;'><img align='none' height='16' src='http://courses.edx.org/static/images/bulk_email/FacebookIcon.png' style='width: 16px;height: 16px;border: 0;line-height: 100%;outline: none;text-decoration: none;' width='16'></a> <a href='http://twitter.com/edxonline' target='_blank' style='color: #2f73bc;font-weight: normal;text-decoration: underline;word-wrap: break-word !important;'><img align='none' height='16' src='http://courses.edx.org/static/images/bulk_email/TwitterIcon.png' style='width: 16px;height: 16px;border: 0;line-height: 100%;outline: none;text-decoration: none;' width='16'></a> <a href='https://plus.google.com/108235383044095082735' target='_blank' style='color: #2f73bc;font-weight: normal;text-decoration: underline;word-wrap: break-word !important;'><img align='none' height='16' src='http://courses.edx.org/static/images/bulk_email/GooglePlusIcon.png' style='width: 16px;height: 16px;border: 0;line-height: 100%;outline: none;text-decoration: none;' width='16'></a> <a href='http://www.meetup.com/edX-Communities/' target='_blank' style='color: #2f73bc;font-weight: normal;text-decoration: underline;word-wrap: break-word !important;'><img align='none' height='16' src='http://courses.edx.org/static/images/bulk_email/MeetupIcon.png' style='width: 16px;height: 16px;border: 0;line-height: 100%;outline: none;text-decoration: none;' width='16'></a></div>
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
@@ -225,12 +225,12 @@
|
||||
<tbody class='mcnTextBlockOuter'>
|
||||
<tr>
|
||||
<td valign='top' class='mcnTextBlockInner' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
|
||||
|
||||
<table align='left' border='0' cellpadding='0' cellspacing='0' width='600' class='mcnTextContentContainer' style='border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;'>
|
||||
<tbody><tr>
|
||||
|
||||
|
||||
<td valign='top' class='mcnTextContent' style='padding-top: 9px;padding-right: 18px;padding-bottom: 9px;padding-left: 18px;border-collapse: collapse;mso-table-lspace: 0pt;mso-table-rspace: 0pt;color: #f2f2f2;font-family: Helvetica;font-size: 11px;line-height: 125%;text-align: left;'>
|
||||
|
||||
|
||||
<em>Copyright © 2013 edX, All rights reserved.</em><br>
|
||||
<br>
|
||||
<br>
|
||||
@@ -242,11 +242,12 @@
|
||||
<br>
|
||||
This email was automatically sent from {platform_name}. <br>
|
||||
You are receiving this email at address {email} because you are enrolled in <a href='{course_url}'>{course_title}</a>.<br>
|
||||
To stop receiving email like this, update your course email settings <a href='{email_settings_url}'>here</a>. <br>
|
||||
To stop receiving email like this, update your course email settings <a href='{email_settings_url}'>here</a>. <br><br>
|
||||
<a href='{unsubscribe_link}'>unsubscribe</a>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody></table>
|
||||
|
||||
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
||||
@@ -41,6 +41,7 @@ from markupsafe import escape
|
||||
from six import text_type
|
||||
|
||||
from bulk_email.models import CourseEmail, Optout
|
||||
from bulk_email.api import get_unsubscribed_link
|
||||
from lms.djangoapps.courseware.courses import get_course
|
||||
from lms.djangoapps.instructor_task.models import InstructorTask
|
||||
from lms.djangoapps.instructor_task.subtasks import (
|
||||
@@ -187,7 +188,7 @@ def perform_delegate_email_batches(entry_id, course_id, task_input, action_name)
|
||||
# inefficient OUTER JOIN query that would read the whole user table.
|
||||
combined_set = recipient_qsets[0].union(*recipient_qsets[1:]) if len(recipient_qsets) > 1 \
|
||||
else recipient_qsets[0]
|
||||
recipient_fields = ['profile__name', 'email']
|
||||
recipient_fields = ['profile__name', 'email', 'username']
|
||||
|
||||
log.info(u"Task %s: Preparing to queue subtasks for sending emails for course %s, email %s",
|
||||
task_id, course_id, email_id)
|
||||
@@ -502,6 +503,7 @@ def _send_course_email(entry_id, email_id, to_list, global_email_context, subtas
|
||||
|
||||
# use the CourseEmailTemplate that was associated with the CourseEmail
|
||||
course_email_template = course_email.get_template()
|
||||
|
||||
try:
|
||||
connection = get_connection()
|
||||
connection.open()
|
||||
@@ -537,6 +539,8 @@ def _send_course_email(entry_id, email_id, to_list, global_email_context, subtas
|
||||
email_context['name'] = current_recipient['profile__name']
|
||||
email_context['user_id'] = current_recipient['pk']
|
||||
email_context['course_id'] = course_email.course_id
|
||||
email_context['unsubscribe_link'] = get_unsubscribed_link(current_recipient['username'],
|
||||
text_type(course_email.course_id))
|
||||
|
||||
# Construct message content using templates and context:
|
||||
plaintext_msg = course_email_template.render_plaintext(course_email.text_message, email_context)
|
||||
|
||||
@@ -23,6 +23,8 @@ from student.tests.factories import AdminFactory, CourseEnrollmentFactory, UserF
|
||||
from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
|
||||
from xmodule.modulestore.tests.factories import CourseFactory
|
||||
|
||||
from lms.djangoapps.bulk_email.api import get_unsubscribed_link
|
||||
|
||||
|
||||
@patch('bulk_email.models.html_to_text', Mock(return_value='Mocking CourseEmail.text_message', autospec=True))
|
||||
class TestOptoutCourseEmails(ModuleStoreTestCase):
|
||||
@@ -87,6 +89,30 @@ class TestOptoutCourseEmails(ModuleStoreTestCase):
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
self.assertEqual(mail.outbox[0].to[0], self.instructor.email)
|
||||
|
||||
def test_optout_using_unsubscribe_link_in_email(self):
|
||||
"""
|
||||
Make sure email is't sent to learner after opt out.
|
||||
"""
|
||||
self.client.logout()
|
||||
|
||||
self.client.login(username=self.instructor.username, password="test")
|
||||
|
||||
unsubscribe_link = get_unsubscribed_link(self.student.username, text_type(self.course.id))
|
||||
response = self.client.post(unsubscribe_link, {'unsubscribe': True})
|
||||
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertContains(response, 'You have successfully unsubscribed from')
|
||||
|
||||
test_email = {
|
||||
'action': 'Send email',
|
||||
'send_to': '["myself", "learners"]',
|
||||
'subject': 'Checking unsubscribe link in email',
|
||||
'message': 'test message for all'
|
||||
}
|
||||
response = self.client.post(self.send_mail_url, test_email)
|
||||
self.assertEqual(json.loads(response.content.decode('utf-8')), self.success_content)
|
||||
self.assertEqual(len(mail.outbox), 1)
|
||||
|
||||
def test_optin_course(self):
|
||||
"""
|
||||
Make sure student receives course email after opting in.
|
||||
|
||||
@@ -24,6 +24,7 @@ from mock import Mock, patch
|
||||
from bulk_email.models import BulkEmailFlag, Optout
|
||||
from bulk_email.tasks import _get_course_email_context, _get_source_address
|
||||
from course_modes.models import CourseMode
|
||||
|
||||
from lms.djangoapps.courseware.tests.factories import InstructorFactory, StaffFactory
|
||||
from lms.djangoapps.instructor_task.subtasks import update_subtask_status
|
||||
from openedx.core.djangoapps.course_groups.cohorts import add_user_to_cohort
|
||||
@@ -603,6 +604,28 @@ class TestEmailSendFromDashboardMockedHtmlToText(EmailSendFromDashboardTestCase)
|
||||
[s.email for s in added_users if s not in optouts])
|
||||
six.assertCountEqual(self, outbox_contents, should_send_contents)
|
||||
|
||||
def test_unsubscribe_link_in_email(self):
|
||||
"""
|
||||
Make sure opt out link is present in email.
|
||||
"""
|
||||
|
||||
test_email = {
|
||||
'action': 'Send email',
|
||||
'send_to': '["learners"]',
|
||||
'subject': 'Checking unsubscribe link in email',
|
||||
'message': 'test message for all'
|
||||
}
|
||||
response = self.client.post(self.send_mail_url, test_email)
|
||||
self.assertEqual(json.loads(response.content.decode('utf-8')), self.success_content)
|
||||
|
||||
# check unsubscribe link in template
|
||||
for m in mail.outbox:
|
||||
plain_template = m.body
|
||||
html_template = m.alternatives[0][0]
|
||||
|
||||
assert u'bulk_email/email/optout/' in plain_template
|
||||
assert u'bulk_email/email/optout/' in html_template
|
||||
|
||||
|
||||
@skipIf(os.environ.get("TRAVIS") == 'true', "Skip this test in Travis CI.")
|
||||
class TestEmailSendFromDashboard(EmailSendFromDashboardTestCase):
|
||||
|
||||
@@ -188,6 +188,7 @@ class CourseEmailTemplateTest(TestCase):
|
||||
'email_settings_url': "/location/of/email/settings/url",
|
||||
'platform_name': 'edX',
|
||||
'email': 'your-email@test.com',
|
||||
'unsubscribe_link': '/bulk_email/email/optout/dummy'
|
||||
}
|
||||
return context
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
"""
|
||||
Test the bulk email opt out view.
|
||||
"""
|
||||
from six import text_type
|
||||
|
||||
import ddt
|
||||
from django.http import Http404
|
||||
@@ -12,6 +11,8 @@ from django.urls import reverse
|
||||
|
||||
from bulk_email.models import Optout
|
||||
from bulk_email.views import opt_out_email_updates
|
||||
from six import text_type
|
||||
|
||||
from lms.djangoapps.discussion.notification_prefs.views import UsernameCipher
|
||||
from openedx.core.lib.tests import attr
|
||||
from student.tests.factories import UserFactory
|
||||
@@ -28,10 +29,10 @@ class OptOutEmailUpdatesViewTest(ModuleStoreTestCase):
|
||||
"""
|
||||
def setUp(self):
|
||||
super(OptOutEmailUpdatesViewTest, self).setUp()
|
||||
self.user = UserFactory.create(username="testuser1")
|
||||
self.user = UserFactory.create(username="testuser1", email='test@example.com')
|
||||
self.course = CourseFactory.create(run='testcourse1', display_name='Test Course Title')
|
||||
self.token = UsernameCipher.encrypt('testuser1')
|
||||
self.request_factory = RequestFactory()
|
||||
self.course = CourseFactory.create(run='testcourse1', display_name='Test Course Title')
|
||||
self.url = reverse('bulk_email_opt_out', args=[self.token, text_type(self.course.id)])
|
||||
|
||||
# Ensure we start with no opt-out records
|
||||
@@ -42,28 +43,28 @@ class OptOutEmailUpdatesViewTest(ModuleStoreTestCase):
|
||||
Ensure that the default GET view asks for confirmation.
|
||||
"""
|
||||
response = self.client.get(self.url)
|
||||
self.assertContains(response, "Do you want to unsubscribe from emails for Test Course Title?")
|
||||
self.assertContains(response, "confirm unsubscribe from")
|
||||
self.assertEqual(Optout.objects.count(), 0)
|
||||
|
||||
def test_opt_out_email_unsubscribe(self):
|
||||
"""
|
||||
Ensure that the POSTing "confirm" creates the opt-out record.
|
||||
"""
|
||||
response = self.client.post(self.url, {'submit': 'confirm'})
|
||||
self.assertContains(response, "You have been unsubscribed from emails for Test Course Title.")
|
||||
response = self.client.post(self.url, {'unsubscribe': True})
|
||||
self.assertContains(response, "You have successfully unsubscribed from")
|
||||
self.assertEqual(Optout.objects.count(), 1)
|
||||
|
||||
def test_opt_out_email_cancel(self):
|
||||
"""
|
||||
Ensure that the POSTing "cancel" does not create the opt-out record
|
||||
"""
|
||||
response = self.client.post(self.url, {'submit': 'cancel'})
|
||||
self.assertContains(response, "You have not been unsubscribed from emails for Test Course Title.")
|
||||
response = self.client.post(self.url)
|
||||
self.assertContains(response, "You have not been unsubscribed from")
|
||||
self.assertEqual(Optout.objects.count(), 0)
|
||||
|
||||
@ddt.data(
|
||||
("ZOMG INVALID BASE64 CHARS!!!", "base64url", False),
|
||||
("Non-ASCII\xff", "base64url", False),
|
||||
("Non-ASCII\xff".encode(), "base64url", False),
|
||||
("D6L8Q01ztywqnr3coMOlq0C3DG05686lXX_1ArEd0ok", "base64url", False),
|
||||
("AAAAAAAAAAA=", "initialization_vector", False),
|
||||
("nMXVK7PdSlKPOovci-M7iqS09Ux8VoCNDJixLBmj", "aes", False),
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
URLs for bulk_email app
|
||||
"""
|
||||
|
||||
from django.conf import settings
|
||||
from django.conf.urls import url
|
||||
from django.conf import settings
|
||||
|
||||
from bulk_email import views
|
||||
|
||||
|
||||
@@ -35,8 +35,9 @@ def opt_out_email_updates(request, token, course_id):
|
||||
|
||||
Raises a 404 if there are any errors parsing the input.
|
||||
"""
|
||||
|
||||
try:
|
||||
username = UsernameCipher().decrypt(token.encode())
|
||||
username = UsernameCipher().decrypt(token)
|
||||
user = User.objects.get(username=username)
|
||||
course_key = CourseKey.from_string(course_id)
|
||||
course = get_course_by_id(course_key, depth=0)
|
||||
@@ -49,23 +50,22 @@ def opt_out_email_updates(request, token, course_id):
|
||||
except InvalidKeyError:
|
||||
raise Http404("course")
|
||||
|
||||
unsub_check = request.POST.get('unsubscribe', False)
|
||||
context = {
|
||||
'course': course,
|
||||
'cancelled': False,
|
||||
'confirmed': False,
|
||||
'unsubscribe': unsub_check
|
||||
}
|
||||
|
||||
if request.method == 'POST':
|
||||
if request.POST.get('submit') == 'confirm':
|
||||
Optout.objects.get_or_create(user=user, course_id=course.id)
|
||||
log.info(
|
||||
u"User %s (%s) opted out of receiving emails from course %s",
|
||||
user.username,
|
||||
user.email,
|
||||
course_id,
|
||||
)
|
||||
context['confirmed'] = True
|
||||
else:
|
||||
context['cancelled'] = True
|
||||
if request.method == 'GET':
|
||||
return render_to_response('bulk_email/confirm_unsubscribe.html', context)
|
||||
|
||||
return render_to_response('bulk_email/unsubscribe.html', context)
|
||||
if request.method == 'POST' and unsub_check:
|
||||
Optout.objects.get_or_create(user=user, course_id=course_key)
|
||||
log.info(
|
||||
u"User %s (%s) opted out of receiving emails from course %s",
|
||||
user.username,
|
||||
user.email,
|
||||
course_id,
|
||||
)
|
||||
|
||||
return render_to_response('bulk_email/unsubscribe_success.html', context)
|
||||
|
||||
@@ -22,3 +22,26 @@
|
||||
width: flex-grid(6);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.container.unsubscribe-bulk-email {
|
||||
max-width: 400px;
|
||||
min-width: 320px;
|
||||
margin: 0 auto;
|
||||
padding: ($baseline*3) 15px ($baseline*6);
|
||||
|
||||
h1 {
|
||||
@include text-align(left);
|
||||
|
||||
font-size: 30px;
|
||||
}
|
||||
|
||||
.button {
|
||||
background-color: #23419f;
|
||||
border-color: #23419f;
|
||||
border: 2px solid;
|
||||
border-radius: 25px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
46
lms/templates/bulk_email/confirm_unsubscribe.html
Normal file
46
lms/templates/bulk_email/confirm_unsubscribe.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
from openedx.core.djangolib.markup import Text, HTML
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
%>
|
||||
<%inherit file="../main.html" />
|
||||
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="pagetitle">${Text(_("Unsubscribe"))}</%block>
|
||||
<section class="container unsubscribe-bulk-email">
|
||||
|
||||
<section>
|
||||
<h1>
|
||||
|
||||
<%block name="pageheader">${Text(_("Unsubscribe"))} </%block>
|
||||
|
||||
</h1>
|
||||
|
||||
<form class="message" action="" method="POST">
|
||||
|
||||
<%block name="pagecontent">
|
||||
|
||||
<input type="hidden" name="csrfmiddlewaretoken" value="${ csrf_token }">
|
||||
<input type="checkbox" name="unsubscribe" checked>
|
||||
${Text(_("confirm unsubscribe from {org} {course} emails.")).format(
|
||||
org=HTML("<strong>{}'s</strong>").format(course.display_org_with_default),
|
||||
course=HTML("<strong>{}</strong>").format(course.display_name_with_default)
|
||||
)
|
||||
}
|
||||
<br><br>
|
||||
<p>
|
||||
<strong>${_("Note:")}</strong>
|
||||
${_("You will still receive course emails from other courses you are enrolled in.")}
|
||||
</p>
|
||||
<br><br>
|
||||
<input class="button" type="submit" value="Confirm">
|
||||
|
||||
</%block>
|
||||
|
||||
</form>
|
||||
|
||||
|
||||
</section>
|
||||
</section>
|
||||
@@ -1,48 +0,0 @@
|
||||
<%page expression_filter="h" />
|
||||
<%inherit file="../main.html" />
|
||||
<%!
|
||||
from openedx.core.djangolib.markup import Text
|
||||
from django.utils.translation import ugettext as _
|
||||
%>
|
||||
<%def name="header()">
|
||||
%if confirmed:
|
||||
${Text(_("Unsubscribe Successful"))}
|
||||
%elif cancelled:
|
||||
${Text(_("Unsubscribe Cancelled"))}
|
||||
%else:
|
||||
${Text(_("Confirm Unsubscribe"))}
|
||||
%endif
|
||||
</%def>
|
||||
|
||||
<%block name="pagetitle">${header()}</%block>
|
||||
<section class="container unsubscribe">
|
||||
|
||||
<section class="message">
|
||||
<h1>
|
||||
<%block name="pageheader">${header()}</%block>
|
||||
</h1>
|
||||
<p>
|
||||
<%block name="pagecontent">
|
||||
%if confirmed:
|
||||
${Text(_("You have been unsubscribed from emails for {course}.")).format(
|
||||
course=course.display_name_with_default
|
||||
)}
|
||||
%elif cancelled:
|
||||
${Text(_("You have not been unsubscribed from emails for {course}.")).format(
|
||||
course=course.display_name_with_default
|
||||
)}
|
||||
%else:
|
||||
${Text(_("Do you want to unsubscribe from emails for {course}?")).format(
|
||||
course=course.display_name_with_default
|
||||
)}
|
||||
<br /><br />
|
||||
<form method="post">
|
||||
<input type="hidden" id="csrf_token" name="csrfmiddlewaretoken" value="${csrf_token}">
|
||||
<button name="submit" value="confirm" type="submit">${Text(_('Unsubscribe'))}</button>
|
||||
<button name="submit" value="cancel" type="submit">${Text(_('Cancel'))}</button>
|
||||
</form>
|
||||
%endif
|
||||
</%block>
|
||||
</p>
|
||||
</section>
|
||||
</section>
|
||||
46
lms/templates/bulk_email/unsubscribe_success.html
Normal file
46
lms/templates/bulk_email/unsubscribe_success.html
Normal file
@@ -0,0 +1,46 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
from openedx.core.djangolib.markup import HTML, Text
|
||||
from django.utils.translation import ugettext as _
|
||||
|
||||
%>
|
||||
<%inherit file="../main.html" />
|
||||
|
||||
<%namespace name='static' file='../static_content.html'/>
|
||||
|
||||
<%block name="pagetitle">${Text(_("Unsubscribe"))}</%block>
|
||||
<section class="container unsubscribe-bulk-email">
|
||||
|
||||
<section class="message">
|
||||
<h1>
|
||||
|
||||
<%block name="pageheader">${Text(_("Unsubscribe"))} </%block>
|
||||
|
||||
</h1>
|
||||
|
||||
<%block name="pagecontent">
|
||||
|
||||
%if unsubscribe:
|
||||
${Text(_("You have successfully unsubscribed from {org} {course} emails.")).format(
|
||||
org=HTML("<strong>{}'s</strong>").format(course.display_org_with_default),
|
||||
course=HTML("<strong>{}</strong>").format(course.display_name_with_default)
|
||||
)}
|
||||
|
||||
%else:
|
||||
${Text(_("You have not been unsubscribed from {org} {course} emails.")).format(
|
||||
org=HTML("<strong>{}'s</strong>").format(course.display_org_with_default),
|
||||
course=HTML("<strong>{}</strong>").format(course.display_name_with_default)
|
||||
)}
|
||||
|
||||
%endif
|
||||
<br><br>
|
||||
${Text(_("{link_start}Return to edX.org{link_end}")).format(
|
||||
link_start=HTML("<u> <a href='https://www.edx.org/'>"),
|
||||
link_end=HTML("</a> </u> "),
|
||||
)}
|
||||
|
||||
</%block>
|
||||
|
||||
</section>
|
||||
|
||||
</section>
|
||||
Reference in New Issue
Block a user