Include course ID in analytics events for logistration
This commit is contained in:
@@ -343,6 +343,20 @@ def shim_student_view(view_func, check_logged_in=False):
|
|||||||
if "course_id" in request.POST:
|
if "course_id" in request.POST:
|
||||||
del request.POST["course_id"]
|
del request.POST["course_id"]
|
||||||
|
|
||||||
|
# Include the course ID if it's specified in the analytics info
|
||||||
|
# so it can be included in analytics events.
|
||||||
|
if "analytics" in request.POST:
|
||||||
|
try:
|
||||||
|
analytics = json.loads(request.POST["analytics"])
|
||||||
|
if "enroll_course_id" in analytics:
|
||||||
|
request.POST["course_id"] = analytics.get("enroll_course_id")
|
||||||
|
except (ValueError, TypeError):
|
||||||
|
LOGGER.error(
|
||||||
|
u"Could not parse analytics object sent to user API: {analytics}".format(
|
||||||
|
analytics=analytics
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
# Backwards compatibility: the student view expects both
|
# Backwards compatibility: the student view expects both
|
||||||
# terms of service and honor code values. Since we're combining
|
# terms of service and honor code values. Since we're combining
|
||||||
# these into a single checkbox, the only value we may get
|
# these into a single checkbox, the only value we may get
|
||||||
|
|||||||
@@ -150,6 +150,17 @@ class StudentViewShimTest(TestCase):
|
|||||||
self.assertNotIn("enrollment_action", self.captured_request.POST)
|
self.assertNotIn("enrollment_action", self.captured_request.POST)
|
||||||
self.assertNotIn("course_id", self.captured_request.POST)
|
self.assertNotIn("course_id", self.captured_request.POST)
|
||||||
|
|
||||||
|
def test_include_analytics_info(self):
|
||||||
|
view = self._shimmed_view(HttpResponse())
|
||||||
|
request = HttpRequest()
|
||||||
|
request.POST["analytics"] = json.dumps({
|
||||||
|
"enroll_course_id": "edX/DemoX/Fall"
|
||||||
|
})
|
||||||
|
view(request)
|
||||||
|
|
||||||
|
# Expect that the analytics course ID was passed to the view
|
||||||
|
self.assertEqual(self.captured_request.POST.get("course_id"), "edX/DemoX/Fall")
|
||||||
|
|
||||||
def test_third_party_auth_login_failure(self):
|
def test_third_party_auth_login_failure(self):
|
||||||
view = self._shimmed_view(
|
view = self._shimmed_view(
|
||||||
HttpResponse(status=403),
|
HttpResponse(status=403),
|
||||||
|
|||||||
@@ -133,6 +133,13 @@ class LoginSessionView(APIView):
|
|||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""Log in a user.
|
"""Log in a user.
|
||||||
|
|
||||||
|
You must send all required form fields with the request.
|
||||||
|
|
||||||
|
You can optionally send an `analytics` param with a JSON-encoded
|
||||||
|
object with additional info to include in the login analytics event.
|
||||||
|
Currently, the only supported field is "enroll_course_id" to indicate
|
||||||
|
that the user logged in while enrolling in a particular course.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
request (HttpRequest)
|
request (HttpRequest)
|
||||||
|
|
||||||
@@ -148,7 +155,7 @@ class LoginSessionView(APIView):
|
|||||||
Example Usage:
|
Example Usage:
|
||||||
|
|
||||||
POST /user_api/v1/login_session
|
POST /user_api/v1/login_session
|
||||||
with POST params `email` and `password`
|
with POST params `email`, `password`, and `remember`.
|
||||||
|
|
||||||
200 OK
|
200 OK
|
||||||
|
|
||||||
@@ -246,6 +253,13 @@ class RegistrationView(APIView):
|
|||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""Create the user's account.
|
"""Create the user's account.
|
||||||
|
|
||||||
|
You must send all required form fields with the request.
|
||||||
|
|
||||||
|
You can optionally send an `analytics` param with a JSON-encoded
|
||||||
|
object with additional info to include in the registration analytics event.
|
||||||
|
Currently, the only supported field is "enroll_course_id" to indicate
|
||||||
|
that the user registered while enrolling in a particular course.
|
||||||
|
|
||||||
Arguments:
|
Arguments:
|
||||||
request (HTTPRequest)
|
request (HTTPRequest)
|
||||||
|
|
||||||
|
|||||||
@@ -6,208 +6,237 @@ define([
|
|||||||
'js/student_account/models/LoginModel',
|
'js/student_account/models/LoginModel',
|
||||||
'js/student_account/views/LoginView'
|
'js/student_account/views/LoginView'
|
||||||
], function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView) {
|
], function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView) {
|
||||||
describe('edx.student.account.LoginView', function() {
|
'use strict';
|
||||||
'use strict';
|
describe('edx.student.account.LoginView', function() {
|
||||||
|
|
||||||
var model = null,
|
var model = null,
|
||||||
view = null,
|
view = null,
|
||||||
requests = null,
|
requests = null,
|
||||||
authComplete = false,
|
authComplete = false,
|
||||||
PLATFORM_NAME = 'edX',
|
PLATFORM_NAME = 'edX',
|
||||||
USER_DATA = {
|
USER_DATA = {
|
||||||
email: 'xsy@edx.org',
|
email: 'xsy@edx.org',
|
||||||
password: 'xsyisawesome',
|
password: 'xsyisawesome',
|
||||||
remember: true
|
remember: true
|
||||||
},
|
},
|
||||||
THIRD_PARTY_AUTH = {
|
THIRD_PARTY_AUTH = {
|
||||||
currentProvider: null,
|
currentProvider: null,
|
||||||
providers: [
|
providers: [
|
||||||
{
|
{
|
||||||
name: 'Google',
|
name: 'Google',
|
||||||
iconClass: 'icon-google-plus',
|
iconClass: 'icon-google-plus',
|
||||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'Facebook',
|
name: 'Facebook',
|
||||||
iconClass: 'icon-facebook',
|
iconClass: 'icon-facebook',
|
||||||
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
|
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
|
||||||
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
|
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
FORM_DESCRIPTION = {
|
FORM_DESCRIPTION = {
|
||||||
method: 'post',
|
method: 'post',
|
||||||
submit_url: '/user_api/v1/account/login_session/',
|
submit_url: '/user_api/v1/account/login_session/',
|
||||||
fields: [
|
fields: [
|
||||||
{
|
{
|
||||||
name: 'email',
|
name: 'email',
|
||||||
label: 'Email',
|
label: 'Email',
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
type: 'email',
|
type: 'email',
|
||||||
required: true,
|
required: true,
|
||||||
placeholder: 'place@holder.org',
|
placeholder: 'place@holder.org',
|
||||||
instructions: 'Enter your email.',
|
instructions: 'Enter your email.',
|
||||||
restrictions: {}
|
restrictions: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'password',
|
name: 'password',
|
||||||
label: 'Password',
|
label: 'Password',
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
type: 'password',
|
type: 'password',
|
||||||
required: true,
|
required: true,
|
||||||
instructions: 'Enter your password.',
|
instructions: 'Enter your password.',
|
||||||
restrictions: {}
|
restrictions: {}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: 'remember',
|
name: 'remember',
|
||||||
label: 'Remember me',
|
label: 'Remember me',
|
||||||
defaultValue: '',
|
defaultValue: '',
|
||||||
type: 'checkbox',
|
type: 'checkbox',
|
||||||
required: true,
|
required: true,
|
||||||
instructions: "Agree to the terms of service.",
|
instructions: "Agree to the terms of service.",
|
||||||
restrictions: {}
|
restrictions: {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
};
|
},
|
||||||
|
COURSE_ID = "edX/demoX/Fall";
|
||||||
|
|
||||||
var createLoginView = function(test) {
|
var createLoginView = function(test) {
|
||||||
// Initialize the login model
|
// Initialize the login model
|
||||||
model = new LoginModel({}, {
|
model = new LoginModel({}, {
|
||||||
url: FORM_DESCRIPTION.submit_url,
|
url: FORM_DESCRIPTION.submit_url,
|
||||||
method: FORM_DESCRIPTION.method
|
method: FORM_DESCRIPTION.method
|
||||||
|
});
|
||||||
|
|
||||||
|
// Initialize the login view
|
||||||
|
view = new LoginView({
|
||||||
|
fields: FORM_DESCRIPTION.fields,
|
||||||
|
model: model,
|
||||||
|
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||||
|
platformName: PLATFORM_NAME
|
||||||
|
});
|
||||||
|
|
||||||
|
// Spy on AJAX requests
|
||||||
|
requests = AjaxHelpers.requests(test);
|
||||||
|
|
||||||
|
// Intercept events from the view
|
||||||
|
authComplete = false;
|
||||||
|
view.on("auth-complete", function() {
|
||||||
|
authComplete = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var submitForm = function(validationSuccess) {
|
||||||
|
// Simulate manual entry of login form data
|
||||||
|
$('#login-email').val(USER_DATA.email);
|
||||||
|
$('#login-password').val(USER_DATA.password);
|
||||||
|
|
||||||
|
// Check the "Remember me" checkbox
|
||||||
|
$('#login-remember').prop('checked', USER_DATA.remember);
|
||||||
|
|
||||||
|
// Create a fake click event
|
||||||
|
var clickEvent = $.Event('click');
|
||||||
|
|
||||||
|
// If validationSuccess isn't passed, we avoid
|
||||||
|
// spying on `view.validate` twice
|
||||||
|
if ( !_.isUndefined(validationSuccess) ) {
|
||||||
|
// Force validation to return as expected
|
||||||
|
spyOn(view, 'validate').andReturn({
|
||||||
|
isValid: validationSuccess,
|
||||||
|
message: 'Submission was validated.'
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize the login view
|
// Submit the email address
|
||||||
view = new LoginView({
|
view.submitForm(clickEvent);
|
||||||
fields: FORM_DESCRIPTION.fields,
|
};
|
||||||
model: model,
|
|
||||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
|
||||||
platformName: PLATFORM_NAME
|
|
||||||
});
|
|
||||||
|
|
||||||
// Spy on AJAX requests
|
beforeEach(function() {
|
||||||
requests = AjaxHelpers.requests(test);
|
setFixtures('<div id="login-form"></div>');
|
||||||
|
TemplateHelpers.installTemplate('templates/student_account/login');
|
||||||
// Intercept events from the view
|
TemplateHelpers.installTemplate('templates/student_account/form_field');
|
||||||
authComplete = false;
|
|
||||||
view.on("auth-complete", function() {
|
|
||||||
authComplete = true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var submitForm = function(validationSuccess) {
|
|
||||||
// Simulate manual entry of login form data
|
|
||||||
$('#login-email').val(USER_DATA.email);
|
|
||||||
$('#login-password').val(USER_DATA.password);
|
|
||||||
|
|
||||||
// Check the "Remember me" checkbox
|
|
||||||
$('#login-remember').prop('checked', USER_DATA.remember);
|
|
||||||
|
|
||||||
// Create a fake click event
|
|
||||||
var clickEvent = $.Event('click');
|
|
||||||
|
|
||||||
// If validationSuccess isn't passed, we avoid
|
|
||||||
// spying on `view.validate` twice
|
|
||||||
if ( !_.isUndefined(validationSuccess) ) {
|
|
||||||
// Force validation to return as expected
|
|
||||||
spyOn(view, 'validate').andReturn({
|
|
||||||
isValid: validationSuccess,
|
|
||||||
message: 'Submission was validated.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Submit the email address
|
|
||||||
view.submitForm(clickEvent);
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
setFixtures('<div id="login-form"></div>');
|
|
||||||
TemplateHelpers.installTemplate('templates/student_account/login');
|
|
||||||
TemplateHelpers.installTemplate('templates/student_account/form_field');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('logs the user in', function() {
|
|
||||||
createLoginView(this);
|
|
||||||
|
|
||||||
// Submit the form, with successful validation
|
|
||||||
submitForm(true);
|
|
||||||
|
|
||||||
// Verify that the client contacts the server with the expected data
|
|
||||||
AjaxHelpers.expectRequest(
|
|
||||||
requests, 'POST',
|
|
||||||
FORM_DESCRIPTION.submit_url,
|
|
||||||
$.param( USER_DATA )
|
|
||||||
);
|
|
||||||
|
|
||||||
// Respond with status code 200
|
|
||||||
AjaxHelpers.respondWithJson(requests, {});
|
|
||||||
|
|
||||||
// Verify that auth-complete is triggered
|
|
||||||
expect(authComplete).toBe(true);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('displays third-party auth login buttons', function() {
|
|
||||||
createLoginView(this);
|
|
||||||
|
|
||||||
// Verify that Google and Facebook registration buttons are displayed
|
|
||||||
expect($('.button-Google')).toBeVisible();
|
|
||||||
expect($('.button-Facebook')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('displays a link to the password reset form', function() {
|
|
||||||
createLoginView(this);
|
|
||||||
|
|
||||||
// Verify that the password reset link is displayed
|
|
||||||
expect($('.forgot-password')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('validates login form fields', function() {
|
|
||||||
createLoginView(this);
|
|
||||||
|
|
||||||
submitForm(true);
|
|
||||||
|
|
||||||
// Verify that validation of form fields occurred
|
|
||||||
expect(view.validate).toHaveBeenCalledWith($('#login-email')[0]);
|
|
||||||
expect(view.validate).toHaveBeenCalledWith($('#login-password')[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('displays login form validation errors', function() {
|
|
||||||
createLoginView(this);
|
|
||||||
|
|
||||||
// Submit the form, with failed validation
|
|
||||||
submitForm(false);
|
|
||||||
|
|
||||||
// Verify that submission errors are visible
|
|
||||||
expect(view.$errors).not.toHaveClass('hidden');
|
|
||||||
|
|
||||||
// Expect auth complete NOT to have been triggered
|
|
||||||
expect(authComplete).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('displays an error if the server returns an error while logging in', function() {
|
|
||||||
createLoginView(this);
|
|
||||||
|
|
||||||
// Submit the form, with successful validation
|
|
||||||
submitForm(true);
|
|
||||||
|
|
||||||
// Simulate an error from the LMS servers
|
|
||||||
AjaxHelpers.respondWithError(requests);
|
|
||||||
|
|
||||||
// Expect that an error is displayed and that auth complete is not triggered
|
|
||||||
expect(view.$errors).not.toHaveClass('hidden');
|
|
||||||
expect(authComplete).toBe(false);
|
|
||||||
|
|
||||||
// If we try again and succeed, the error should go away
|
|
||||||
submitForm();
|
|
||||||
|
|
||||||
// This time, respond with status code 200
|
|
||||||
AjaxHelpers.respondWithJson(requests, {});
|
|
||||||
|
|
||||||
// Expect that the error is hidden and auth complete is triggered
|
|
||||||
expect(view.$errors).toHaveClass('hidden');
|
|
||||||
expect(authComplete).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
);
|
it('logs the user in', function() {
|
||||||
|
createLoginView(this);
|
||||||
|
|
||||||
|
// Submit the form, with successful validation
|
||||||
|
submitForm(true);
|
||||||
|
|
||||||
|
// Verify that the client contacts the server with the expected data
|
||||||
|
AjaxHelpers.expectRequest(
|
||||||
|
requests, 'POST',
|
||||||
|
FORM_DESCRIPTION.submit_url,
|
||||||
|
$.param( USER_DATA )
|
||||||
|
);
|
||||||
|
|
||||||
|
// Respond with status code 200
|
||||||
|
AjaxHelpers.respondWithJson(requests, {});
|
||||||
|
|
||||||
|
// Verify that auth-complete is triggered
|
||||||
|
expect(authComplete).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sends analytics info containing the enrolled course ID', function() {
|
||||||
|
createLoginView( this );
|
||||||
|
|
||||||
|
// Simulate that the user is attempting to enroll in a course
|
||||||
|
// by setting the course_id query string param.
|
||||||
|
spyOn($, 'url').andCallFake(function( param ) {
|
||||||
|
if (param === "?course_id") {
|
||||||
|
return encodeURIComponent( COURSE_ID );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Attempt to login
|
||||||
|
submitForm( true );
|
||||||
|
|
||||||
|
// Verify that the client sent the course ID for analytics
|
||||||
|
var expectedData = {};
|
||||||
|
$.extend(expectedData, USER_DATA, {
|
||||||
|
analytics: JSON.stringify({
|
||||||
|
enroll_course_id: COURSE_ID
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
AjaxHelpers.expectRequest(
|
||||||
|
requests, 'POST',
|
||||||
|
FORM_DESCRIPTION.submit_url,
|
||||||
|
$.param( expectedData )
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays third-party auth login buttons', function() {
|
||||||
|
createLoginView(this);
|
||||||
|
|
||||||
|
// Verify that Google and Facebook registration buttons are displayed
|
||||||
|
expect($('.button-Google')).toBeVisible();
|
||||||
|
expect($('.button-Facebook')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays a link to the password reset form', function() {
|
||||||
|
createLoginView(this);
|
||||||
|
|
||||||
|
// Verify that the password reset link is displayed
|
||||||
|
expect($('.forgot-password')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('validates login form fields', function() {
|
||||||
|
createLoginView(this);
|
||||||
|
|
||||||
|
submitForm(true);
|
||||||
|
|
||||||
|
// Verify that validation of form fields occurred
|
||||||
|
expect(view.validate).toHaveBeenCalledWith($('#login-email')[0]);
|
||||||
|
expect(view.validate).toHaveBeenCalledWith($('#login-password')[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays login form validation errors', function() {
|
||||||
|
createLoginView(this);
|
||||||
|
|
||||||
|
// Submit the form, with failed validation
|
||||||
|
submitForm(false);
|
||||||
|
|
||||||
|
// Verify that submission errors are visible
|
||||||
|
expect(view.$errors).not.toHaveClass('hidden');
|
||||||
|
|
||||||
|
// Expect auth complete NOT to have been triggered
|
||||||
|
expect(authComplete).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays an error if the server returns an error while logging in', function() {
|
||||||
|
createLoginView(this);
|
||||||
|
|
||||||
|
// Submit the form, with successful validation
|
||||||
|
submitForm(true);
|
||||||
|
|
||||||
|
// Simulate an error from the LMS servers
|
||||||
|
AjaxHelpers.respondWithError(requests);
|
||||||
|
|
||||||
|
// Expect that an error is displayed and that auth complete is not triggered
|
||||||
|
expect(view.$errors).not.toHaveClass('hidden');
|
||||||
|
expect(authComplete).toBe(false);
|
||||||
|
|
||||||
|
// If we try again and succeed, the error should go away
|
||||||
|
submitForm();
|
||||||
|
|
||||||
|
// This time, respond with status code 200
|
||||||
|
AjaxHelpers.respondWithJson(requests, {});
|
||||||
|
|
||||||
|
// Expect that the error is hidden and auth complete is triggered
|
||||||
|
expect(view.$errors).toHaveClass('hidden');
|
||||||
|
expect(authComplete).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -6,302 +6,332 @@ define([
|
|||||||
'js/student_account/models/RegisterModel',
|
'js/student_account/models/RegisterModel',
|
||||||
'js/student_account/views/RegisterView'
|
'js/student_account/views/RegisterView'
|
||||||
], function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) {
|
], function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) {
|
||||||
describe('edx.student.account.RegisterView', function() {
|
'use strict';
|
||||||
'use strict';
|
|
||||||
|
|
||||||
var model = null,
|
describe('edx.student.account.RegisterView', function() {
|
||||||
view = null,
|
|
||||||
requests = null,
|
|
||||||
authComplete = false,
|
|
||||||
PLATFORM_NAME = 'edX',
|
|
||||||
USER_DATA = {
|
|
||||||
email: 'xsy@edx.org',
|
|
||||||
name: 'Xsy M. Education',
|
|
||||||
username: 'Xsy',
|
|
||||||
password: 'xsyisawesome',
|
|
||||||
level_of_education: 'p',
|
|
||||||
gender: 'm',
|
|
||||||
year_of_birth: 2014,
|
|
||||||
mailing_address: '141 Portland',
|
|
||||||
goals: 'To boldly learn what no letter of the alphabet has learned before',
|
|
||||||
honor_code: true
|
|
||||||
},
|
|
||||||
THIRD_PARTY_AUTH = {
|
|
||||||
currentProvider: null,
|
|
||||||
providers: [
|
|
||||||
{
|
|
||||||
name: 'Google',
|
|
||||||
iconClass: 'icon-google-plus',
|
|
||||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
|
||||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'Facebook',
|
|
||||||
iconClass: 'icon-facebook',
|
|
||||||
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
|
|
||||||
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
|
|
||||||
}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
FORM_DESCRIPTION = {
|
|
||||||
method: 'post',
|
|
||||||
submit_url: '/user_api/v1/account/registration/',
|
|
||||||
fields: [
|
|
||||||
{
|
|
||||||
name: 'email',
|
|
||||||
label: 'Email',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'email',
|
|
||||||
required: true,
|
|
||||||
placeholder: 'place@holder.org',
|
|
||||||
instructions: 'Enter your email.',
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'name',
|
|
||||||
label: 'Full Name',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'text',
|
|
||||||
required: true,
|
|
||||||
instructions: 'Enter your username.',
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'username',
|
|
||||||
label: 'Username',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'text',
|
|
||||||
required: true,
|
|
||||||
instructions: 'Enter your username.',
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'password',
|
|
||||||
label: 'Password',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'password',
|
|
||||||
required: true,
|
|
||||||
instructions: 'Enter your password.',
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'level_of_education',
|
|
||||||
label: 'Highest Level of Education Completed',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{value: "", name: "--"},
|
|
||||||
{value: "p", name: "Doctorate"},
|
|
||||||
{value: "m", name: "Master's or professional degree"},
|
|
||||||
{value: "b", name: "Bachelor's degree"},
|
|
||||||
],
|
|
||||||
required: false,
|
|
||||||
instructions: 'Select your education level.',
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'gender',
|
|
||||||
label: 'Gender',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{value: "", name: "--"},
|
|
||||||
{value: "m", name: "Male"},
|
|
||||||
{value: "f", name: "Female"},
|
|
||||||
{value: "o", name: "Other"},
|
|
||||||
],
|
|
||||||
required: false,
|
|
||||||
instructions: 'Select your gender.',
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'year_of_birth',
|
|
||||||
label: 'Year of Birth',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'select',
|
|
||||||
options: [
|
|
||||||
{value: "", name: "--"},
|
|
||||||
{value: 1900, name: "1900"},
|
|
||||||
{value: 1950, name: "1950"},
|
|
||||||
{value: 2014, name: "2014"},
|
|
||||||
],
|
|
||||||
required: false,
|
|
||||||
instructions: 'Select your year of birth.',
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'mailing_address',
|
|
||||||
label: 'Mailing Address',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'textarea',
|
|
||||||
required: false,
|
|
||||||
instructions: 'Enter your mailing address.',
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'goals',
|
|
||||||
label: 'Goals',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'textarea',
|
|
||||||
required: false,
|
|
||||||
instructions: "If you'd like, tell us why you're interested in edX.",
|
|
||||||
restrictions: {}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'honor_code',
|
|
||||||
label: 'I agree to the <a href="/honor">Terms of Service and Honor Code</a>',
|
|
||||||
defaultValue: '',
|
|
||||||
type: 'checkbox',
|
|
||||||
required: true,
|
|
||||||
instructions: '',
|
|
||||||
restrictions: {}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
};
|
|
||||||
|
|
||||||
var createRegisterView = function(that) {
|
var model = null,
|
||||||
// Initialize the register model
|
view = null,
|
||||||
model = new RegisterModel({}, {
|
requests = null,
|
||||||
url: FORM_DESCRIPTION.submit_url,
|
authComplete = false,
|
||||||
method: FORM_DESCRIPTION.method
|
PLATFORM_NAME = 'edX',
|
||||||
});
|
COURSE_ID = "edX/DemoX/Fall",
|
||||||
|
USER_DATA = {
|
||||||
// Initialize the register view
|
email: 'xsy@edx.org',
|
||||||
view = new RegisterView({
|
name: 'Xsy M. Education',
|
||||||
fields: FORM_DESCRIPTION.fields,
|
username: 'Xsy',
|
||||||
model: model,
|
password: 'xsyisawesome',
|
||||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
level_of_education: 'p',
|
||||||
platformName: PLATFORM_NAME
|
gender: 'm',
|
||||||
});
|
year_of_birth: 2014,
|
||||||
|
mailing_address: '141 Portland',
|
||||||
// Spy on AJAX requests
|
goals: 'To boldly learn what no letter of the alphabet has learned before',
|
||||||
requests = AjaxHelpers.requests(that);
|
honor_code: true
|
||||||
|
},
|
||||||
// Intercept events from the view
|
THIRD_PARTY_AUTH = {
|
||||||
authComplete = false;
|
currentProvider: null,
|
||||||
view.on("auth-complete", function() {
|
providers: [
|
||||||
authComplete = true;
|
{
|
||||||
});
|
name: 'Google',
|
||||||
|
iconClass: 'icon-google-plus',
|
||||||
|
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||||
|
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'Facebook',
|
||||||
|
iconClass: 'icon-facebook',
|
||||||
|
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
|
||||||
|
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
FORM_DESCRIPTION = {
|
||||||
|
method: 'post',
|
||||||
|
submit_url: '/user_api/v1/account/registration/',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
name: 'email',
|
||||||
|
label: 'Email',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'email',
|
||||||
|
required: true,
|
||||||
|
placeholder: 'place@holder.org',
|
||||||
|
instructions: 'Enter your email.',
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'name',
|
||||||
|
label: 'Full Name',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
instructions: 'Enter your username.',
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'username',
|
||||||
|
label: 'Username',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'text',
|
||||||
|
required: true,
|
||||||
|
instructions: 'Enter your username.',
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'password',
|
||||||
|
label: 'Password',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'password',
|
||||||
|
required: true,
|
||||||
|
instructions: 'Enter your password.',
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'level_of_education',
|
||||||
|
label: 'Highest Level of Education Completed',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{value: "", name: "--"},
|
||||||
|
{value: "p", name: "Doctorate"},
|
||||||
|
{value: "m", name: "Master's or professional degree"},
|
||||||
|
{value: "b", name: "Bachelor's degree"},
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
instructions: 'Select your education level.',
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'gender',
|
||||||
|
label: 'Gender',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{value: "", name: "--"},
|
||||||
|
{value: "m", name: "Male"},
|
||||||
|
{value: "f", name: "Female"},
|
||||||
|
{value: "o", name: "Other"},
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
instructions: 'Select your gender.',
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'year_of_birth',
|
||||||
|
label: 'Year of Birth',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'select',
|
||||||
|
options: [
|
||||||
|
{value: "", name: "--"},
|
||||||
|
{value: 1900, name: "1900"},
|
||||||
|
{value: 1950, name: "1950"},
|
||||||
|
{value: 2014, name: "2014"},
|
||||||
|
],
|
||||||
|
required: false,
|
||||||
|
instructions: 'Select your year of birth.',
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'mailing_address',
|
||||||
|
label: 'Mailing Address',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'textarea',
|
||||||
|
required: false,
|
||||||
|
instructions: 'Enter your mailing address.',
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'goals',
|
||||||
|
label: 'Goals',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'textarea',
|
||||||
|
required: false,
|
||||||
|
instructions: "If you'd like, tell us why you're interested in edX.",
|
||||||
|
restrictions: {}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'honor_code',
|
||||||
|
label: 'I agree to the <a href="/honor">Terms of Service and Honor Code</a>',
|
||||||
|
defaultValue: '',
|
||||||
|
type: 'checkbox',
|
||||||
|
required: true,
|
||||||
|
instructions: '',
|
||||||
|
restrictions: {}
|
||||||
|
}
|
||||||
|
]
|
||||||
};
|
};
|
||||||
|
|
||||||
var submitForm = function(validationSuccess) {
|
var createRegisterView = function(that) {
|
||||||
// Simulate manual entry of registration form data
|
// Initialize the register model
|
||||||
$('#register-email').val(USER_DATA.email);
|
model = new RegisterModel({}, {
|
||||||
$('#register-name').val(USER_DATA.name);
|
url: FORM_DESCRIPTION.submit_url,
|
||||||
$('#register-username').val(USER_DATA.username);
|
method: FORM_DESCRIPTION.method
|
||||||
$('#register-password').val(USER_DATA.password);
|
|
||||||
$('#register-level_of_education').val(USER_DATA.level_of_education);
|
|
||||||
$('#register-gender').val(USER_DATA.gender);
|
|
||||||
$('#register-year_of_birth').val(USER_DATA.year_of_birth);
|
|
||||||
$('#register-mailing_address').val(USER_DATA.mailing_address);
|
|
||||||
$('#register-goals').val(USER_DATA.goals);
|
|
||||||
|
|
||||||
// Check the honor code checkbox
|
|
||||||
$('#register-honor_code').prop('checked', USER_DATA.honor_code);
|
|
||||||
|
|
||||||
// Create a fake click event
|
|
||||||
var clickEvent = $.Event('click');
|
|
||||||
|
|
||||||
// If validationSuccess isn't passed, we avoid
|
|
||||||
// spying on `view.validate` twice
|
|
||||||
if ( !_.isUndefined(validationSuccess) ) {
|
|
||||||
// Force validation to return as expected
|
|
||||||
spyOn(view, 'validate').andReturn({
|
|
||||||
isValid: validationSuccess,
|
|
||||||
message: 'Submission was validated.'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Submit the email address
|
|
||||||
view.submitForm(clickEvent);
|
|
||||||
};
|
|
||||||
|
|
||||||
beforeEach(function() {
|
|
||||||
setFixtures('<div id="register-form"></div>');
|
|
||||||
TemplateHelpers.installTemplate('templates/student_account/register');
|
|
||||||
TemplateHelpers.installTemplate('templates/student_account/form_field');
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('registers a new user', function() {
|
// Initialize the register view
|
||||||
createRegisterView(this);
|
view = new RegisterView({
|
||||||
|
fields: FORM_DESCRIPTION.fields,
|
||||||
// Submit the form, with successful validation
|
model: model,
|
||||||
submitForm(true);
|
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||||
|
platformName: PLATFORM_NAME
|
||||||
// Verify that the client contacts the server with the expected data
|
|
||||||
AjaxHelpers.expectRequest(
|
|
||||||
requests, 'POST',
|
|
||||||
FORM_DESCRIPTION.submit_url,
|
|
||||||
$.param( USER_DATA )
|
|
||||||
);
|
|
||||||
|
|
||||||
// Respond with status code 200
|
|
||||||
AjaxHelpers.respondWithJson(requests, {});
|
|
||||||
|
|
||||||
// Verify that auth complete is triggered
|
|
||||||
expect(authComplete).toBe(true);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('displays third-party auth registration buttons', function() {
|
// Spy on AJAX requests
|
||||||
createRegisterView(this);
|
requests = AjaxHelpers.requests(that);
|
||||||
|
|
||||||
// Verify that Google and Facebook registration buttons are displayed
|
// Intercept events from the view
|
||||||
expect($('.button-Google')).toBeVisible();
|
authComplete = false;
|
||||||
expect($('.button-Facebook')).toBeVisible();
|
view.on("auth-complete", function() {
|
||||||
|
authComplete = true;
|
||||||
});
|
});
|
||||||
|
};
|
||||||
|
|
||||||
it('validates registration form fields', function() {
|
var submitForm = function(validationSuccess) {
|
||||||
createRegisterView(this);
|
// Simulate manual entry of registration form data
|
||||||
|
$('#register-email').val(USER_DATA.email);
|
||||||
|
$('#register-name').val(USER_DATA.name);
|
||||||
|
$('#register-username').val(USER_DATA.username);
|
||||||
|
$('#register-password').val(USER_DATA.password);
|
||||||
|
$('#register-level_of_education').val(USER_DATA.level_of_education);
|
||||||
|
$('#register-gender').val(USER_DATA.gender);
|
||||||
|
$('#register-year_of_birth').val(USER_DATA.year_of_birth);
|
||||||
|
$('#register-mailing_address').val(USER_DATA.mailing_address);
|
||||||
|
$('#register-goals').val(USER_DATA.goals);
|
||||||
|
|
||||||
// Submit the form, with successful validation
|
// Check the honor code checkbox
|
||||||
submitForm(true);
|
$('#register-honor_code').prop('checked', USER_DATA.honor_code);
|
||||||
|
|
||||||
// Verify that validation of form fields occurred
|
// Create a fake click event
|
||||||
expect(view.validate).toHaveBeenCalledWith($('#register-email')[0]);
|
var clickEvent = $.Event('click');
|
||||||
expect(view.validate).toHaveBeenCalledWith($('#register-name')[0]);
|
|
||||||
expect(view.validate).toHaveBeenCalledWith($('#register-username')[0]);
|
|
||||||
expect(view.validate).toHaveBeenCalledWith($('#register-password')[0]);
|
|
||||||
|
|
||||||
// Verify that no submission errors are visible
|
// If validationSuccess isn't passed, we avoid
|
||||||
expect(view.$errors).toHaveClass('hidden');
|
// spying on `view.validate` twice
|
||||||
});
|
if ( !_.isUndefined(validationSuccess) ) {
|
||||||
|
// Force validation to return as expected
|
||||||
|
spyOn(view, 'validate').andReturn({
|
||||||
|
isValid: validationSuccess,
|
||||||
|
message: 'Submission was validated.'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
it('displays registration form validation errors', function() {
|
// Submit the email address
|
||||||
createRegisterView(this);
|
view.submitForm(clickEvent);
|
||||||
|
};
|
||||||
|
|
||||||
// Submit the form, with failed validation
|
beforeEach(function() {
|
||||||
submitForm(false);
|
setFixtures('<div id="register-form"></div>');
|
||||||
|
TemplateHelpers.installTemplate('templates/student_account/register');
|
||||||
// Verify that submission errors are visible
|
TemplateHelpers.installTemplate('templates/student_account/form_field');
|
||||||
expect(view.$errors).not.toHaveClass('hidden');
|
|
||||||
|
|
||||||
// Expect that auth complete is NOT triggered
|
|
||||||
expect(authComplete).toBe(false);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('displays an error if the server returns an error while registering', function() {
|
|
||||||
createRegisterView(this);
|
|
||||||
|
|
||||||
// Submit the form, with successful validation
|
|
||||||
submitForm(true);
|
|
||||||
|
|
||||||
// Simulate an error from the LMS servers
|
|
||||||
AjaxHelpers.respondWithError(requests);
|
|
||||||
|
|
||||||
// Expect that an error is displayed and that auth complete is NOT triggered
|
|
||||||
expect(view.$errors).not.toHaveClass('hidden');
|
|
||||||
expect(authComplete).toBe(false);
|
|
||||||
|
|
||||||
// If we try again and succeed, the error should go away
|
|
||||||
submitForm();
|
|
||||||
|
|
||||||
// This time, respond with status code 200
|
|
||||||
AjaxHelpers.respondWithJson(requests, {});
|
|
||||||
|
|
||||||
// Expect that the error is hidden and that auth complete is triggered
|
|
||||||
expect(view.$errors).toHaveClass('hidden');
|
|
||||||
expect(authComplete).toBe(true);
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
}
|
|
||||||
);
|
it('registers a new user', function() {
|
||||||
|
createRegisterView(this);
|
||||||
|
|
||||||
|
// Submit the form, with successful validation
|
||||||
|
submitForm( true );
|
||||||
|
|
||||||
|
// Verify that the client contacts the server with the expected data
|
||||||
|
AjaxHelpers.expectRequest(
|
||||||
|
requests, 'POST',
|
||||||
|
FORM_DESCRIPTION.submit_url,
|
||||||
|
$.param( USER_DATA )
|
||||||
|
);
|
||||||
|
|
||||||
|
// Respond with status code 200
|
||||||
|
AjaxHelpers.respondWithJson(requests, {});
|
||||||
|
|
||||||
|
// Verify that auth complete is triggered
|
||||||
|
expect(authComplete).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('sends analytics info containing the enrolled course ID', function() {
|
||||||
|
createRegisterView( this );
|
||||||
|
|
||||||
|
// Simulate that the user is attempting to enroll in a course
|
||||||
|
// by setting the course_id query string param.
|
||||||
|
spyOn($, 'url').andCallFake(function( param ) {
|
||||||
|
if (param === "?course_id") {
|
||||||
|
return encodeURIComponent( COURSE_ID );
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Attempt to register
|
||||||
|
submitForm( true );
|
||||||
|
|
||||||
|
// Verify that the client sent the course ID for analytics
|
||||||
|
var expectedData = {};
|
||||||
|
$.extend(expectedData, USER_DATA, {
|
||||||
|
analytics: JSON.stringify({
|
||||||
|
enroll_course_id: COURSE_ID
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
AjaxHelpers.expectRequest(
|
||||||
|
requests, 'POST',
|
||||||
|
FORM_DESCRIPTION.submit_url,
|
||||||
|
$.param( expectedData )
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays third-party auth registration buttons', function() {
|
||||||
|
createRegisterView(this);
|
||||||
|
|
||||||
|
// Verify that Google and Facebook registration buttons are displayed
|
||||||
|
expect($('.button-Google')).toBeVisible();
|
||||||
|
expect($('.button-Facebook')).toBeVisible();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('validates registration form fields', function() {
|
||||||
|
createRegisterView(this);
|
||||||
|
|
||||||
|
// Submit the form, with successful validation
|
||||||
|
submitForm(true);
|
||||||
|
|
||||||
|
// Verify that validation of form fields occurred
|
||||||
|
expect(view.validate).toHaveBeenCalledWith($('#register-email')[0]);
|
||||||
|
expect(view.validate).toHaveBeenCalledWith($('#register-name')[0]);
|
||||||
|
expect(view.validate).toHaveBeenCalledWith($('#register-username')[0]);
|
||||||
|
expect(view.validate).toHaveBeenCalledWith($('#register-password')[0]);
|
||||||
|
|
||||||
|
// Verify that no submission errors are visible
|
||||||
|
expect(view.$errors).toHaveClass('hidden');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays registration form validation errors', function() {
|
||||||
|
createRegisterView(this);
|
||||||
|
|
||||||
|
// Submit the form, with failed validation
|
||||||
|
submitForm(false);
|
||||||
|
|
||||||
|
// Verify that submission errors are visible
|
||||||
|
expect(view.$errors).not.toHaveClass('hidden');
|
||||||
|
|
||||||
|
// Expect that auth complete is NOT triggered
|
||||||
|
expect(authComplete).toBe(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('displays an error if the server returns an error while registering', function() {
|
||||||
|
createRegisterView(this);
|
||||||
|
|
||||||
|
// Submit the form, with successful validation
|
||||||
|
submitForm(true);
|
||||||
|
|
||||||
|
// Simulate an error from the LMS servers
|
||||||
|
AjaxHelpers.respondWithError(requests);
|
||||||
|
|
||||||
|
// Expect that an error is displayed and that auth complete is NOT triggered
|
||||||
|
expect(view.$errors).not.toHaveClass('hidden');
|
||||||
|
expect(authComplete).toBe(false);
|
||||||
|
|
||||||
|
// If we try again and succeed, the error should go away
|
||||||
|
submitForm();
|
||||||
|
|
||||||
|
// This time, respond with status code 200
|
||||||
|
AjaxHelpers.respondWithJson(requests, {});
|
||||||
|
|
||||||
|
// Expect that the error is hidden and that auth complete is triggered
|
||||||
|
expect(view.$errors).toHaveClass('hidden');
|
||||||
|
expect(authComplete).toBe(true);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -24,14 +24,27 @@ var edx = edx || {};
|
|||||||
},
|
},
|
||||||
|
|
||||||
sync: function(method, model) {
|
sync: function(method, model) {
|
||||||
var headers = {
|
var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
|
||||||
'X-CSRFToken': $.cookie('csrftoken')
|
data = {},
|
||||||
};
|
analytics,
|
||||||
|
courseId = $.url( '?course_id' );
|
||||||
|
|
||||||
|
// If there is a course ID in the query string param,
|
||||||
|
// send that to the server as well so it can be included
|
||||||
|
// in analytics events.
|
||||||
|
if ( courseId ) {
|
||||||
|
analytics = JSON.stringify({
|
||||||
|
enroll_course_id: decodeURIComponent( courseId )
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include all form fields and analytics info in the data sent to the server
|
||||||
|
$.extend( data, model.attributes, { analytics: analytics });
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: model.urlRoot,
|
url: model.urlRoot,
|
||||||
type: model.ajaxType,
|
type: model.ajaxType,
|
||||||
data: model.attributes,
|
data: data,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
success: function() {
|
success: function() {
|
||||||
model.trigger('sync');
|
model.trigger('sync');
|
||||||
|
|||||||
@@ -30,14 +30,27 @@ var edx = edx || {};
|
|||||||
},
|
},
|
||||||
|
|
||||||
sync: function(method, model) {
|
sync: function(method, model) {
|
||||||
var headers = {
|
var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
|
||||||
'X-CSRFToken': $.cookie('csrftoken')
|
data = {},
|
||||||
};
|
analytics,
|
||||||
|
courseId = $.url( '?course_id' );
|
||||||
|
|
||||||
|
// If there is a course ID in the query string param,
|
||||||
|
// send that to the server as well so it can be included
|
||||||
|
// in analytics events.
|
||||||
|
if ( courseId ) {
|
||||||
|
analytics = JSON.stringify({
|
||||||
|
enroll_course_id: decodeURIComponent( courseId )
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include all form fields and analytics info in the data sent to the server
|
||||||
|
$.extend( data, model.attributes, { analytics: analytics });
|
||||||
|
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url: model.urlRoot,
|
url: model.urlRoot,
|
||||||
type: model.ajaxType,
|
type: model.ajaxType,
|
||||||
data: model.attributes,
|
data: data,
|
||||||
headers: headers,
|
headers: headers,
|
||||||
success: function() {
|
success: function() {
|
||||||
model.trigger('sync');
|
model.trigger('sync');
|
||||||
|
|||||||
Reference in New Issue
Block a user