Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
E
edx-platform
Overview
Overview
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
edx
edx-platform
Commits
d9f98b84
Commit
d9f98b84
authored
Jan 23, 2014
by
Jason Bau
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #2226 from edx/jbau/resubscribe-notifer
resubscribe feature for notifier
parents
5bacfcc3
dfacbbdd
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
84 additions
and
21 deletions
+84
-21
lms/djangoapps/notification_prefs/tests.py
+29
-11
lms/djangoapps/notification_prefs/views.py
+14
-5
lms/templates/resubscribe.html
+24
-0
lms/templates/unsubscribe.html
+13
-4
lms/urls.py
+4
-1
No files found.
lms/djangoapps/notification_prefs/tests.py
View file @
d9f98b84
...
@@ -9,13 +9,14 @@ from django.test.utils import override_settings
...
@@ -9,13 +9,14 @@ from django.test.utils import override_settings
from
mock
import
Mock
,
patch
from
mock
import
Mock
,
patch
from
notification_prefs
import
NOTIFICATION_PREF_KEY
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
,
set_subscription
,
UsernameCipher
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.
...
@@ -48,10 +51,12 @@ class NotificationPrefViewTest(TestCase):
...
@@ -48,10 +51,12 @@ class NotificationPrefViewTest(TestCase):
def
assertPrefValid
(
self
,
user
):
def
assertPrefValid
(
self
,
user
):
"""Ensure that the correct preference for the user is persisted"""
"""Ensure that the correct preference for the user is persisted"""
self
.
assertEqual
(
pref
=
UserPreference
.
objects
.
get
(
user
=
user
,
key
=
NOTIFICATION_PREF_KEY
)
UserPreference
.
objects
.
get
(
user
=
user
,
key
=
NOTIFICATION_PREF_KEY
)
.
value
,
self
.
assertTrue
(
pref
)
# check exists and only 1 (.get)
self
.
tokens
[
user
]
# now coerce username to utf-8 encoded str, since we test with non-ascii unicdoe above and
)
# the unittest framework has hard time coercing to unicode.
# decrypt also can't take a unicode input, so coerce its input to str
self
.
assertEqual
(
str
(
user
.
username
.
encode
(
'utf-8'
)),
UsernameCipher
()
.
decrypt
(
str
(
pref
.
value
)))
def
assertNotPrefExists
(
self
,
user
):
def
assertNotPrefExists
(
self
,
user
):
"""Ensure that the user does not have a persisted preference"""
"""Ensure that the user does not have a persisted preference"""
...
@@ -174,13 +179,13 @@ class NotificationPrefViewTest(TestCase):
...
@@ -174,13 +179,13 @@ class NotificationPrefViewTest(TestCase):
def
test_unsubscribe_post
(
self
):
def
test_unsubscribe_post
(
self
):
request
=
self
.
request_factory
.
post
(
"dummy"
)
request
=
self
.
request_factory
.
post
(
"dummy"
)
response
=
unsubscribe
(
request
,
"dummy"
)
response
=
set_subscription
(
request
,
"dummy"
,
subscribe
=
False
)
self
.
assertEqual
(
response
.
status_code
,
405
)
self
.
assertEqual
(
response
.
status_code
,
405
)
def
test_unsubscribe_invalid_token
(
self
):
def
test_unsubscribe_invalid_token
(
self
):
def
test_invalid_token
(
token
,
message
):
def
test_invalid_token
(
token
,
message
):
request
=
self
.
request_factory
.
get
(
"dummy"
)
request
=
self
.
request_factory
.
get
(
"dummy"
)
self
.
assertRaisesRegexp
(
Http404
,
"^{}$"
.
format
(
message
),
unsubscribe
,
request
,
token
)
self
.
assertRaisesRegexp
(
Http404
,
"^{}$"
.
format
(
message
),
set_subscription
,
request
,
token
,
False
)
# Invalid base64 encoding
# Invalid base64 encoding
test_invalid_token
(
"ZOMG INVALID BASE64 CHARS!!!"
,
"base64url"
)
test_invalid_token
(
"ZOMG INVALID BASE64 CHARS!!!"
,
"base64url"
)
...
@@ -215,7 +220,7 @@ class NotificationPrefViewTest(TestCase):
...
@@ -215,7 +220,7 @@ class NotificationPrefViewTest(TestCase):
def
test_user
(
user
):
def
test_user
(
user
):
request
=
self
.
request_factory
.
get
(
"dummy"
)
request
=
self
.
request_factory
.
get
(
"dummy"
)
request
.
user
=
AnonymousUser
()
request
.
user
=
AnonymousUser
()
response
=
unsubscribe
(
request
,
self
.
tokens
[
user
]
)
response
=
set_subscription
(
request
,
self
.
tokens
[
user
],
subscribe
=
False
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertNotPrefExists
(
user
)
self
.
assertNotPrefExists
(
user
)
...
@@ -226,7 +231,20 @@ class NotificationPrefViewTest(TestCase):
...
@@ -226,7 +231,20 @@ class NotificationPrefViewTest(TestCase):
self
.
create_prefs
()
self
.
create_prefs
()
request
=
self
.
request_factory
.
get
(
"dummy"
)
request
=
self
.
request_factory
.
get
(
"dummy"
)
request
.
user
=
AnonymousUser
()
request
.
user
=
AnonymousUser
()
unsubscribe
(
request
,
self
.
tokens
[
self
.
user
]
)
set_subscription
(
request
,
self
.
tokens
[
self
.
user
],
False
)
response
=
unsubscribe
(
request
,
self
.
tokens
[
self
.
user
]
)
response
=
set_subscription
(
request
,
self
.
tokens
[
self
.
user
],
subscribe
=
False
)
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
=
set_subscription
(
request
,
self
.
tokens
[
user
],
subscribe
=
True
)
self
.
assertEqual
(
response
.
status_code
,
200
)
self
.
assertPrefValid
(
user
)
for
user
in
self
.
tokens
.
keys
():
test_user
(
user
)
lms/djangoapps/notification_prefs/views.py
View file @
d9f98b84
...
@@ -153,12 +153,14 @@ def ajax_status(request):
...
@@ -153,12 +153,14 @@ def ajax_status(request):
@require_GET
@require_GET
def
unsubscribe
(
request
,
token
):
def
set_subscription
(
request
,
token
,
subscribe
):
# 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 subscribe flag feature controls whether the view subscribes or unsubscribes the user, with subscribe=True
used to "undo" accidentally clicking on the unsubscribe link
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 +176,13 @@ def unsubscribe(request, token):
...
@@ -174,6 +176,13 @@ def unsubscribe(request, token):
except
User
.
DoesNotExist
:
except
User
.
DoesNotExist
:
raise
Http404
(
"username"
)
raise
Http404
(
"username"
)
UserPreference
.
objects
.
filter
(
user
=
user
,
key
=
NOTIFICATION_PREF_KEY
)
.
delete
()
if
subscribe
:
UserPreference
.
objects
.
get_or_create
(
user
=
user
,
return
render_to_response
(
"unsubscribe.html"
,
{})
key
=
NOTIFICATION_PREF_KEY
,
defaults
=
{
"value"
:
UsernameCipher
.
encrypt
(
user
.
username
)
})
return
render_to_response
(
"resubscribe.html"
,
{
'token'
:
token
})
else
:
UserPreference
.
objects
.
filter
(
user
=
user
,
key
=
NOTIFICATION_PREF_KEY
)
.
delete
()
return
render_to_response
(
"unsubscribe.html"
,
{
'token'
:
token
})
lms/templates/resubscribe.html
0 → 100644
View file @
d9f98b84
<
%!
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>
lms/templates/unsubscribe.html
View file @
d9f98b84
<
%!
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>
lms/urls.py
View file @
d9f98b84
...
@@ -338,7 +338,10 @@ if settings.COURSEWARE_ENABLED:
...
@@ -338,7 +338,10 @@ if settings.COURSEWARE_ENABLED:
url
(
r'^notification_prefs/enable/'
,
'notification_prefs.views.ajax_enable'
),
url
(
r'^notification_prefs/enable/'
,
'notification_prefs.views.ajax_enable'
),
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.set_subscription'
,
{
'subscribe'
:
False
},
name
=
"unsubscribe_forum_update"
),
url
(
r'^notification_prefs/resubscribe/(?P<token>[a-zA-Z0-9-_=]+)/'
,
'notification_prefs.views.set_subscription'
,
{
'subscribe'
:
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.
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment