Skip to content

Commit

Permalink
Hooking the logistration page into the new HTTP endpoint for email op…
Browse files Browse the repository at this point in the history
…t in.

Update the PR based on Code Review comments, and additional tests.

Using underscore js
  • Loading branch information
stephensanchez committed Dec 1, 2014
1 parent eacd525 commit 32c9230
Show file tree
Hide file tree
Showing 9 changed files with 197 additions and 3 deletions.
58 changes: 58 additions & 0 deletions common/djangoapps/user_api/tests/test_views.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,12 @@
import ddt
from pytz import UTC
import mock
from xmodule.modulestore.tests.factories import CourseFactory

from user_api.api import account as account_api, profile as profile_api

from student.tests.factories import UserFactory
from user_api.models import UserOrgTag
from user_api.tests.factories import UserPreferenceFactory
from django_comment_common import models
from opaque_keys.edx.locations import SlashSeparatedCourseKey
Expand Down Expand Up @@ -1468,3 +1470,59 @@ def _assert_reg_field(self, extra_fields_setting, expected_field):
# Verify that the form description matches what we'd expect
form_desc = json.loads(response.content)
self.assertIn(expected_field, form_desc["fields"])


@ddt.ddt
class UpdateEmailOptInTestCase(ApiTestCase):
"""Tests the UpdateEmailOptInPreference view. """

USERNAME = "steve"
EMAIL = "[email protected]"
PASSWORD = "steveopolis"

def setUp(self):
""" Create a course and user, then log in. """
super(UpdateEmailOptInTestCase, self).setUp()
self.course = CourseFactory.create()
self.user = UserFactory.create(username=self.USERNAME, email=self.EMAIL, password=self.PASSWORD)
self.client.login(username=self.USERNAME, password=self.PASSWORD)
self.url = reverse("preferences_email_opt_in")

@ddt.data(
(u"True", u"True"),
(u"true", u"True"),
(u"TrUe", u"True"),
(u"Banana", u"False"),
(u"strawberries", u"False"),
(u"False", u"False"),
)
@ddt.unpack
def test_update_email_opt_in(self, opt, result):
"""Tests the email opt in preference"""
# Register, which should trigger an activation email
response = self.client.post(self.url, {
"course_id": unicode(self.course.id),
"email_opt_in": opt
})
self.assertHttpOK(response)
preference = UserOrgTag.objects.get(
user=self.user, org=self.course.id.org, key="email-optin"
)
self.assertEquals(preference.value, result)

@ddt.data(
(True, False),
(False, True),
(False, False)
)
@ddt.unpack
def test_update_email_opt_in_wrong_params(self, use_course_id, use_opt_in):
"""Tests the email opt in preference"""
params = {}
if use_course_id:
params["course_id"] = unicode(self.course.id)
if use_opt_in:
params["email_opt_in"] = u"True"

response = self.client.post(self.url, params)
self.assertHttpBadRequest(response)
6 changes: 6 additions & 0 deletions common/djangoapps/user_api/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,12 @@
r'^v1/forum_roles/(?P<name>[a-zA-Z]+)/users/$',
user_api_views.ForumRoleUsersListView.as_view()
),

url(
r'^v1/preferences/email_opt_in/$',
user_api_views.UpdateEmailOptInPreference.as_view(),
name="preferences_email_opt_in"
),
)

if settings.FEATURES.get('ENABLE_COMBINED_LOGIN_REGISTRATION'):
Expand Down
31 changes: 31 additions & 0 deletions common/djangoapps/user_api/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,12 @@
from django.utils.translation import ugettext as _
from django.utils.decorators import method_decorator
from django.views.decorators.csrf import ensure_csrf_cookie, csrf_protect
from opaque_keys.edx import locator
from rest_framework import authentication
from rest_framework import filters
from rest_framework import generics
from rest_framework import permissions
from rest_framework import status
from rest_framework import viewsets
from rest_framework.views import APIView
from rest_framework.exceptions import ParseError
Expand Down Expand Up @@ -836,3 +838,32 @@ class PreferenceUsersListView(generics.ListAPIView):

def get_queryset(self):
return User.objects.filter(preferences__key=self.kwargs["pref_key"]).prefetch_related("preferences")


class UpdateEmailOptInPreference(APIView):
"""View for updating the email opt in preference. """
authentication_classes = (authentication.SessionAuthentication,)

@method_decorator(require_post_params(["course_id", "email_opt_in"]))
@method_decorator(ensure_csrf_cookie)
def post(self, request):
""" Post function for updating the email opt in preference.
Allows the modification or creation of the email opt in preference at an
organizational level.
Args:
request (Request): The request should contain the following POST parameters:
* course_id: The slash separated course ID. Used to determine the organization
for this preference setting.
* email_opt_in: "True" or "False" to determine if the user is opting in for emails from
this organization. If the string does not match "True" (case insensitive) it will
assume False.
"""
course_id = request.DATA['course_id']
org = locator.CourseLocator.from_string(course_id).org
# Only check for true. All other values are False.
email_opt_in = request.DATA['email_opt_in'].lower() == 'true'
profile_api.update_email_opt_in(request.user, org, email_opt_in)
return HttpResponse(status=status.HTTP_200_OK)
1 change: 1 addition & 0 deletions lms/envs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -1046,6 +1046,7 @@
'js/utils/edx.utils.validate.js',
'js/src/utility.js',
'js/student_account/enrollment.js',
'js/student_account/emailoptin.js',
'js/student_account/shoppingcart.js',
'js/student_account/models/LoginModel.js',
'js/student_account/models/RegisterModel.js',
Expand Down
6 changes: 6 additions & 0 deletions lms/static/js/spec/main.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,6 +293,10 @@
exports: 'edx.student.account.EnrollmentInterface',
deps: ['jquery', 'jquery.cookie']
},
'js/student_account/emailoptin': {
exports: 'edx.student.account.EmailOptInInterface',
deps: ['jquery', 'jquery.cookie']
},
'js/student_account/shoppingcart': {
exports: 'edx.student.account.ShoppingCartInterface',
deps: ['jquery', 'jquery.cookie', 'underscore']
Expand Down Expand Up @@ -362,6 +366,7 @@
'js/student_account/models/PasswordResetModel',
'js/student_account/models/RegisterModel',
'js/student_account/views/FormView',
'js/student_account/emailoptin',
'js/student_account/enrollment',
'js/student_account/shoppingcart',
]
Expand All @@ -383,6 +388,7 @@
'lms/include/js/spec/student_account/register_spec.js',
'lms/include/js/spec/student_account/password_reset_spec.js',
'lms/include/js/spec/student_account/enrollment_spec.js',
'lms/include/js/spec/student_account/emailoptin_spec.js',
'lms/include/js/spec/student_account/shoppingcart_spec.js',
'lms/include/js/spec/student_profile/profile_spec.js'
]);
Expand Down
3 changes: 2 additions & 1 deletion lms/static/js/spec/student_account/access_spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ define([
'js/student_account/views/AccessView',
'js/student_account/views/FormView',
'js/student_account/enrollment',
'js/student_account/shoppingcart'
'js/student_account/shoppingcart',
'js/student_account/emailoptin'
], function($, TemplateHelpers, AjaxHelpers, AccessView, FormView, EnrollmentInterface, ShoppingCartInterface) {
describe('edx.student.account.AccessView', function() {
'use strict';
Expand Down
28 changes: 28 additions & 0 deletions lms/static/js/spec/student_account/emailoptin_spec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
define(['js/common_helpers/ajax_helpers', 'js/student_account/emailoptin'],
function( AjaxHelpers, EmailOptInInterface ) {
'use strict';

describe( 'edx.student.account.EmailOptInInterface', function() {

var COURSE_KEY = 'edX/DemoX/Fall',
EMAIL_OPT_IN = 'True',
EMAIL_OPT_IN_URL = '/user_api/v1/preferences/email_opt_in/';

it('Opts in for organization emails', function() {
// Spy on Ajax requests
var requests = AjaxHelpers.requests( this );

// Attempt to enroll the user
EmailOptInInterface.setPreference( COURSE_KEY, EMAIL_OPT_IN );

// Expect that the correct request was made to the server
AjaxHelpers.expectRequest(
requests, 'POST', EMAIL_OPT_IN_URL, 'course_id=edX%2FDemoX%2FFall&email_opt_in=True'
);

// Simulate a successful response from the server
AjaxHelpers.respondWithJson(requests, {});
});
});
}
);
35 changes: 35 additions & 0 deletions lms/static/js/student_account/emailoptin.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
var edx = edx || {};

(function($) {
'use strict';

edx.student = edx.student || {};
edx.student.account = edx.student.account || {};

edx.student.account.EmailOptInInterface = {

urls: {
emailOptInUrl: '/user_api/v1/preferences/email_opt_in/'
},

headers: {
'X-CSRFToken': $.cookie('csrftoken')
},

/**
* Set the email opt in setting for the organization associated
* with this course.
* @param {string} courseKey Slash-separated course key.
* @param {string} emailOptIn The preference to opt in or out of organization emails.
*/
setPreference: function( courseKey, emailOptIn, context ) {
return $.ajax({
url: this.urls.emailOptInUrl,
type: 'POST',
data: {course_id: courseKey, email_opt_in: emailOptIn},
headers: this.headers,
context: context
});
}
};
})(jQuery);
32 changes: 30 additions & 2 deletions lms/static/js/student_account/views/AccessView.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,33 @@ var edx = edx || {};
* Once authentication has completed successfully, a user may need to:
*
* - Enroll in a course.
* - Update email opt-in preferences
*
* These actions are delegated from the authComplete function to additional
* functions requiring authentication.
*
*/
authComplete: function() {
var emailOptIn = edx.student.account.EmailOptInInterface,
queryParams = this.queryParams();

// Set the email opt in preference.
if (!_.isUndefined(queryParams.emailOptIn) && queryParams.enrollmentAction) {
emailOptIn.setPreference(
decodeURIComponent(queryParams.courseId),
queryParams.emailOptIn,
this
).always(this.enrollment);
} else {
this.enrollment();
}
},

/**
* Designed to be invoked after authentication has completed. This function enrolls
* the student as requested.
*
* - Enroll in a course.
* - Add a course to the shopping cart.
* - Be redirected to the dashboard / track selection page / shopping cart.
*
Expand All @@ -191,7 +218,7 @@ var edx = edx || {};
* ?course_id: The slash-separated course ID to enroll in or add to the cart.
*
*/
authComplete: function() {
enrollment: function() {
var enrollment = edx.student.account.EnrollmentInterface,
shoppingcart = edx.student.account.ShoppingCartInterface,
redirectUrl = '/dashboard',
Expand Down Expand Up @@ -248,7 +275,8 @@ var edx = edx || {};
return {
next: $.url( '?next' ),
enrollmentAction: $.url( '?enrollment_action' ),
courseId: $.url( '?course_id' )
courseId: $.url( '?course_id' ),
emailOptIn: $.url( '?email_opt_in')
};
},

Expand Down

0 comments on commit 32c9230

Please sign in to comment.