diff --git a/lms/djangoapps/bulk_email/tests/test_views.py b/lms/djangoapps/bulk_email/tests/test_views.py
deleted file mode 100644
index c062eff69d..0000000000
--- a/lms/djangoapps/bulk_email/tests/test_views.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# -*- coding: utf-8 -*-
-"""
-Test the bulk email opt out view.
-"""
-from six import text_type
-
-import ddt
-from django.http import Http404
-from django.test.client import RequestFactory
-from django.test.utils import override_settings
-from django.urls import reverse
-
-from bulk_email.models import Optout
-from bulk_email.views import opt_out_email_updates
-from notification_prefs.views import UsernameCipher
-from openedx.core.lib.tests import attr
-from student.tests.factories import UserFactory
-from xmodule.modulestore.tests.factories import CourseFactory
-from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
-
-
-@attr(shard=1)
-@ddt.ddt
-@override_settings(SECRET_KEY="test secret key")
-class OptOutEmailUpdatesViewTest(ModuleStoreTestCase):
- """
- Check the opt out email functionality.
- """
- def setUp(self):
- super(OptOutEmailUpdatesViewTest, self).setUp()
- self.user = UserFactory.create(username="testuser1")
- 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
- self.assertEqual(Optout.objects.count(), 0)
-
- def test_opt_out_email_confirm(self):
- """
- 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.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.")
- 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.")
- self.assertEqual(Optout.objects.count(), 0)
-
- @ddt.data(
- ("ZOMG INVALID BASE64 CHARS!!!", "base64url", False),
- ("Non-ASCII\xff", "base64url", False),
- ("D6L8Q01ztywqnr3coMOlq0C3DG05686lXX_1ArEd0ok", "base64url", False),
- ("AAAAAAAAAAA=", "initialization_vector", False),
- ("nMXVK7PdSlKPOovci-M7iqS09Ux8VoCNDJixLBmj", "aes", False),
- ("AAAAAAAAAAAAAAAAAAAAAMoazRI7ePLjEWXN1N7keLw=", "padding", False),
- ("AAAAAAAAAAAAAAAAAAAAACpyUxTGIrUjnpuUsNi7mAY=", "username", False),
- ("_KHGdCAUIToc4iaRGy7K57mNZiiXxO61qfKT08ExlY8=", "course", 'course-v1:testcourse'),
- )
- @ddt.unpack
- def test_unsubscribe_invalid_token(self, token, message, course):
- """
- Make sure that view returns 404 in case token is not valid
- """
- request = self.request_factory.get("dummy")
- self.assertRaisesRegexp(Http404, "^{}$".format(message), opt_out_email_updates, request, token, course)
diff --git a/lms/djangoapps/bulk_email/urls.py b/lms/djangoapps/bulk_email/urls.py
deleted file mode 100644
index 9beea793e1..0000000000
--- a/lms/djangoapps/bulk_email/urls.py
+++ /dev/null
@@ -1,18 +0,0 @@
-"""
-URLs for bulk_email app
-"""
-
-from django.conf import settings
-from django.conf.urls import url
-
-from bulk_email import views
-
-urlpatterns = [
- url(
- r'^email/optout/(?P[a-zA-Z0-9-_=]+)/{}/$'.format(
- settings.COURSE_ID_PATTERN,
- ),
- views.opt_out_email_updates,
- name='bulk_email_opt_out',
- ),
-]
diff --git a/lms/djangoapps/bulk_email/views.py b/lms/djangoapps/bulk_email/views.py
deleted file mode 100644
index 299047c176..0000000000
--- a/lms/djangoapps/bulk_email/views.py
+++ /dev/null
@@ -1,72 +0,0 @@
-"""
-Views to support bulk email functionalities like opt-out.
-"""
-
-from __future__ import division
-
-import logging
-
-from six import text_type
-
-from django.contrib.auth.models import User
-from django.http import Http404
-
-from bulk_email.models import Optout
-from courseware.courses import get_course_by_id
-from edxmako.shortcuts import render_to_response
-from notification_prefs.views import (
- UsernameCipher,
- UsernameDecryptionException,
-)
-
-from opaque_keys import InvalidKeyError
-from opaque_keys.edx.keys import CourseKey
-
-
-log = logging.getLogger(__name__)
-
-
-def opt_out_email_updates(request, token, course_id):
- """
- A view that let users opt out of any email updates.
-
- This meant is meant to be the target of an opt-out link or button.
- The `token` parameter must decrypt to a valid username.
- The `course_id` is the string course key of any course.
-
- Raises a 404 if there are any errors parsing the input.
- """
- try:
- username = UsernameCipher().decrypt(token.encode())
- user = User.objects.get(username=username)
- course_key = CourseKey.from_string(course_id)
- course = get_course_by_id(course_key, depth=0)
- except UnicodeDecodeError:
- raise Http404("base64url")
- except UsernameDecryptionException as exn:
- raise Http404(text_type(exn))
- except User.DoesNotExist:
- raise Http404("username")
- except InvalidKeyError:
- raise Http404("course")
-
- context = {
- 'course': course,
- 'cancelled': False,
- 'confirmed': False,
- }
-
- 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
-
- return render_to_response('bulk_email/unsubscribe.html', context)
diff --git a/lms/templates/bulk_email/unsubscribe.html b/lms/templates/bulk_email/unsubscribe.html
deleted file mode 100644
index 270f8dc604..0000000000
--- a/lms/templates/bulk_email/unsubscribe.html
+++ /dev/null
@@ -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>
-
-
-
-
- <%block name="pageheader">${header()}%block>
-
-
- <%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
- )}
-
-
- %endif
- %block>
-
-
-
diff --git a/lms/urls.py b/lms/urls.py
index 5f4c4cd777..ba2be98a95 100644
--- a/lms/urls.py
+++ b/lms/urls.py
@@ -746,10 +746,6 @@ if settings.FEATURES.get('ENABLE_DISCUSSION_SERVICE'):
),
]
-urlpatterns += [
- url(r'^bulk_email/', include('bulk_email.urls')),
-]
-
urlpatterns += [
url(
r'^courses/{}/tab/(?P[^/]+)/$'.format(
diff --git a/openedx/core/djangoapps/ace_common/templates/ace_common/edx_ace/common/base_body.html b/openedx/core/djangoapps/ace_common/templates/ace_common/edx_ace/common/base_body.html
index 918c321405..2c5d3efc24 100644
--- a/openedx/core/djangoapps/ace_common/templates/ace_common/edx_ace/common/base_body.html
+++ b/openedx/core/djangoapps/ace_common/templates/ace_common/edx_ace/common/base_body.html
@@ -175,13 +175,6 @@
{{ contact_mailing_address }}
- {% if unsubscribe_url %}
-
- |
- {% trans "Unsubscribe from these emails." %}
- |
-
- {% endif %}
diff --git a/openedx/core/djangoapps/schedules/config.py b/openedx/core/djangoapps/schedules/config.py
index 1e831175e8..5090e9f7c5 100644
--- a/openedx/core/djangoapps/schedules/config.py
+++ b/openedx/core/djangoapps/schedules/config.py
@@ -1,14 +1,10 @@
"""
-Contains waffle flags and switches for use with the Schedules app.
+Contains configuration for schedules app
"""
-from openedx.core.djangoapps.waffle_utils import (
- WaffleFlagNamespace, CourseWaffleFlag, WaffleFlag,
- WaffleSwitch, WaffleSwitchNamespace,
-)
+from openedx.core.djangoapps.waffle_utils import WaffleFlagNamespace, CourseWaffleFlag, WaffleFlag
WAFFLE_FLAG_NAMESPACE = WaffleFlagNamespace(name=u'schedules')
-WAFFLE_SWITCH_NAMESPACE = WaffleSwitchNamespace(name=u'schedules')
CREATE_SCHEDULE_WAFFLE_FLAG = CourseWaffleFlag(
waffle_namespace=WAFFLE_FLAG_NAMESPACE,
@@ -23,5 +19,3 @@ COURSE_UPDATE_WAFFLE_FLAG = CourseWaffleFlag(
)
DEBUG_MESSAGE_WAFFLE_FLAG = WaffleFlag(WAFFLE_FLAG_NAMESPACE, u'enable_debugging')
-
-COURSE_UPDATE_SHOW_UNSUBSCRIBE_WAFFLE_SWITCH = WaffleSwitch(WAFFLE_SWITCH_NAMESPACE, u'course_update_show_unsubscribe')
diff --git a/openedx/core/djangoapps/schedules/resolvers.py b/openedx/core/djangoapps/schedules/resolvers.py
index 56af9cf388..1fad58c654 100644
--- a/openedx/core/djangoapps/schedules/resolvers.py
+++ b/openedx/core/djangoapps/schedules/resolvers.py
@@ -13,8 +13,6 @@ from edx_ace.recipient import Recipient
from edx_django_utils.monitoring import function_trace, set_custom_metric
from courseware.date_summary import verified_upgrade_deadline_link, verified_upgrade_link_is_valid
-from lms.djangoapps.notification_prefs.views import UsernameCipher
-from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_SHOW_UNSUBSCRIBE_WAFFLE_SWITCH
from openedx.core.djangoapps.schedules.content_highlights import get_week_highlights
from openedx.core.djangoapps.schedules.exceptions import CourseUpdateDoesNotExist
from openedx.core.djangoapps.schedules.models import Schedule, ScheduleExperience
@@ -360,14 +358,6 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver):
)
# continue to the next schedule, don't yield an email for this one
else:
- unsubscribe_url = None
- if (COURSE_UPDATE_SHOW_UNSUBSCRIBE_WAFFLE_SWITCH.is_enabled() and
- 'bulk_email_optout' in settings.ACE_ENABLED_POLICIES):
- unsubscribe_url = reverse('bulk_email_opt_out', kwargs={
- 'token': UsernameCipher.encrypt(user.username),
- 'course_id': str(enrollment.course_id),
- })
-
template_context.update({
'course_name': schedule.enrollment.course.display_name,
'course_url': _get_trackable_course_home_url(enrollment.course_id),
@@ -377,7 +367,6 @@ class CourseUpdateResolver(BinnedSchedulesBaseResolver):
# This is used by the bulk email optout policy
'course_ids': [str(enrollment.course_id)],
- 'unsubscribe_url': unsubscribe_url,
})
template_context.update(_get_upsell_information_for_schedule(user, schedule))
diff --git a/openedx/core/djangoapps/schedules/tests/test_resolvers.py b/openedx/core/djangoapps/schedules/tests/test_resolvers.py
index 3411f16797..6cca8dfd71 100644
--- a/openedx/core/djangoapps/schedules/tests/test_resolvers.py
+++ b/openedx/core/djangoapps/schedules/tests/test_resolvers.py
@@ -1,51 +1,27 @@
-"""
-Tests for the Schedules app resolvers.
-"""
import datetime
from unittest import skipUnless
import ddt
from django.conf import settings
-from freezegun import freeze_time
-from mock import Mock, patch
-from waffle.testutils import override_switch
+from mock import Mock
-from openedx.core.djangoapps.schedules.config import COURSE_UPDATE_WAFFLE_FLAG
-from openedx.core.djangoapps.schedules.resolvers import (
- BinnedSchedulesBaseResolver,
- CourseUpdateResolver,
-)
+from openedx.core.djangoapps.schedules.resolvers import BinnedSchedulesBaseResolver
from openedx.core.djangoapps.schedules.tests.factories import ScheduleConfigFactory
from openedx.core.djangoapps.site_configuration.tests.factories import SiteFactory, SiteConfigurationFactory
-from openedx.core.djangoapps.waffle_utils.testutils import override_waffle_flag
from openedx.core.djangolib.testing.utils import CacheIsolationTestCase, skip_unless_lms
-from student.tests.factories import CourseEnrollmentFactory
-from xmodule.modulestore.tests.django_utils import ModuleStoreTestCase
-from xmodule.modulestore.tests.factories import CourseFactory, ItemFactory
-
-
-class SchedulesResolverTestMixin(CacheIsolationTestCase):
- """
- Base class for the resolver tests.
- """
- def setUp(self):
- super(SchedulesResolverTestMixin, self).setUp()
- self.site = SiteFactory.create()
- self.site_config = SiteConfigurationFactory(site=self.site)
- self.schedule_config = ScheduleConfigFactory.create(site=self.site)
@ddt.ddt
@skip_unless_lms
@skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS,
"Can't test schedules if the app isn't installed")
-class TestBinnedSchedulesBaseResolver(SchedulesResolverTestMixin, CacheIsolationTestCase):
- """
- Tests the BinnedSchedulesBaseResolver.
- """
+class TestBinnedSchedulesBaseResolver(CacheIsolationTestCase):
def setUp(self):
super(TestBinnedSchedulesBaseResolver, self).setUp()
+ self.site = SiteFactory.create()
+ self.site_config = SiteConfigurationFactory(site=self.site)
+ self.schedule_config = ScheduleConfigFactory.create(site=self.site)
self.resolver = BinnedSchedulesBaseResolver(
async_send_task=Mock(name='async_send_task'),
site=self.site,
@@ -91,64 +67,3 @@ class TestBinnedSchedulesBaseResolver(SchedulesResolverTestMixin, CacheIsolation
result = self.resolver.filter_by_org(mock_query)
mock_query.exclude.assert_called_once_with(enrollment__course__org__in=expected_org_list)
self.assertEqual(result, mock_query.exclude.return_value)
-
-
-@ddt.ddt
-@skip_unless_lms
-@skipUnless('openedx.core.djangoapps.schedules.apps.SchedulesConfig' in settings.INSTALLED_APPS,
- "Can't test schedules if the app isn't installed")
-@override_waffle_flag(COURSE_UPDATE_WAFFLE_FLAG, True)
-@freeze_time('2017-08-01 01:00:00', tz_offset=0, tick=False)
-class TestCourseUpdateResolver(SchedulesResolverTestMixin, CacheIsolationTestCase, ModuleStoreTestCase):
- """
- Tests the CourseUpdateResolver.
- """
- def setUp(self):
- super(TestCourseUpdateResolver, self).setUp()
- self.course = CourseFactory(highlights_enabled_for_messaging=True, self_paced=True)
- with self.store.bulk_operations(self.course.id):
- ItemFactory.create(parent=self.course, category='chapter', highlights=[u'good stuff'])
-
- def create_resolver(self):
- """
- Creates a CourseUpdateResolver with an enrollment to schedule.
- """
- with patch('openedx.core.djangoapps.schedules.signals.get_current_site') as mock_get_current_site:
- mock_get_current_site.return_value = self.site_config.site
- enrollment = CourseEnrollmentFactory(course_id=self.course.id, user=self.user, mode=u'audit')
-
- return CourseUpdateResolver(
- async_send_task=Mock(name='async_send_task'),
- site=self.site_config.site,
- target_datetime=enrollment.schedule.start,
- day_offset=-7,
- bin_num=1,
- )
-
- def test_schedule_context(self):
- resolver = self.create_resolver()
- schedules = list(resolver.schedules_for_bin())
- expected_context = {
- 'course_name': self.course.display_name,
- 'course_url': '/courses/{}/course/'.format(self.course.id),
- 'week_num': 1,
- 'week_highlights': ['good stuff'],
- 'course_ids': [str(self.course.id)],
- 'platform_name': u'\xe9dX',
- 'mobile_store_urls': {'google': '#', 'apple': '#'},
- 'homepage_url': '/',
- 'template_revision': 'unknown',
- 'contact_email': 'info@example.com',
- 'social_media_urls': {},
- 'dashboard_url': '/dashboard',
- 'contact_mailing_address': '',
- 'show_upsell': False,
- 'unsubscribe_url': None,
- }
- self.assertEqual(schedules, [(self.user, None, expected_context)])
-
- @override_switch('schedules.course_update_show_unsubscribe', True)
- def test_schedule_context_show_unsubscribe(self):
- resolver = self.create_resolver()
- schedules = list(resolver.schedules_for_bin())
- self.assertIn('optout', schedules[0][2]['unsubscribe_url'])