Commit 8abfdc63 by Jason Bau

add undo (resub) to landing page of forum unsub link

parent 7f742978
...@@ -12,10 +12,11 @@ from notification_prefs import NOTIFICATION_PREF_KEY ...@@ -12,10 +12,11 @@ from notification_prefs import NOTIFICATION_PREF_KEY
from notification_prefs.views import ajax_enable, ajax_disable, ajax_status, unsubscribe from notification_prefs.views import ajax_enable, ajax_disable, ajax_status, unsubscribe
from student.tests.factories import UserFactory from student.tests.factories import UserFactory
from user_api.models import UserPreference from user_api.models import UserPreference
from util.testing import UrlResetMixin
@override_settings(SECRET_KEY="test secret key") @override_settings(SECRET_KEY="test secret key")
class NotificationPrefViewTest(TestCase): class NotificationPrefViewTest(UrlResetMixin, TestCase):
INITIALIZATION_VECTOR = "\x00" * 16 INITIALIZATION_VECTOR = "\x00" * 16
@classmethod @classmethod
...@@ -23,7 +24,9 @@ class NotificationPrefViewTest(TestCase): ...@@ -23,7 +24,9 @@ class NotificationPrefViewTest(TestCase):
# Make sure global state is set up appropriately # Make sure global state is set up appropriately
Client().get("/") Client().get("/")
@patch.dict("django.conf.settings.FEATURES", {"ENABLE_DISCUSSION_SERVICE": True})
def setUp(self): def setUp(self):
super(NotificationPrefViewTest, self).setUp()
self.user = UserFactory.create(username="testuser") self.user = UserFactory.create(username="testuser")
# Tokens are intentionally hard-coded instead of computed to help us # Tokens are intentionally hard-coded instead of computed to help us
# avoid breaking existing links. # avoid breaking existing links.
...@@ -230,3 +233,17 @@ class NotificationPrefViewTest(TestCase): ...@@ -230,3 +233,17 @@ class NotificationPrefViewTest(TestCase):
response = unsubscribe(request, self.tokens[self.user]) response = unsubscribe(request, self.tokens[self.user])
self.assertEqual(response.status_code, 200) self.assertEqual(response.status_code, 200)
self.assertNotPrefExists(self.user) self.assertNotPrefExists(self.user)
def test_resubscribe_success(self):
def test_user(user):
# start without a pref key
self.assertFalse(UserPreference.objects.filter(user=user, key=NOTIFICATION_PREF_KEY))
request = self.request_factory.get("dummy")
request.user = AnonymousUser()
response = unsubscribe(request, self.tokens[user], resubscribe=True)
self.assertEqual(response.status_code, 200)
# this new DB entry will have a new value, so can't use assertPrefValid. Just check existence
self.assertTrue(UserPreference.objects.filter(user=user, key=NOTIFICATION_PREF_KEY))
for user in self.tokens.keys():
test_user(user)
...@@ -153,12 +153,13 @@ def ajax_status(request): ...@@ -153,12 +153,13 @@ def ajax_status(request):
@require_GET @require_GET
def unsubscribe(request, token): def unsubscribe(request, token, resubscribe=False): # pylint: disable=unused-argument
""" """
A view that disables notifications for a user who may not be authenticated A view that disables or re-enables notifications for a user who may not be authenticated
This view is meant to be the target of an unsubscribe link. The request This view is meant to be the target of an unsubscribe link. The request
must be a GET, and the `token` parameter must decrypt to a valid username. must be a GET, and the `token` parameter must decrypt to a valid username.
The resubscribe feature allows "undo" of accidentally clicking on unsubscribe
A 405 will be returned if the request method is not GET. A 404 will be A 405 will be returned if the request method is not GET. A 404 will be
returned if the token parameter does not decrypt to a valid username. On returned if the token parameter does not decrypt to a valid username. On
...@@ -174,6 +175,13 @@ def unsubscribe(request, token): ...@@ -174,6 +175,13 @@ def unsubscribe(request, token):
except User.DoesNotExist: except User.DoesNotExist:
raise Http404("username") raise Http404("username")
if not resubscribe:
UserPreference.objects.filter(user=user, key=NOTIFICATION_PREF_KEY).delete() UserPreference.objects.filter(user=user, key=NOTIFICATION_PREF_KEY).delete()
return render_to_response("unsubscribe.html", {'token': token})
return render_to_response("unsubscribe.html", {}) else:
UserPreference.objects.get_or_create(user=user,
key=NOTIFICATION_PREF_KEY,
defaults={
"value": UsernameCipher.encrypt(user.username)
})
return render_to_response("resubscribe.html", {'token': token})
<%!
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.conf import settings
%>
<%inherit file="main.html" />
<%namespace name='static' file='static_content.html'/>
<section class="container unsubscribe">
<section class="message">
<h1>${_("Re-subscribe Successful!")}</h1>
<hr class="horizontal-divider">
<p>
${_("You have re-enabled forum notification emails from {platform_name}. "
"Click {dashboard_link_start}here{link_end} to return to your dashboard. ").format(
platform_name=settings.PLATFORM_NAME,
dashboard_link_start="<a href='{}'>".format(reverse('dashboard')),
link_end="</a>",)}
</p>
</section>
</section>
<%! from django.core.urlresolvers import reverse %> <%!
from django.core.urlresolvers import reverse
from django.utils.translation import ugettext as _
from django.conf import settings
%>
<%inherit file="main.html" /> <%inherit file="main.html" />
<%namespace name='static' file='static_content.html'/> <%namespace name='static' file='static_content.html'/>
...@@ -6,12 +10,17 @@ ...@@ -6,12 +10,17 @@
<section class="container unsubscribe"> <section class="container unsubscribe">
<section class="message"> <section class="message">
<h1>Unsubscribe Successful!</h1> <h1>${_("Unsubscribe Successful!")}</h1>
<hr class="horizontal-divider"> <hr class="horizontal-divider">
<p> <p>
You will no longer receive notification emails from edX. ${_("You will no longer receive forum notification emails from {platform_name}. "
Click <a href="${reverse('dashboard')}">here</a> to return to your dashboard. "Click {dashboard_link_start}here{link_end} to return to your dashboard. "
"If you did not mean to do this, click {undo_link_start}here{link_end} to re-subscribe.").format(
platform_name=settings.PLATFORM_NAME,
dashboard_link_start="<a href='{}'>".format(reverse('dashboard')),
undo_link_start="<a id='resub_link' href='{}'>".format(reverse('resubscribe_forum_update', args=[token])),
link_end="</a>",)}
</p> </p>
</section> </section>
</section> </section>
...@@ -339,6 +339,8 @@ if settings.COURSEWARE_ENABLED: ...@@ -339,6 +339,8 @@ if settings.COURSEWARE_ENABLED:
url(r'^notification_prefs/disable/', 'notification_prefs.views.ajax_disable'), url(r'^notification_prefs/disable/', 'notification_prefs.views.ajax_disable'),
url(r'^notification_prefs/status/', 'notification_prefs.views.ajax_status'), url(r'^notification_prefs/status/', 'notification_prefs.views.ajax_status'),
url(r'^notification_prefs/unsubscribe/(?P<token>[a-zA-Z0-9-_=]+)/', 'notification_prefs.views.unsubscribe'), url(r'^notification_prefs/unsubscribe/(?P<token>[a-zA-Z0-9-_=]+)/', 'notification_prefs.views.unsubscribe'),
url(r'^notification_prefs/resubscribe/(?P<token>[a-zA-Z0-9-_=]+)/',
'notification_prefs.views.unsubscribe', {'resubscribe': True}, name="resubscribe_forum_update"),
) )
urlpatterns += ( urlpatterns += (
# This MUST be the last view in the courseware--it's a catch-all for custom tabs. # This MUST be the last view in the courseware--it's a catch-all for custom tabs.
......
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