Merge pull request #9473 from edx/zub/story/ecom-2044-login-registration-requirejs
use the standard syntax to load JavaScript dependencies on logistrati…
This commit is contained in:
@@ -69,8 +69,8 @@ class IntegrationTestLTI(testutil.TestCase):
|
||||
self.assertTrue(login_response['Location'].endswith(reverse('signin_user')))
|
||||
register_response = self.client.get(login_response['Location'])
|
||||
self.assertEqual(register_response.status_code, 200)
|
||||
self.assertIn('currentProvider": "LTI Test Tool Consumer"', register_response.content)
|
||||
self.assertIn('"errorMessage": null', register_response.content)
|
||||
self.assertIn('"currentProvider": "LTI Test Tool Consumer"', register_response.content)
|
||||
self.assertIn('"errorMessage": null', register_response.content)
|
||||
|
||||
# Now complete the form:
|
||||
ajax_register_response = self.client.post(
|
||||
@@ -153,7 +153,7 @@ class IntegrationTestLTI(testutil.TestCase):
|
||||
register_response = self.client.get(login_response['Location'])
|
||||
self.assertEqual(register_response.status_code, 200)
|
||||
self.assertIn(
|
||||
'currentProvider": "Tool Consumer with Secret in Settings"',
|
||||
'"currentProvider": "Tool Consumer with Secret in Settings"',
|
||||
register_response.content
|
||||
)
|
||||
self.assertIn('"errorMessage": null', register_response.content)
|
||||
self.assertIn('"errorMessage": null', register_response.content)
|
||||
|
||||
@@ -1,13 +1,20 @@
|
||||
"""
|
||||
Third_party_auth integration tests using a mock version of the TestShib provider
|
||||
"""
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
import json
|
||||
import unittest
|
||||
import httpretty
|
||||
from mock import patch
|
||||
|
||||
from django.core.urlresolvers import reverse
|
||||
|
||||
from openedx.core.lib.json_utils import EscapedEdxJSONEncoder
|
||||
|
||||
from student.tests.factories import UserFactory
|
||||
from third_party_auth.tasks import fetch_saml_metadata
|
||||
from third_party_auth.tests import testutil
|
||||
import unittest
|
||||
|
||||
|
||||
TESTSHIB_ENTITY_ID = 'https://idp.testshib.org/idp/shibboleth'
|
||||
TESTSHIB_METADATA_URL = 'https://mock.testshib.org/metadata/testshib-providers.xml'
|
||||
@@ -81,11 +88,11 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
|
||||
# We'd now like to see if the "You've successfully signed into TestShib" message is
|
||||
# shown, but it's managed by a JavaScript runtime template, and we can't run JS in this
|
||||
# type of test, so we just check for the variable that triggers that message.
|
||||
self.assertIn('"currentProvider": "TestShib"', register_response.content)
|
||||
self.assertIn('"errorMessage": null', register_response.content)
|
||||
self.assertIn('"currentProvider": "TestShib"', register_response.content)
|
||||
self.assertIn('"errorMessage": null', register_response.content)
|
||||
# Now do a crude check that the data (e.g. email) from the provider is displayed in the form:
|
||||
self.assertIn('"defaultValue": "myself@testshib.org"', register_response.content)
|
||||
self.assertIn('"defaultValue": "Me Myself And I"', register_response.content)
|
||||
self.assertIn('"defaultValue": "myself@testshib.org"', register_response.content)
|
||||
self.assertIn('"defaultValue": "Me Myself And I"', register_response.content)
|
||||
# Now complete the form:
|
||||
ajax_register_response = self.client.post(
|
||||
reverse('user_api_registration'),
|
||||
@@ -128,8 +135,8 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
|
||||
# We'd now like to see if the "You've successfully signed into TestShib" message is
|
||||
# shown, but it's managed by a JavaScript runtime template, and we can't run JS in this
|
||||
# type of test, so we just check for the variable that triggers that message.
|
||||
self.assertIn('"currentProvider": "TestShib"', login_response.content)
|
||||
self.assertIn('"errorMessage": null', login_response.content)
|
||||
self.assertIn('"currentProvider": "TestShib"', login_response.content)
|
||||
self.assertIn('"errorMessage": null', login_response.content)
|
||||
# Now the user enters their username and password.
|
||||
# The AJAX on the page will log them in:
|
||||
ajax_login_response = self.client.post(
|
||||
@@ -183,7 +190,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
|
||||
response = self.client.get(self.login_page_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("TestShib", response.content)
|
||||
self.assertIn(TPA_TESTSHIB_LOGIN_URL.replace('&', '&'), response.content)
|
||||
self.assertIn(json.dumps(TPA_TESTSHIB_LOGIN_URL, cls=EscapedEdxJSONEncoder), response.content)
|
||||
return response
|
||||
|
||||
def _check_register_page(self):
|
||||
@@ -191,7 +198,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
|
||||
response = self.client.get(self.register_page_url)
|
||||
self.assertEqual(response.status_code, 200)
|
||||
self.assertIn("TestShib", response.content)
|
||||
self.assertIn(TPA_TESTSHIB_REGISTER_URL.replace('&', '&'), response.content)
|
||||
self.assertIn(json.dumps(TPA_TESTSHIB_REGISTER_URL, cls=EscapedEdxJSONEncoder), response.content)
|
||||
return response
|
||||
|
||||
def _configure_testshib_provider(self, **kwargs):
|
||||
|
||||
@@ -1,192 +1,195 @@
|
||||
describe('edx.utils.validate', function () {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define(['jquery', 'js/utils/edx.utils.validate'],
|
||||
function($) {
|
||||
|
||||
var fixture = null,
|
||||
field = null,
|
||||
result = null,
|
||||
MIN_LENGTH = 2,
|
||||
MAX_LENGTH = 20,
|
||||
VALID_STRING = 'xsy_is_awesome',
|
||||
SHORT_STRING = 'x',
|
||||
LONG_STRING = 'xsy_is_way_too_awesome',
|
||||
EMAIL_ERROR_FRAGMENT = 'formatted',
|
||||
MIN_ERROR_FRAGMENT = 'least',
|
||||
MAX_ERROR_FRAGMENT = 'up to',
|
||||
REQUIRED_ERROR_FRAGMENT = 'Please enter your',
|
||||
CUSTOM_MESSAGE = 'custom message';
|
||||
var fixture = null,
|
||||
field = null,
|
||||
result = null,
|
||||
MIN_LENGTH = 2,
|
||||
MAX_LENGTH = 20,
|
||||
VALID_STRING = 'xsy_is_awesome',
|
||||
SHORT_STRING = 'x',
|
||||
LONG_STRING = 'xsy_is_way_too_awesome',
|
||||
EMAIL_ERROR_FRAGMENT = 'formatted',
|
||||
MIN_ERROR_FRAGMENT = 'least',
|
||||
MAX_ERROR_FRAGMENT = 'up to',
|
||||
REQUIRED_ERROR_FRAGMENT = 'Please enter your',
|
||||
CUSTOM_MESSAGE = 'custom message';
|
||||
|
||||
var createFixture = function( type, name, required, minlength, maxlength, value ) {
|
||||
setFixtures('<input id="field" type=' + type + '>');
|
||||
var createFixture = function( type, name, required, minlength, maxlength, value ) {
|
||||
setFixtures('<input id="field" type=' + type + '>');
|
||||
|
||||
field = $('#field');
|
||||
field.prop('required', required);
|
||||
field.attr({
|
||||
name: name,
|
||||
minlength: minlength,
|
||||
maxlength: maxlength,
|
||||
value: value
|
||||
field = $('#field');
|
||||
field.prop('required', required);
|
||||
field.attr({
|
||||
name: name,
|
||||
minlength: minlength,
|
||||
maxlength: maxlength,
|
||||
value: value
|
||||
});
|
||||
};
|
||||
|
||||
var expectValid = function() {
|
||||
result = edx.utils.validate(field);
|
||||
expect(result.isValid).toBe(true);
|
||||
};
|
||||
|
||||
var expectInvalid = function( errorFragment ) {
|
||||
result = edx.utils.validate(field);
|
||||
expect(result.isValid).toBe(false);
|
||||
expect(result.message).toMatch(errorFragment);
|
||||
};
|
||||
|
||||
it('succeeds if an optional field is left blank', function () {
|
||||
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, '');
|
||||
expectValid();
|
||||
});
|
||||
};
|
||||
|
||||
var expectValid = function() {
|
||||
result = edx.utils.validate(field);
|
||||
expect(result.isValid).toBe(true);
|
||||
};
|
||||
it('succeeds if a required field is provided a valid value', function () {
|
||||
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, VALID_STRING);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
var expectInvalid = function( errorFragment ) {
|
||||
result = edx.utils.validate(field);
|
||||
expect(result.isValid).toBe(false);
|
||||
expect(result.message).toMatch(errorFragment);
|
||||
};
|
||||
it('fails if a required field is left blank', function () {
|
||||
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
|
||||
expectInvalid(REQUIRED_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if an optional field is left blank', function () {
|
||||
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, '');
|
||||
expectValid();
|
||||
it('fails if a field is provided a value below its minimum character limit', function () {
|
||||
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, SHORT_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectInvalid(MIN_ERROR_FRAGMENT);
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectInvalid(MIN_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if a field with no minimum character limit is provided a value below its maximum character limit', function () {
|
||||
createFixture('text', 'username', false, null, MAX_LENGTH, SHORT_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectValid();
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('fails if a required field with no minimum character limit is left blank', function () {
|
||||
createFixture('text', 'username', true, null, MAX_LENGTH, '');
|
||||
expectInvalid(REQUIRED_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('fails if a field is provided a value above its maximum character limit', function () {
|
||||
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, LONG_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectInvalid(MAX_ERROR_FRAGMENT);
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectInvalid(MAX_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if a field with no maximum character limit is provided a value above its minimum character limit', function () {
|
||||
createFixture('text', 'username', false, MIN_LENGTH, null, LONG_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectValid();
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('succeeds if a field with no character limits is provided a value', function () {
|
||||
createFixture('text', 'username', false, null, null, VALID_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectValid();
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('fails if an email field is provided an invalid address', function () {
|
||||
createFixture('email', 'email', false, MIN_LENGTH, MAX_LENGTH, 'localpart');
|
||||
|
||||
// Verify optional field behavior
|
||||
expectInvalid(EMAIL_ERROR_FRAGMENT);
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', false);
|
||||
expectInvalid(EMAIL_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if an email field is provided a valid address', function () {
|
||||
createFixture('email', 'email', false, MIN_LENGTH, MAX_LENGTH, 'localpart@label.tld');
|
||||
|
||||
// Verify optional field behavior
|
||||
expectValid();
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('succeeds if a checkbox is optional, or required and checked, but fails if a required checkbox is unchecked', function () {
|
||||
createFixture('checkbox', 'checkbox', false, null, null, 'value');
|
||||
|
||||
// Optional, unchecked
|
||||
expectValid();
|
||||
|
||||
// Optional, checked
|
||||
field.prop('checked', true);
|
||||
expectValid();
|
||||
|
||||
// Required, checked
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
|
||||
// Required, unchecked
|
||||
field.prop('checked', false);
|
||||
expectInvalid(REQUIRED_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if a select is optional, or required and default is selected, but fails if a required select has the default option selected', function () {
|
||||
var select = [
|
||||
'<select id="dropdown" name="country">',
|
||||
'<option value="" data-isdefault="true">Please select a country</option>',
|
||||
'<option value="BE">Belgium</option>',
|
||||
'<option value="DE">Germany</option>',
|
||||
'</select>'
|
||||
].join('');
|
||||
|
||||
setFixtures(select);
|
||||
|
||||
field = $('#dropdown');
|
||||
|
||||
// Optional
|
||||
expectValid();
|
||||
|
||||
// Required, default text selected
|
||||
field.attr('required', true);
|
||||
expectInvalid(REQUIRED_ERROR_FRAGMENT);
|
||||
|
||||
// Required, country selected
|
||||
field.val('BE');
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('returns a custom error message if an invalid field has one attached', function () {
|
||||
// Create a blank required field
|
||||
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
|
||||
|
||||
// Attach a custom error message to the field
|
||||
field.data('errormsg-required', CUSTOM_MESSAGE);
|
||||
|
||||
expectInvalid(CUSTOM_MESSAGE);
|
||||
});
|
||||
});
|
||||
|
||||
it('succeeds if a required field is provided a valid value', function () {
|
||||
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, VALID_STRING);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('fails if a required field is left blank', function () {
|
||||
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
|
||||
expectInvalid(REQUIRED_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('fails if a field is provided a value below its minimum character limit', function () {
|
||||
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, SHORT_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectInvalid(MIN_ERROR_FRAGMENT);
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectInvalid(MIN_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if a field with no minimum character limit is provided a value below its maximum character limit', function () {
|
||||
createFixture('text', 'username', false, null, MAX_LENGTH, SHORT_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectValid();
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('fails if a required field with no minimum character limit is left blank', function () {
|
||||
createFixture('text', 'username', true, null, MAX_LENGTH, '');
|
||||
expectInvalid(REQUIRED_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('fails if a field is provided a value above its maximum character limit', function () {
|
||||
createFixture('text', 'username', false, MIN_LENGTH, MAX_LENGTH, LONG_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectInvalid(MAX_ERROR_FRAGMENT);
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectInvalid(MAX_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if a field with no maximum character limit is provided a value above its minimum character limit', function () {
|
||||
createFixture('text', 'username', false, MIN_LENGTH, null, LONG_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectValid();
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('succeeds if a field with no character limits is provided a value', function () {
|
||||
createFixture('text', 'username', false, null, null, VALID_STRING);
|
||||
|
||||
// Verify optional field behavior
|
||||
expectValid();
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('fails if an email field is provided an invalid address', function () {
|
||||
createFixture('email', 'email', false, MIN_LENGTH, MAX_LENGTH, 'localpart');
|
||||
|
||||
// Verify optional field behavior
|
||||
expectInvalid(EMAIL_ERROR_FRAGMENT);
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', false);
|
||||
expectInvalid(EMAIL_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if an email field is provided a valid address', function () {
|
||||
createFixture('email', 'email', false, MIN_LENGTH, MAX_LENGTH, 'localpart@label.tld');
|
||||
|
||||
// Verify optional field behavior
|
||||
expectValid();
|
||||
|
||||
// Verify required field behavior
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('succeeds if a checkbox is optional, or required and checked, but fails if a required checkbox is unchecked', function () {
|
||||
createFixture('checkbox', 'checkbox', false, null, null, 'value');
|
||||
|
||||
// Optional, unchecked
|
||||
expectValid();
|
||||
|
||||
// Optional, checked
|
||||
field.prop('checked', true);
|
||||
expectValid();
|
||||
|
||||
// Required, checked
|
||||
field.prop('required', true);
|
||||
expectValid();
|
||||
|
||||
// Required, unchecked
|
||||
field.prop('checked', false);
|
||||
expectInvalid(REQUIRED_ERROR_FRAGMENT);
|
||||
});
|
||||
|
||||
it('succeeds if a select is optional, or required and default is selected, but fails if a required select has the default option selected', function () {
|
||||
var select = [
|
||||
'<select id="dropdown" name="country">',
|
||||
'<option value="" data-isdefault="true">Please select a country</option>',
|
||||
'<option value="BE">Belgium</option>',
|
||||
'<option value="DE">Germany</option>',
|
||||
'</select>'
|
||||
].join('');
|
||||
|
||||
setFixtures(select);
|
||||
|
||||
field = $('#dropdown');
|
||||
|
||||
// Optional
|
||||
expectValid();
|
||||
|
||||
// Required, default text selected
|
||||
field.attr('required', true);
|
||||
expectInvalid(REQUIRED_ERROR_FRAGMENT);
|
||||
|
||||
// Required, country selected
|
||||
field.val('BE');
|
||||
expectValid();
|
||||
});
|
||||
|
||||
it('returns a custom error message if an invalid field has one attached', function () {
|
||||
// Create a blank required field
|
||||
createFixture('text', 'username', true, MIN_LENGTH, MAX_LENGTH, '');
|
||||
|
||||
// Attach a custom error message to the field
|
||||
field.data('errormsg-required', CUSTOM_MESSAGE);
|
||||
|
||||
expectInvalid(CUSTOM_MESSAGE);
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,186 +1,193 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function( $, _, _s, gettext ) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'underscore.string',
|
||||
'gettext'
|
||||
],
|
||||
function($, _, _s, gettext) {
|
||||
var utils;
|
||||
|
||||
/* Mix non-conflicting functions from underscore.string
|
||||
* (all but include, contains, and reverse) into the
|
||||
* Underscore namespace. In practice, this mixin is done
|
||||
* by the access view, but doing it here helps keep the
|
||||
* utility self-contained.
|
||||
*/
|
||||
_.mixin( _.str.exports() );
|
||||
/* Mix non-conflicting functions from underscore.string
|
||||
* (all but include, contains, and reverse) into the
|
||||
* Underscore namespace. In practice, this mixin is done
|
||||
* by the access view, but doing it here helps keep the
|
||||
* utility self-contained.
|
||||
*/
|
||||
if (_.isUndefined(_s)) {
|
||||
_s = _.str;
|
||||
}
|
||||
_.mixin( _s.exports() );
|
||||
|
||||
edx.utils = edx.utils || {};
|
||||
utils = (function(){
|
||||
var _fn = {
|
||||
validate: {
|
||||
|
||||
var utils = (function(){
|
||||
var _fn = {
|
||||
validate: {
|
||||
msg: {
|
||||
email: '<li><%- gettext("The email address you\'ve provided isn\'t formatted correctly.") %></li>',
|
||||
min: '<li><%- _.sprintf( gettext("%(field)s must have at least %(count)d characters."), context ) %></li>',
|
||||
max: '<li><%- _.sprintf( gettext("%(field)s can only contain up to %(count)d characters."), context ) %></li>',
|
||||
required: '<li><%- _.sprintf( gettext("Please enter your %(field)s."), context ) %></li>',
|
||||
custom: '<li><%= content %></li>'
|
||||
},
|
||||
|
||||
msg: {
|
||||
email: '<li><%- gettext("The email address you\'ve provided isn\'t formatted correctly.") %></li>',
|
||||
min: '<li><%- _.sprintf( gettext("%(field)s must have at least %(count)d characters."), context ) %></li>',
|
||||
max: '<li><%- _.sprintf( gettext("%(field)s can only contain up to %(count)d characters."), context ) %></li>',
|
||||
required: '<li><%- _.sprintf( gettext("Please enter your %(field)s."), context ) %></li>',
|
||||
custom: '<li><%= content %></li>'
|
||||
},
|
||||
field: function( el ) {
|
||||
var $el = $(el),
|
||||
required = true,
|
||||
min = true,
|
||||
max = true,
|
||||
email = true,
|
||||
response = {},
|
||||
isBlank = _fn.validate.isBlank( $el );
|
||||
|
||||
field: function( el ) {
|
||||
var $el = $(el),
|
||||
required = true,
|
||||
min = true,
|
||||
max = true,
|
||||
email = true,
|
||||
response = {},
|
||||
isBlank = _fn.validate.isBlank( $el );
|
||||
|
||||
if ( _fn.validate.isRequired( $el ) ) {
|
||||
if ( isBlank ) {
|
||||
required = false;
|
||||
} else {
|
||||
if ( _fn.validate.isRequired( $el ) ) {
|
||||
if ( isBlank ) {
|
||||
required = false;
|
||||
} else {
|
||||
min = _fn.validate.str.minlength( $el );
|
||||
max = _fn.validate.str.maxlength( $el );
|
||||
email = _fn.validate.email.valid( $el );
|
||||
}
|
||||
} else if ( !isBlank ) {
|
||||
min = _fn.validate.str.minlength( $el );
|
||||
max = _fn.validate.str.maxlength( $el );
|
||||
email = _fn.validate.email.valid( $el );
|
||||
}
|
||||
} else if ( !isBlank ) {
|
||||
min = _fn.validate.str.minlength( $el );
|
||||
max = _fn.validate.str.maxlength( $el );
|
||||
email = _fn.validate.email.valid( $el );
|
||||
}
|
||||
|
||||
response.isValid = required && min && max && email;
|
||||
response.isValid = required && min && max && email;
|
||||
|
||||
if ( !response.isValid ) {
|
||||
_fn.validate.removeDefault( $el );
|
||||
if ( !response.isValid ) {
|
||||
_fn.validate.removeDefault( $el );
|
||||
|
||||
response.message = _fn.validate.getMessage( $el, {
|
||||
required: required,
|
||||
min: min,
|
||||
max: max,
|
||||
email: email
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
},
|
||||
|
||||
str: {
|
||||
minlength: function( $el ) {
|
||||
var min = $el.attr('minlength') || 0;
|
||||
|
||||
return min <= $el.val().length;
|
||||
},
|
||||
|
||||
maxlength: function( $el ) {
|
||||
var max = $el.attr('maxlength') || false;
|
||||
|
||||
return ( !!max ) ? max >= $el.val().length : true;
|
||||
}
|
||||
},
|
||||
|
||||
isRequired: function( $el ) {
|
||||
return $el.attr('required');
|
||||
},
|
||||
|
||||
isBlank: function( $el ) {
|
||||
var type = $el.attr('type'),
|
||||
isBlank;
|
||||
|
||||
if ( type === 'checkbox' ) {
|
||||
isBlank = !$el.prop('checked');
|
||||
} else if ( type === 'select' ) {
|
||||
isBlank = ( $el.data('isdefault') === true );
|
||||
} else {
|
||||
isBlank = !$el.val();
|
||||
}
|
||||
|
||||
return isBlank;
|
||||
},
|
||||
|
||||
email: {
|
||||
// This is the same regex used to validate email addresses in Django 1.4
|
||||
regex: new RegExp(
|
||||
[
|
||||
'(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*',
|
||||
'|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"',
|
||||
')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+[A-Z]{2,6}\\.?$)',
|
||||
'|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$'
|
||||
].join(''), 'i'
|
||||
),
|
||||
|
||||
valid: function( $el ) {
|
||||
return $el.attr('type') === 'email' ? _fn.validate.email.format( $el.val() ) : true;
|
||||
},
|
||||
|
||||
format: function( str ) {
|
||||
return _fn.validate.email.regex.test( str );
|
||||
}
|
||||
},
|
||||
|
||||
getLabel: function( id ) {
|
||||
// Extract the field label, remove the asterisk (if it appears) and any extra whitespace
|
||||
return $("label[for=" + id + "]").text().split("*")[0].trim();
|
||||
},
|
||||
|
||||
getMessage: function( $el, tests ) {
|
||||
var txt = [],
|
||||
tpl,
|
||||
label,
|
||||
obj,
|
||||
customMsg;
|
||||
|
||||
_.each( tests, function( value, key ) {
|
||||
if ( !value ) {
|
||||
label = _fn.validate.getLabel( $el.attr('id') );
|
||||
customMsg = $el.data('errormsg-' + key) || false;
|
||||
|
||||
// If the field has a custom error msg attached, use it
|
||||
if ( customMsg ) {
|
||||
tpl = _fn.validate.msg.custom;
|
||||
|
||||
obj = {
|
||||
content: customMsg
|
||||
};
|
||||
} else {
|
||||
tpl = _fn.validate.msg[key];
|
||||
|
||||
obj = {
|
||||
// We pass the context object to the template so that
|
||||
// we can perform variable interpolation using sprintf
|
||||
context: {
|
||||
field: label
|
||||
}
|
||||
};
|
||||
|
||||
if ( key === 'min' ) {
|
||||
obj.context.count = parseInt( $el.attr('minlength'), 10 );
|
||||
} else if ( key === 'max' ) {
|
||||
obj.context.count = parseInt( $el.attr('maxlength'), 10 );
|
||||
}
|
||||
}
|
||||
|
||||
txt.push( _.template( tpl, obj ) );
|
||||
response.message = _fn.validate.getMessage( $el, {
|
||||
required: required,
|
||||
min: min,
|
||||
max: max,
|
||||
email: email
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return txt.join(' ');
|
||||
},
|
||||
return response;
|
||||
},
|
||||
|
||||
// Removes the default HTML5 validation pop-up
|
||||
removeDefault: function( $el ) {
|
||||
if ( $el.setCustomValidity ) {
|
||||
$el.setCustomValidity(' ');
|
||||
str: {
|
||||
minlength: function( $el ) {
|
||||
var min = $el.attr('minlength') || 0;
|
||||
|
||||
return min <= $el.val().length;
|
||||
},
|
||||
|
||||
maxlength: function( $el ) {
|
||||
var max = $el.attr('maxlength') || false;
|
||||
|
||||
return ( !!max ) ? max >= $el.val().length : true;
|
||||
}
|
||||
},
|
||||
|
||||
isRequired: function( $el ) {
|
||||
return $el.attr('required');
|
||||
},
|
||||
|
||||
isBlank: function( $el ) {
|
||||
var type = $el.attr('type'),
|
||||
isBlank;
|
||||
|
||||
if ( type === 'checkbox' ) {
|
||||
isBlank = !$el.prop('checked');
|
||||
} else if ( type === 'select' ) {
|
||||
isBlank = ( $el.data('isdefault') === true );
|
||||
} else {
|
||||
isBlank = !$el.val();
|
||||
}
|
||||
|
||||
return isBlank;
|
||||
},
|
||||
|
||||
email: {
|
||||
// This is the same regex used to validate email addresses in Django 1.4
|
||||
regex: new RegExp(
|
||||
[
|
||||
'(^[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+(\\.[-!#$%&\'*+/=?^_`{}|~0-9A-Z]+)*',
|
||||
'|^"([\\001-\\010\\013\\014\\016-\\037!#-\\[\\]-\\177]|\\\\[\\001-\\011\\013\\014\\016-\\177])*"',
|
||||
')@((?:[A-Z0-9](?:[A-Z0-9-]{0,61}[A-Z0-9])?\\.)+[A-Z]{2,6}\\.?$)',
|
||||
'|\\[(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)(\\.(25[0-5]|2[0-4]\\d|[0-1]?\\d?\\d)){3}\\]$'
|
||||
].join(''), 'i'
|
||||
),
|
||||
|
||||
valid: function( $el ) {
|
||||
return $el.attr('type') === 'email' ? _fn.validate.email.format( $el.val() ) : true;
|
||||
},
|
||||
|
||||
format: function( str ) {
|
||||
return _fn.validate.email.regex.test( str );
|
||||
}
|
||||
},
|
||||
|
||||
getLabel: function( id ) {
|
||||
// Extract the field label, remove the asterisk (if it appears) and any extra whitespace
|
||||
return $("label[for=" + id + "]").text().split("*")[0].trim();
|
||||
},
|
||||
|
||||
getMessage: function( $el, tests ) {
|
||||
var txt = [],
|
||||
tpl,
|
||||
label,
|
||||
obj,
|
||||
customMsg;
|
||||
|
||||
_.each( tests, function( value, key ) {
|
||||
if ( !value ) {
|
||||
label = _fn.validate.getLabel( $el.attr('id') );
|
||||
customMsg = $el.data('errormsg-' + key) || false;
|
||||
|
||||
// If the field has a custom error msg attached, use it
|
||||
if ( customMsg ) {
|
||||
tpl = _fn.validate.msg.custom;
|
||||
|
||||
obj = {
|
||||
content: customMsg
|
||||
};
|
||||
} else {
|
||||
tpl = _fn.validate.msg[key];
|
||||
|
||||
obj = {
|
||||
// We pass the context object to the template so that
|
||||
// we can perform variable interpolation using sprintf
|
||||
context: {
|
||||
field: label
|
||||
}
|
||||
};
|
||||
|
||||
if ( key === 'min' ) {
|
||||
obj.context.count = parseInt( $el.attr('minlength'), 10 );
|
||||
} else if ( key === 'max' ) {
|
||||
obj.context.count = parseInt( $el.attr('maxlength'), 10 );
|
||||
}
|
||||
}
|
||||
|
||||
txt.push( _.template( tpl, obj ) );
|
||||
}
|
||||
});
|
||||
|
||||
return txt.join(' ');
|
||||
},
|
||||
|
||||
// Removes the default HTML5 validation pop-up
|
||||
removeDefault: function( $el ) {
|
||||
if ( $el.setCustomValidity ) {
|
||||
$el.setCustomValidity(' ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
|
||||
return {
|
||||
validate: _fn.validate.field
|
||||
};
|
||||
return {
|
||||
validate: _fn.validate.field
|
||||
};
|
||||
|
||||
})();
|
||||
})();
|
||||
|
||||
edx.utils.validate = utils.validate;
|
||||
|
||||
})( jQuery, _, _.str, gettext );
|
||||
return utils;
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -8,7 +8,6 @@ import json
|
||||
|
||||
import mock
|
||||
import ddt
|
||||
import markupsafe
|
||||
from django.conf import settings
|
||||
from django.core.urlresolvers import reverse
|
||||
from django.core import mail
|
||||
@@ -20,6 +19,7 @@ from django.test.client import RequestFactory
|
||||
|
||||
from openedx.core.djangoapps.user_api.accounts.api import activate_account, create_account
|
||||
from openedx.core.djangoapps.user_api.accounts import EMAIL_MAX_LENGTH
|
||||
from openedx.core.lib.json_utils import EscapedEdxJSONEncoder
|
||||
from student.tests.factories import UserFactory
|
||||
from student_account.views import account_settings_context
|
||||
from third_party_auth.tests.testutil import simulate_running_pipeline, ThirdPartyAuthTestMixin
|
||||
@@ -223,7 +223,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
|
||||
@ddt.unpack
|
||||
def test_login_and_registration_form(self, url_name, initial_mode):
|
||||
response = self.client.get(reverse(url_name))
|
||||
expected_data = u"data-initial-mode=\"{mode}\"".format(mode=initial_mode)
|
||||
expected_data = '"initial_mode": "{mode}"'.format(mode=initial_mode)
|
||||
self.assertContains(response, expected_data)
|
||||
|
||||
@ddt.data("signin_user", "register_user")
|
||||
@@ -255,6 +255,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
|
||||
# that preserves the querystring params
|
||||
with mock.patch.dict(settings.FEATURES, {'IS_EDX_DOMAIN': is_edx_domain}):
|
||||
response = self.client.get(reverse(url_name), params)
|
||||
|
||||
expected_url = '/login?{}'.format(self._finish_auth_url_param(params + [('next', '/dashboard')]))
|
||||
self.assertContains(response, expected_url)
|
||||
|
||||
@@ -330,7 +331,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
|
||||
def test_hinted_login(self):
|
||||
params = [("next", "/courses/something/?tpa_hint=oa2-google-oauth2")]
|
||||
response = self.client.get(reverse('signin_user'), params)
|
||||
self.assertContains(response, "data-third-party-auth-hint='oa2-google-oauth2'")
|
||||
self.assertContains(response, '"third_party_auth_hint": "oa2-google-oauth2"')
|
||||
|
||||
@override_settings(SITE_NAME=settings.MICROSITE_TEST_HOSTNAME)
|
||||
def test_microsite_uses_old_login_page(self):
|
||||
@@ -358,17 +359,17 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
|
||||
finish_auth_url = None
|
||||
if current_backend:
|
||||
finish_auth_url = reverse("social:complete", kwargs={"backend": current_backend}) + "?"
|
||||
auth_info = markupsafe.escape(
|
||||
json.dumps({
|
||||
"currentProvider": current_provider,
|
||||
"providers": providers,
|
||||
"secondaryProviders": [],
|
||||
"finishAuthUrl": finish_auth_url,
|
||||
"errorMessage": None,
|
||||
})
|
||||
)
|
||||
|
||||
expected_data = u"data-third-party-auth='{auth_info}'".format(
|
||||
auth_info = {
|
||||
"currentProvider": current_provider,
|
||||
"providers": providers,
|
||||
"secondaryProviders": [],
|
||||
"finishAuthUrl": finish_auth_url,
|
||||
"errorMessage": None,
|
||||
}
|
||||
auth_info = json.dumps(auth_info, cls=EscapedEdxJSONEncoder)
|
||||
|
||||
expected_data = '"third_party_auth": {auth_info}'.format(
|
||||
auth_info=auth_info
|
||||
)
|
||||
|
||||
|
||||
@@ -95,22 +95,25 @@ def login_and_registration_form(request, initial_mode="login"):
|
||||
|
||||
# Otherwise, render the combined login/registration page
|
||||
context = {
|
||||
'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in the header
|
||||
'disable_courseware_js': True,
|
||||
'initial_mode': initial_mode,
|
||||
'third_party_auth': json.dumps(_third_party_auth_context(request, redirect_to)),
|
||||
'third_party_auth_hint': third_party_auth_hint or '',
|
||||
'platform_name': settings.PLATFORM_NAME,
|
||||
'data': {
|
||||
'login_redirect_url': redirect_to,
|
||||
'initial_mode': initial_mode,
|
||||
'third_party_auth': _third_party_auth_context(request, redirect_to),
|
||||
'third_party_auth_hint': third_party_auth_hint or '',
|
||||
'platform_name': settings.PLATFORM_NAME,
|
||||
|
||||
# Include form descriptions retrieved from the user API.
|
||||
# We could have the JS client make these requests directly,
|
||||
# but we include them in the initial page load to avoid
|
||||
# the additional round-trip to the server.
|
||||
'login_form_desc': json.loads(form_descriptions['login']),
|
||||
'registration_form_desc': json.loads(form_descriptions['registration']),
|
||||
'password_reset_form_desc': json.loads(form_descriptions['password_reset']),
|
||||
},
|
||||
'login_redirect_url': redirect_to, # This gets added to the query string of the "Sign In" button in header
|
||||
'responsive': True,
|
||||
'allow_iframing': True,
|
||||
|
||||
# Include form descriptions retrieved from the user API.
|
||||
# We could have the JS client make these requests directly,
|
||||
# but we include them in the initial page load to avoid
|
||||
# the additional round-trip to the server.
|
||||
'login_form_desc': form_descriptions['login'],
|
||||
'registration_form_desc': form_descriptions['registration'],
|
||||
'password_reset_form_desc': form_descriptions['password_reset'],
|
||||
'disable_courseware_js': True,
|
||||
}
|
||||
|
||||
return render_to_response('student_account/login_and_register.html', context)
|
||||
|
||||
@@ -1303,26 +1303,6 @@ instructor_dash_js = (
|
||||
sorted(rooted_glob(PROJECT_ROOT / 'static', 'js/instructor_dashboard/**/*.js'))
|
||||
)
|
||||
|
||||
# JavaScript used by the student account and profile pages
|
||||
# These are not courseware, so they do not need many of the courseware-specific
|
||||
# JavaScript modules.
|
||||
student_account_js = [
|
||||
'js/utils/edx.utils.validate.js',
|
||||
'js/sticky_filter.js',
|
||||
'js/query-params.js',
|
||||
'js/student_account/models/LoginModel.js',
|
||||
'js/student_account/models/RegisterModel.js',
|
||||
'js/student_account/models/PasswordResetModel.js',
|
||||
'js/student_account/views/FormView.js',
|
||||
'js/student_account/views/LoginView.js',
|
||||
'js/student_account/views/HintedLoginView.js',
|
||||
'js/student_account/views/RegisterView.js',
|
||||
'js/student_account/views/PasswordResetView.js',
|
||||
'js/student_account/views/AccessView.js',
|
||||
'js/student_account/views/InstitutionLoginView.js',
|
||||
'js/student_account/accessApp.js',
|
||||
]
|
||||
|
||||
verify_student_js = [
|
||||
'js/sticky_filter.js',
|
||||
'js/query-params.js',
|
||||
@@ -1574,10 +1554,6 @@ PIPELINE_JS = {
|
||||
'source_filenames': dashboard_js,
|
||||
'output_filename': 'js/dashboard.js'
|
||||
},
|
||||
'student_account': {
|
||||
'source_filenames': student_account_js,
|
||||
'output_filename': 'js/student_account.js'
|
||||
},
|
||||
'verify_student': {
|
||||
'source_filenames': verify_student_js,
|
||||
'output_filename': 'js/verify_student.js'
|
||||
|
||||
@@ -75,16 +75,6 @@
|
||||
'js/views/file_uploader': 'js/views/file_uploader',
|
||||
'js/views/notification': 'js/views/notification',
|
||||
'js/student_account/account': 'js/student_account/account',
|
||||
'js/student_account/views/FormView': 'js/student_account/views/FormView',
|
||||
'js/student_account/models/LoginModel': 'js/student_account/models/LoginModel',
|
||||
'js/student_account/views/LoginView': 'js/student_account/views/LoginView',
|
||||
'js/student_account/views/InstitutionLoginView': 'js/student_account/views/InstitutionLoginView',
|
||||
'js/student_account/models/PasswordResetModel': 'js/student_account/models/PasswordResetModel',
|
||||
'js/student_account/views/PasswordResetView': 'js/student_account/views/PasswordResetView',
|
||||
'js/student_account/models/RegisterModel': 'js/student_account/models/RegisterModel',
|
||||
'js/student_account/views/RegisterView': 'js/student_account/views/RegisterView',
|
||||
'js/student_account/views/AccessView': 'js/student_account/views/AccessView',
|
||||
'js/student_account/views/HintedLoginView': 'js/student_account/views/HintedLoginView',
|
||||
'js/student_profile/views/learner_profile_fields': 'js/student_profile/views/learner_profile_fields',
|
||||
'js/student_profile/views/learner_profile_factory': 'js/student_profile/views/learner_profile_factory',
|
||||
'js/student_profile/views/learner_profile_view': 'js/student_profile/views/learner_profile_view',
|
||||
@@ -94,7 +84,10 @@
|
||||
'DiscussionModuleView': 'xmodule_js/common_static/coffee/src/discussion/discussion_module_view',
|
||||
|
||||
// edxnotes
|
||||
'annotator_1.2.9': 'xmodule_js/common_static/js/vendor/edxnotes/annotator-full.min'
|
||||
'annotator_1.2.9': 'xmodule_js/common_static/js/vendor/edxnotes/annotator-full.min',
|
||||
|
||||
// Common edx utils
|
||||
'js/utils/edx.utils.validate': 'xmodule_js/common_static/js/utils/edx.utils.validate'
|
||||
},
|
||||
shim: {
|
||||
'gettext': {
|
||||
@@ -337,91 +330,6 @@
|
||||
'js/models/notification', 'jquery.fileupload'
|
||||
]
|
||||
},
|
||||
// Student account registration/login
|
||||
// Loaded explicitly until these are converted to RequireJS
|
||||
'js/student_account/views/FormView': {
|
||||
exports: 'edx.student.account.FormView',
|
||||
deps: ['jquery', 'underscore', 'backbone', 'gettext']
|
||||
},
|
||||
'js/student_account/models/LoginModel': {
|
||||
exports: 'edx.student.account.LoginModel',
|
||||
deps: ['jquery', 'jquery.cookie', 'backbone']
|
||||
},
|
||||
'js/student_account/views/LoginView': {
|
||||
exports: 'edx.student.account.LoginView',
|
||||
deps: [
|
||||
'jquery',
|
||||
'jquery.url',
|
||||
'underscore',
|
||||
'gettext',
|
||||
'js/student_account/models/LoginModel',
|
||||
'js/student_account/views/FormView'
|
||||
]
|
||||
},
|
||||
'js/student_account/views/InstitutionLoginView': {
|
||||
exports: 'edx.student.account.InstitutionLoginView',
|
||||
deps: [
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone'
|
||||
]
|
||||
},
|
||||
'js/student_account/models/PasswordResetModel': {
|
||||
exports: 'edx.student.account.PasswordResetModel',
|
||||
deps: ['jquery', 'jquery.cookie', 'backbone']
|
||||
},
|
||||
'js/student_account/views/PasswordResetView': {
|
||||
exports: 'edx.student.account.PasswordResetView',
|
||||
deps: [
|
||||
'jquery',
|
||||
'underscore',
|
||||
'gettext',
|
||||
'js/student_account/models/PasswordResetModel',
|
||||
'js/student_account/views/FormView'
|
||||
]
|
||||
},
|
||||
'js/student_account/models/RegisterModel': {
|
||||
exports: 'edx.student.account.RegisterModel',
|
||||
deps: ['jquery', 'jquery.cookie', 'backbone']
|
||||
},
|
||||
'js/student_account/views/RegisterView': {
|
||||
exports: 'edx.student.account.RegisterView',
|
||||
deps: [
|
||||
'jquery',
|
||||
'jquery.url',
|
||||
'underscore',
|
||||
'gettext',
|
||||
'js/student_account/models/RegisterModel',
|
||||
'js/student_account/views/FormView'
|
||||
]
|
||||
},
|
||||
'js/student_account/views/HintedLoginView': {
|
||||
exports: 'edx.student.account.HintedLoginView',
|
||||
deps: [
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'gettext'
|
||||
]
|
||||
},
|
||||
'js/student_account/views/AccessView': {
|
||||
exports: 'edx.student.account.AccessView',
|
||||
deps: [
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'history',
|
||||
'utility',
|
||||
'js/student_account/views/LoginView',
|
||||
'js/student_account/views/PasswordResetView',
|
||||
'js/student_account/views/RegisterView',
|
||||
'js/student_account/views/InstitutionLoginView',
|
||||
'js/student_account/models/LoginModel',
|
||||
'js/student_account/models/PasswordResetModel',
|
||||
'js/student_account/models/RegisterModel',
|
||||
'js/student_account/views/FormView'
|
||||
]
|
||||
},
|
||||
'js/verify_student/models/verification_model': {
|
||||
exports: 'edx.verify_student.VerificationModel',
|
||||
deps: [ 'jquery', 'underscore', 'backbone', 'jquery.cookie' ]
|
||||
@@ -731,6 +639,7 @@
|
||||
'lms/include/js/spec/instructor_dashboard/student_admin_spec.js',
|
||||
'lms/include/js/spec/student_account/account_spec.js',
|
||||
'lms/include/js/spec/student_account/access_spec.js',
|
||||
'lms/include/js/spec/student_account/logistration_factory_spec.js',
|
||||
'lms/include/js/spec/student_account/finish_auth_spec.js',
|
||||
'lms/include/js/spec/student_account/hinted_login_spec.js',
|
||||
'lms/include/js/spec/student_account/login_spec.js',
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
define([
|
||||
'jquery',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/views/AccessView',
|
||||
'js/student_account/views/FormView',
|
||||
'js/student_account/enrollment',
|
||||
'js/student_account/shoppingcart',
|
||||
'js/student_account/emailoptin'
|
||||
], function($, TemplateHelpers, AjaxHelpers, AccessView, FormView, EnrollmentInterface, ShoppingCartInterface) {
|
||||
"use strict";
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/views/AccessView',
|
||||
'js/student_account/views/FormView',
|
||||
'js/student_account/enrollment',
|
||||
'js/student_account/shoppingcart',
|
||||
'js/student_account/emailoptin'
|
||||
],
|
||||
function($, _, Backbone, TemplateHelpers, AjaxHelpers, AccessView, FormView, EnrollmentInterface,
|
||||
ShoppingCartInterface) {
|
||||
|
||||
describe('edx.student.account.AccessView', function() {
|
||||
var requests = null,
|
||||
view = null,
|
||||
@@ -24,7 +30,7 @@ define([
|
||||
required: true,
|
||||
placeholder: 'xsy@edx.org',
|
||||
instructions: 'Enter your email here.',
|
||||
restrictions: {},
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
@@ -49,24 +55,27 @@ define([
|
||||
THIRD_PARTY_COMPLETE_URL = '/auth/complete/provider/';
|
||||
|
||||
var ajaxSpyAndInitialize = function(that, mode, nextUrl, finishAuthUrl) {
|
||||
var options = {
|
||||
initial_mode: mode,
|
||||
third_party_auth: {
|
||||
currentProvider: null,
|
||||
providers: [],
|
||||
secondaryProviders: [{name: "provider"}],
|
||||
finishAuthUrl: finishAuthUrl
|
||||
},
|
||||
login_redirect_url: nextUrl, // undefined for default
|
||||
platform_name: 'edX',
|
||||
login_form_desc: FORM_DESCRIPTION,
|
||||
registration_form_desc: FORM_DESCRIPTION,
|
||||
password_reset_form_desc: FORM_DESCRIPTION
|
||||
},
|
||||
$logistrationElement = $('#login-and-registration-container');
|
||||
|
||||
// Spy on AJAX requests
|
||||
requests = AjaxHelpers.requests(that);
|
||||
|
||||
// Initialize the access view
|
||||
view = new AccessView({
|
||||
mode: mode,
|
||||
thirdPartyAuth: {
|
||||
currentProvider: null,
|
||||
providers: [],
|
||||
secondaryProviders: [{name: "provider"}],
|
||||
finishAuthUrl: finishAuthUrl
|
||||
},
|
||||
nextUrl: nextUrl, // undefined for default
|
||||
platformName: 'edX',
|
||||
loginFormDesc: FORM_DESCRIPTION,
|
||||
registrationFormDesc: FORM_DESCRIPTION,
|
||||
passwordResetFormDesc: FORM_DESCRIPTION
|
||||
});
|
||||
view = new AccessView(_.extend(options, {el: $logistrationElement}));
|
||||
|
||||
// Mock the redirect call
|
||||
spyOn( view, 'redirect' ).andCallFake( function() {} );
|
||||
@@ -92,7 +101,7 @@ define([
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures('<div id="login-and-registration-container"></div>');
|
||||
setFixtures('<div id="login-and-registration-container" class="login-register" />');
|
||||
TemplateHelpers.installTemplate('templates/student_account/access');
|
||||
TemplateHelpers.installTemplate('templates/student_account/login');
|
||||
TemplateHelpers.installTemplate('templates/student_account/register');
|
||||
@@ -105,6 +114,10 @@ define([
|
||||
window.analytics = jasmine.createSpyObj('analytics', ['track', 'page', 'pageview', 'trackLink']);
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
Backbone.history.stop();
|
||||
});
|
||||
|
||||
it('can initially display the login form', function() {
|
||||
ajaxSpyAndInitialize(this, 'login');
|
||||
|
||||
@@ -217,5 +230,5 @@ define([
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
define([
|
||||
'jquery',
|
||||
'utility',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/views/FinishAuthView',
|
||||
'js/student_account/enrollment',
|
||||
'js/student_account/shoppingcart',
|
||||
'js/student_account/emailoptin'
|
||||
], function($, utility, AjaxHelpers, FinishAuthView, EnrollmentInterface, ShoppingCartInterface, EmailOptInInterface) {
|
||||
'use strict';
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'jquery.url',
|
||||
'utility',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/views/FinishAuthView',
|
||||
'js/student_account/enrollment',
|
||||
'js/student_account/shoppingcart',
|
||||
'js/student_account/emailoptin'
|
||||
],
|
||||
function($, url, utility, AjaxHelpers, FinishAuthView, EnrollmentInterface, ShoppingCartInterface,
|
||||
EmailOptInInterface) {
|
||||
|
||||
describe('FinishAuthView', function() {
|
||||
var requests = null,
|
||||
view = null,
|
||||
@@ -167,5 +172,5 @@ define([
|
||||
expect( view.redirect ).toHaveBeenCalledWith( "/dashboard" );
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,87 +1,90 @@
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/views/HintedLoginView',
|
||||
], function($, _, TemplateHelpers, AjaxHelpers, HintedLoginView) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
describe('edx.student.account.HintedLoginView', function() {
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/views/HintedLoginView'
|
||||
],
|
||||
function($, _, TemplateHelpers, AjaxHelpers, HintedLoginView) {
|
||||
|
||||
var view = null,
|
||||
requests = null,
|
||||
PLATFORM_NAME = 'edX',
|
||||
THIRD_PARTY_AUTH = {
|
||||
currentProvider: null,
|
||||
providers: [
|
||||
{
|
||||
id: 'oa2-google-oauth2',
|
||||
name: 'Google',
|
||||
iconClass: 'fa-google-plus',
|
||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
},
|
||||
{
|
||||
id: 'oa2-facebook',
|
||||
name: 'Facebook',
|
||||
iconClass: 'fa-facebook',
|
||||
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
|
||||
}
|
||||
],
|
||||
secondaryProviders: [
|
||||
{
|
||||
id: 'saml-harvard',
|
||||
name: 'Harvard',
|
||||
iconClass: 'fa-university',
|
||||
loginUrl: '/auth/login/tpa-saml/?auth_entry=account_login&idp=harvard',
|
||||
registerUrl: '/auth/login/tpa-saml/?auth_entry=account_register&idp=harvard'
|
||||
}
|
||||
]
|
||||
describe('edx.student.account.HintedLoginView', function() {
|
||||
var view = null,
|
||||
requests = null,
|
||||
PLATFORM_NAME = 'edX',
|
||||
THIRD_PARTY_AUTH = {
|
||||
currentProvider: null,
|
||||
providers: [
|
||||
{
|
||||
id: 'oa2-google-oauth2',
|
||||
name: 'Google',
|
||||
iconClass: 'fa-google-plus',
|
||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
},
|
||||
{
|
||||
id: 'oa2-facebook',
|
||||
name: 'Facebook',
|
||||
iconClass: 'fa-facebook',
|
||||
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
|
||||
}
|
||||
],
|
||||
secondaryProviders: [
|
||||
{
|
||||
id: 'saml-harvard',
|
||||
name: 'Harvard',
|
||||
iconClass: 'fa-university',
|
||||
loginUrl: '/auth/login/tpa-saml/?auth_entry=account_login&idp=harvard',
|
||||
registerUrl: '/auth/login/tpa-saml/?auth_entry=account_register&idp=harvard'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var createHintedLoginView = function(hintedProvider) {
|
||||
// Initialize the login view
|
||||
view = new HintedLoginView({
|
||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||
hintedProvider: hintedProvider,
|
||||
platformName: PLATFORM_NAME
|
||||
});
|
||||
|
||||
// Mock the redirect call
|
||||
spyOn( view, 'redirect' ).andCallFake( function() {} );
|
||||
|
||||
view.render();
|
||||
};
|
||||
|
||||
var createHintedLoginView = function(hintedProvider) {
|
||||
// Initialize the login view
|
||||
view = new HintedLoginView({
|
||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||
hintedProvider: hintedProvider,
|
||||
platformName: PLATFORM_NAME
|
||||
beforeEach(function() {
|
||||
setFixtures('<div id="hinted-login-form"></div>');
|
||||
TemplateHelpers.installTemplate('templates/student_account/hinted_login');
|
||||
});
|
||||
|
||||
// Mock the redirect call
|
||||
spyOn( view, 'redirect' ).andCallFake( function() {} );
|
||||
it('displays a choice as two buttons', function() {
|
||||
createHintedLoginView("oa2-google-oauth2");
|
||||
|
||||
view.render();
|
||||
};
|
||||
expect($('.proceed-button.button-oa2-google-oauth2')).toBeVisible();
|
||||
expect($('.form-toggle')).toBeVisible();
|
||||
expect($('.proceed-button.button-oa2-facebook')).not.toBeVisible();
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures('<div id="hinted-login-form"></div>');
|
||||
TemplateHelpers.installTemplate('templates/student_account/hinted_login');
|
||||
});
|
||||
it('works with secondary providers as well', function() {
|
||||
createHintedLoginView("saml-harvard");
|
||||
|
||||
it('displays a choice as two buttons', function() {
|
||||
createHintedLoginView("oa2-google-oauth2");
|
||||
expect($('.proceed-button.button-saml-harvard')).toBeVisible();
|
||||
expect($('.form-toggle')).toBeVisible();
|
||||
expect($('.proceed-button.button-oa2-google-oauth2')).not.toBeVisible();
|
||||
});
|
||||
|
||||
expect($('.proceed-button.button-oa2-google-oauth2')).toBeVisible();
|
||||
expect($('.form-toggle')).toBeVisible();
|
||||
expect($('.proceed-button.button-oa2-facebook')).not.toBeVisible();
|
||||
});
|
||||
it('redirects the user to the hinted provider if the user clicks the proceed button', function() {
|
||||
createHintedLoginView('oa2-google-oauth2');
|
||||
|
||||
it('works with secondary providers as well', function() {
|
||||
createHintedLoginView("saml-harvard");
|
||||
// Click the "Yes, proceed" button
|
||||
$('.proceed-button').click();
|
||||
|
||||
expect($('.proceed-button.button-saml-harvard')).toBeVisible();
|
||||
expect($('.form-toggle')).toBeVisible();
|
||||
expect($('.proceed-button.button-oa2-google-oauth2')).not.toBeVisible();
|
||||
});
|
||||
|
||||
it('redirects the user to the hinted provider if the user clicks the proceed button', function() {
|
||||
createHintedLoginView("oa2-google-oauth2");
|
||||
|
||||
// Click the "Yes, proceed" button
|
||||
$('.proceed-button').click();
|
||||
|
||||
expect(view.redirect).toHaveBeenCalledWith( '/auth/login/google-oauth2/?auth_entry=account_login' );
|
||||
expect(view.redirect).toHaveBeenCalledWith( '/auth/login/google-oauth2/?auth_entry=account_login' );
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,80 +1,87 @@
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'js/student_account/views/InstitutionLoginView',
|
||||
], function($, _, TemplateHelpers, InstitutionLoginView) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
describe('edx.student.account.InstitutionLoginView', function() {
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'js/student_account/views/InstitutionLoginView'
|
||||
],
|
||||
function($, _, TemplateHelpers, InstitutionLoginView) {
|
||||
|
||||
var view = null,
|
||||
PLATFORM_NAME = 'edX',
|
||||
THIRD_PARTY_AUTH = {
|
||||
currentProvider: null,
|
||||
providers: [],
|
||||
secondaryProviders: [
|
||||
{
|
||||
id: 'oa2-google-oauth2',
|
||||
name: 'Google',
|
||||
iconClass: 'fa-google-plus',
|
||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
},
|
||||
{
|
||||
id: 'oa2-facebook',
|
||||
name: 'Facebook',
|
||||
iconClass: 'fa-facebook',
|
||||
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
|
||||
}
|
||||
]
|
||||
describe('edx.student.account.InstitutionLoginView', function() {
|
||||
var view = null,
|
||||
PLATFORM_NAME = 'edX',
|
||||
THIRD_PARTY_AUTH = {
|
||||
currentProvider: null,
|
||||
providers: [],
|
||||
secondaryProviders: [
|
||||
{
|
||||
id: 'oa2-google-oauth2',
|
||||
name: 'Google',
|
||||
iconClass: 'fa-google-plus',
|
||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
},
|
||||
{
|
||||
id: 'oa2-facebook',
|
||||
name: 'Facebook',
|
||||
iconClass: 'fa-facebook',
|
||||
loginUrl: '/auth/login/facebook/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/facebook/?auth_entry=account_register'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var createInstLoginView = function(mode) {
|
||||
// Initialize the login view
|
||||
view = new InstitutionLoginView({
|
||||
mode: mode,
|
||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||
platformName: PLATFORM_NAME
|
||||
});
|
||||
view.render();
|
||||
};
|
||||
|
||||
var createInstLoginView = function(mode) {
|
||||
// Initialize the login view
|
||||
view = new InstitutionLoginView({
|
||||
mode: mode,
|
||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||
platformName: PLATFORM_NAME
|
||||
beforeEach(function() {
|
||||
setFixtures('<div id="institution_login-form"></div>');
|
||||
TemplateHelpers.installTemplate('templates/student_account/institution_login');
|
||||
TemplateHelpers.installTemplate('templates/student_account/institution_register');
|
||||
});
|
||||
|
||||
it('displays a list of providers', function() {
|
||||
var $google, $facebook;
|
||||
|
||||
createInstLoginView('login');
|
||||
expect($('#institution_login-form').html()).not.toBe("");
|
||||
$google = $('li a:contains("Google")');
|
||||
expect($google).toBeVisible();
|
||||
expect($google).toHaveAttr(
|
||||
'href', '/auth/login/google-oauth2/?auth_entry=account_login'
|
||||
);
|
||||
$facebook = $('li a:contains("Facebook")');
|
||||
expect($facebook).toBeVisible();
|
||||
expect($facebook).toHaveAttr(
|
||||
'href', '/auth/login/facebook/?auth_entry=account_login'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays a list of providers', function() {
|
||||
var $google, $facebook;
|
||||
|
||||
createInstLoginView('register');
|
||||
expect($('#institution_login-form').html()).not.toBe("");
|
||||
$google = $('li a:contains("Google")');
|
||||
expect($google).toBeVisible();
|
||||
expect($google).toHaveAttr(
|
||||
'href', '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
);
|
||||
$facebook = $('li a:contains("Facebook")');
|
||||
expect($facebook).toBeVisible();
|
||||
expect($facebook).toHaveAttr(
|
||||
'href', '/auth/login/facebook/?auth_entry=account_register'
|
||||
);
|
||||
});
|
||||
view.render();
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures('<div id="institution_login-form"></div>');
|
||||
TemplateHelpers.installTemplate('templates/student_account/institution_login');
|
||||
TemplateHelpers.installTemplate('templates/student_account/institution_register');
|
||||
});
|
||||
|
||||
it('displays a list of providers', function() {
|
||||
createInstLoginView('login');
|
||||
expect($('#institution_login-form').html()).not.toBe("");
|
||||
var $google = $('li a:contains("Google")');
|
||||
expect($google).toBeVisible();
|
||||
expect($google).toHaveAttr(
|
||||
'href', '/auth/login/google-oauth2/?auth_entry=account_login'
|
||||
);
|
||||
var $facebook = $('li a:contains("Facebook")');
|
||||
expect($facebook).toBeVisible();
|
||||
expect($facebook).toHaveAttr(
|
||||
'href', '/auth/login/facebook/?auth_entry=account_login'
|
||||
);
|
||||
});
|
||||
|
||||
it('displays a list of providers', function() {
|
||||
createInstLoginView('register');
|
||||
expect($('#institution_login-form').html()).not.toBe("");
|
||||
var $google = $('li a:contains("Google")');
|
||||
expect($google).toBeVisible();
|
||||
expect($google).toHaveAttr(
|
||||
'href', '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
);
|
||||
var $facebook = $('li a:contains("Facebook")');
|
||||
expect($facebook).toBeVisible();
|
||||
expect($facebook).toHaveAttr(
|
||||
'href', '/auth/login/facebook/?auth_entry=account_register'
|
||||
);
|
||||
});
|
||||
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,265 +1,270 @@
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/models/LoginModel',
|
||||
'js/student_account/views/LoginView',
|
||||
'js/student_account/models/PasswordResetModel'
|
||||
], function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView, PasswordResetModel) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
describe('edx.student.account.LoginView', function() {
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/models/LoginModel',
|
||||
'js/student_account/views/LoginView',
|
||||
'js/student_account/models/PasswordResetModel'
|
||||
],
|
||||
function($, _, TemplateHelpers, AjaxHelpers, LoginModel, LoginView, PasswordResetModel) {
|
||||
|
||||
var model = null,
|
||||
resetModel = null,
|
||||
view = null,
|
||||
requests = null,
|
||||
authComplete = false,
|
||||
PLATFORM_NAME = 'edX',
|
||||
USER_DATA = {
|
||||
email: 'xsy@edx.org',
|
||||
password: 'xsyisawesome',
|
||||
remember: true
|
||||
},
|
||||
THIRD_PARTY_AUTH = {
|
||||
currentProvider: null,
|
||||
providers: [
|
||||
{
|
||||
id: 'oa2-google-oauth2',
|
||||
name: 'Google',
|
||||
iconClass: 'fa-google-plus',
|
||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
},
|
||||
{
|
||||
id: 'oa2-facebook',
|
||||
name: 'Facebook',
|
||||
iconClass: 'fa-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/login_session/',
|
||||
fields: [
|
||||
{
|
||||
placeholder: 'username@domain.com',
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
defaultValue: '',
|
||||
type: 'email',
|
||||
required: true,
|
||||
instructions: 'Enter your email.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'password',
|
||||
label: 'Password',
|
||||
defaultValue: '',
|
||||
type: 'password',
|
||||
required: true,
|
||||
instructions: 'Enter your password.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'remember',
|
||||
label: 'Remember me',
|
||||
defaultValue: '',
|
||||
type: 'checkbox',
|
||||
required: true,
|
||||
instructions: "Agree to the terms of service.",
|
||||
restrictions: {}
|
||||
}
|
||||
]
|
||||
},
|
||||
COURSE_ID = "edX/demoX/Fall";
|
||||
describe('edx.student.account.LoginView', function() {
|
||||
var model = null,
|
||||
resetModel = null,
|
||||
view = null,
|
||||
requests = null,
|
||||
authComplete = false,
|
||||
PLATFORM_NAME = 'edX',
|
||||
USER_DATA = {
|
||||
email: 'xsy@edx.org',
|
||||
password: 'xsyisawesome',
|
||||
remember: true
|
||||
},
|
||||
THIRD_PARTY_AUTH = {
|
||||
currentProvider: null,
|
||||
providers: [
|
||||
{
|
||||
id: 'oa2-google-oauth2',
|
||||
name: 'Google',
|
||||
iconClass: 'fa-google-plus',
|
||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
},
|
||||
{
|
||||
id: 'oa2-facebook',
|
||||
name: 'Facebook',
|
||||
iconClass: 'fa-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/login_session/',
|
||||
fields: [
|
||||
{
|
||||
placeholder: 'username@domain.com',
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
defaultValue: '',
|
||||
type: 'email',
|
||||
required: true,
|
||||
instructions: 'Enter your email.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'password',
|
||||
label: 'Password',
|
||||
defaultValue: '',
|
||||
type: 'password',
|
||||
required: true,
|
||||
instructions: 'Enter your password.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'remember',
|
||||
label: 'Remember me',
|
||||
defaultValue: '',
|
||||
type: 'checkbox',
|
||||
required: true,
|
||||
instructions: 'Agree to the terms of service.',
|
||||
restrictions: {}
|
||||
}
|
||||
]
|
||||
},
|
||||
COURSE_ID = 'edX/demoX/Fall';
|
||||
|
||||
var createLoginView = function(test) {
|
||||
// Initialize the login model
|
||||
model = new LoginModel({}, {
|
||||
url: FORM_DESCRIPTION.submit_url,
|
||||
method: FORM_DESCRIPTION.method
|
||||
});
|
||||
|
||||
// Initialize the passwordReset model
|
||||
resetModel = new PasswordResetModel({}, {
|
||||
method: 'GET',
|
||||
url: '#'
|
||||
});
|
||||
|
||||
// Initialize the login view
|
||||
view = new LoginView({
|
||||
fields: FORM_DESCRIPTION.fields,
|
||||
model: model,
|
||||
resetModel: resetModel,
|
||||
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.'
|
||||
var createLoginView = function(test) {
|
||||
// Initialize the login model
|
||||
model = new LoginModel({}, {
|
||||
url: FORM_DESCRIPTION.submit_url,
|
||||
method: FORM_DESCRIPTION.method
|
||||
});
|
||||
}
|
||||
|
||||
// Submit the email address
|
||||
view.submitForm(clickEvent);
|
||||
};
|
||||
// Initialize the passwordReset model
|
||||
resetModel = new PasswordResetModel({}, {
|
||||
method: 'GET',
|
||||
url: '#'
|
||||
});
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures('<div id="login-form"></div>');
|
||||
TemplateHelpers.installTemplate('templates/student_account/login');
|
||||
TemplateHelpers.installTemplate('templates/student_account/form_field');
|
||||
});
|
||||
// Initialize the login view
|
||||
view = new LoginView({
|
||||
fields: FORM_DESCRIPTION.fields,
|
||||
model: model,
|
||||
resetModel: resetModel,
|
||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||
platformName: PLATFORM_NAME
|
||||
});
|
||||
|
||||
it('logs the user in', function() {
|
||||
createLoginView(this);
|
||||
// Spy on AJAX requests
|
||||
requests = AjaxHelpers.requests(test);
|
||||
|
||||
// Submit the form, with successful validation
|
||||
submitForm(true);
|
||||
// Intercept events from the view
|
||||
authComplete = false;
|
||||
view.on("auth-complete", function() {
|
||||
authComplete = true;
|
||||
});
|
||||
};
|
||||
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
var submitForm = function(validationSuccess) {
|
||||
// Create a fake click event
|
||||
var clickEvent = $.Event('click');
|
||||
|
||||
// Verify that the client contacts the server with the expected data
|
||||
AjaxHelpers.expectRequest(
|
||||
requests, 'POST',
|
||||
FORM_DESCRIPTION.submit_url,
|
||||
$.param(USER_DATA)
|
||||
);
|
||||
// Simulate manual entry of login form data
|
||||
$('#login-email').val(USER_DATA.email);
|
||||
$('#login-password').val(USER_DATA.password);
|
||||
|
||||
// Respond with status code 200
|
||||
AjaxHelpers.respondWithJson(requests, {});
|
||||
// Check the 'Remember me' checkbox
|
||||
$('#login-remember').prop('checked', USER_DATA.remember);
|
||||
|
||||
// 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 );
|
||||
// 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');
|
||||
});
|
||||
|
||||
// Attempt to login
|
||||
submitForm( true );
|
||||
it('logs the user in', function() {
|
||||
createLoginView(this);
|
||||
|
||||
// Verify that the client sent the course ID for analytics
|
||||
var expectedData = {};
|
||||
$.extend(expectedData, USER_DATA, {
|
||||
analytics: JSON.stringify({
|
||||
enroll_course_id: COURSE_ID
|
||||
})
|
||||
// Submit the form, with successful validation
|
||||
submitForm(true);
|
||||
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
|
||||
// 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);
|
||||
});
|
||||
|
||||
AjaxHelpers.expectRequest(
|
||||
requests, 'POST',
|
||||
FORM_DESCRIPTION.submit_url,
|
||||
$.param( expectedData )
|
||||
);
|
||||
});
|
||||
it('sends analytics info containing the enrolled course ID', function() {
|
||||
var expectedData;
|
||||
|
||||
it('displays third-party auth login buttons', function() {
|
||||
createLoginView(this);
|
||||
createLoginView(this);
|
||||
|
||||
// Verify that Google and Facebook registration buttons are displayed
|
||||
expect($('.button-oa2-google-oauth2')).toBeVisible();
|
||||
expect($('.button-oa2-facebook')).toBeVisible();
|
||||
});
|
||||
// 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 );
|
||||
}
|
||||
});
|
||||
|
||||
it('displays a link to the password reset form', function() {
|
||||
createLoginView(this);
|
||||
// Attempt to login
|
||||
submitForm( true );
|
||||
|
||||
// Verify that the password reset link is displayed
|
||||
expect($('.forgot-password')).toBeVisible();
|
||||
});
|
||||
// Verify that the client sent the course ID for analytics
|
||||
expectedData = {};
|
||||
$.extend(expectedData, USER_DATA, {
|
||||
analytics: JSON.stringify({
|
||||
enroll_course_id: COURSE_ID
|
||||
})
|
||||
});
|
||||
|
||||
it('validates login form fields', function() {
|
||||
createLoginView(this);
|
||||
AjaxHelpers.expectRequest(
|
||||
requests, 'POST',
|
||||
FORM_DESCRIPTION.submit_url,
|
||||
$.param( expectedData )
|
||||
);
|
||||
});
|
||||
|
||||
submitForm(true);
|
||||
it('displays third-party auth login buttons', function() {
|
||||
createLoginView(this);
|
||||
|
||||
// Verify that validation of form fields occurred
|
||||
expect(view.validate).toHaveBeenCalledWith($('#login-email')[0]);
|
||||
expect(view.validate).toHaveBeenCalledWith($('#login-password')[0]);
|
||||
});
|
||||
// Verify that Google and Facebook registration buttons are displayed
|
||||
expect($('.button-oa2-google-oauth2')).toBeVisible();
|
||||
expect($('.button-oa2-facebook')).toBeVisible();
|
||||
});
|
||||
|
||||
it('displays login form validation errors', function() {
|
||||
createLoginView(this);
|
||||
it('displays a link to the password reset form', function() {
|
||||
createLoginView(this);
|
||||
|
||||
// Submit the form, with failed validation
|
||||
submitForm(false);
|
||||
// Verify that the password reset link is displayed
|
||||
expect($('.forgot-password')).toBeVisible();
|
||||
});
|
||||
|
||||
// Verify that submission errors are visible
|
||||
expect(view.$errors).not.toHaveClass('hidden');
|
||||
it('validates login form fields', function() {
|
||||
createLoginView(this);
|
||||
|
||||
// Expect auth complete NOT to have been triggered
|
||||
expect(authComplete).toBe(false);
|
||||
// Form button should be re-enabled when errors occur
|
||||
expect(view.$submitButton).not.toHaveAttr('disabled');
|
||||
});
|
||||
submitForm(true);
|
||||
|
||||
it('displays an error if the server returns an error while logging in', function() {
|
||||
createLoginView(this);
|
||||
// Verify that validation of form fields occurred
|
||||
expect(view.validate).toHaveBeenCalledWith($('#login-email')[0]);
|
||||
expect(view.validate).toHaveBeenCalledWith($('#login-password')[0]);
|
||||
});
|
||||
|
||||
// Submit the form, with successful validation
|
||||
submitForm(true);
|
||||
it('displays login form validation errors', function() {
|
||||
createLoginView(this);
|
||||
|
||||
// Simulate an error from the LMS servers
|
||||
AjaxHelpers.respondWithError(requests);
|
||||
// Submit the form, with failed validation
|
||||
submitForm(false);
|
||||
|
||||
// Expect that an error is displayed and that auth complete is not triggered
|
||||
expect(view.$errors).not.toHaveClass('hidden');
|
||||
expect(authComplete).toBe(false);
|
||||
// Form button should be re-enabled on server failure.
|
||||
expect(view.$submitButton).not.toHaveAttr('disabled');
|
||||
// Verify that submission errors are visible
|
||||
expect(view.$errors).not.toHaveClass('hidden');
|
||||
|
||||
// If we try again and succeed, the error should go away
|
||||
submitForm();
|
||||
// Expect auth complete NOT to have been triggered
|
||||
expect(authComplete).toBe(false);
|
||||
// Form button should be re-enabled when errors occur
|
||||
expect(view.$submitButton).not.toHaveAttr('disabled');
|
||||
});
|
||||
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
it('displays an error if the server returns an error while logging in', function() {
|
||||
createLoginView(this);
|
||||
|
||||
// This time, respond with status code 200
|
||||
AjaxHelpers.respondWithJson(requests, {});
|
||||
// Submit the form, with successful validation
|
||||
submitForm(true);
|
||||
|
||||
// Expect that the error is hidden and auth complete is triggered
|
||||
expect(view.$errors).toHaveClass('hidden');
|
||||
expect(authComplete).toBe(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);
|
||||
// Form button should be re-enabled on server failure.
|
||||
expect(view.$submitButton).not.toHaveAttr('disabled');
|
||||
|
||||
// If we try again and succeed, the error should go away
|
||||
submitForm();
|
||||
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
|
||||
// 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
130
lms/static/js/spec/student_account/logistration_factory_spec.js
Normal file
130
lms/static/js/spec/student_account/logistration_factory_spec.js
Normal file
@@ -0,0 +1,130 @@
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/logistration_factory'
|
||||
],
|
||||
function($, _, Backbone, TemplateHelpers, AjaxHelpers, LogistrationFactory) {
|
||||
|
||||
describe('Logistration Factory', function() {
|
||||
var FORM_DESCRIPTION = {
|
||||
method: 'post',
|
||||
submit_url: '/submit',
|
||||
fields: [
|
||||
{
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
defaultValue: '',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: 'xsy@edx.org',
|
||||
instructions: 'Enter your email here.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
name: 'username',
|
||||
label: 'Username',
|
||||
defaultValue: '',
|
||||
type: 'text',
|
||||
required: true,
|
||||
placeholder: 'Xsy',
|
||||
instructions: 'Enter your username here.',
|
||||
restrictions: {
|
||||
max_length: 200
|
||||
}
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
var initializeLogistrationFactory = function(that, mode, nextUrl, finishAuthUrl) {
|
||||
var options = {
|
||||
initial_mode: mode,
|
||||
third_party_auth: {
|
||||
currentProvider: null,
|
||||
providers: [],
|
||||
secondaryProviders: [{name: 'provider'}],
|
||||
finishAuthUrl: finishAuthUrl
|
||||
},
|
||||
login_redirect_url: nextUrl, // undefined for default
|
||||
platform_name: 'edX',
|
||||
login_form_desc: FORM_DESCRIPTION,
|
||||
registration_form_desc: FORM_DESCRIPTION,
|
||||
password_reset_form_desc: FORM_DESCRIPTION
|
||||
};
|
||||
|
||||
// Initialize the logistration Factory
|
||||
LogistrationFactory(options);
|
||||
};
|
||||
|
||||
var assertForms = function(visibleForm, hiddenFormsList) {
|
||||
expect($(visibleForm)).not.toHaveClass('hidden');
|
||||
|
||||
_.each(hiddenFormsList, function (hiddenForm) {
|
||||
expect($(hiddenForm)).toHaveClass('hidden');
|
||||
}, this);
|
||||
};
|
||||
|
||||
beforeEach(function() {
|
||||
setFixtures('<div id="login-and-registration-container" class="login-register" />');
|
||||
TemplateHelpers.installTemplate('templates/student_account/access');
|
||||
TemplateHelpers.installTemplate('templates/student_account/form_field');
|
||||
TemplateHelpers.installTemplate('templates/student_account/login');
|
||||
TemplateHelpers.installTemplate('templates/student_account/register');
|
||||
TemplateHelpers.installTemplate('templates/student_account/password_reset')
|
||||
});
|
||||
|
||||
afterEach(function() {
|
||||
Backbone.history.stop();
|
||||
});
|
||||
|
||||
it('can initially render the login form', function() {
|
||||
var hiddenFormsList;
|
||||
|
||||
initializeLogistrationFactory(this, 'login');
|
||||
|
||||
/* Verify that only login form is expanded, and that the
|
||||
/* all other logistration forms are collapsed.
|
||||
*/
|
||||
hiddenFormsList = [
|
||||
'#register-form',
|
||||
'#password-reset-form'
|
||||
];
|
||||
assertForms('#login-form', hiddenFormsList);
|
||||
});
|
||||
|
||||
it('can initially render the registration form', function() {
|
||||
var hiddenFormsList;
|
||||
|
||||
initializeLogistrationFactory(this, 'register');
|
||||
|
||||
/* Verify that only registration form is expanded, and that the
|
||||
/* all other logistration forms are collapsed.
|
||||
*/
|
||||
hiddenFormsList = [
|
||||
'#login-form',
|
||||
'#password-reset-form'
|
||||
];
|
||||
assertForms('#register-form', hiddenFormsList);
|
||||
});
|
||||
|
||||
it('can initially render the password reset form', function() {
|
||||
var hiddenFormsList;
|
||||
|
||||
initializeLogistrationFactory(this, 'reset');
|
||||
|
||||
/* Verify that only password reset form is expanded, and that the
|
||||
/* all other logistration forms are collapsed.
|
||||
*/
|
||||
hiddenFormsList = [
|
||||
'#login-form',
|
||||
'#register-form'
|
||||
];
|
||||
assertForms('#password-reset-form', hiddenFormsList);
|
||||
});
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,14 +1,16 @@
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/models/PasswordResetModel',
|
||||
'js/student_account/views/PasswordResetView',
|
||||
], function($, _, TemplateHelpers, AjaxHelpers, PasswordResetModel, PasswordResetView) {
|
||||
describe('edx.student.account.PasswordResetView', function() {
|
||||
'use strict';
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/models/PasswordResetModel',
|
||||
'js/student_account/views/PasswordResetView'
|
||||
],
|
||||
function($, _, TemplateHelpers, AjaxHelpers, PasswordResetModel, PasswordResetView) {
|
||||
|
||||
describe('edx.student.account.PasswordResetView', function() {
|
||||
var model = null,
|
||||
view = null,
|
||||
requests = null,
|
||||
@@ -46,12 +48,12 @@ define([
|
||||
};
|
||||
|
||||
var submitEmail = function(validationSuccess) {
|
||||
// Simulate manual entry of an email address
|
||||
$('#password-reset-email').val(EMAIL);
|
||||
|
||||
// Create a fake click event
|
||||
var clickEvent = $.Event('click');
|
||||
|
||||
// Simulate manual entry of an email address
|
||||
$('#password-reset-email').val(EMAIL);
|
||||
|
||||
// If validationSuccess isn't passed, we avoid
|
||||
// spying on `view.validate` twice
|
||||
if ( !_.isUndefined(validationSuccess) ) {
|
||||
@@ -141,5 +143,5 @@ define([
|
||||
expect(view.$errors).toHaveClass('hidden');
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,352 +1,356 @@
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/models/RegisterModel',
|
||||
'js/student_account/views/RegisterView'
|
||||
], function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'common/js/spec_helpers/ajax_helpers',
|
||||
'js/student_account/models/RegisterModel',
|
||||
'js/student_account/views/RegisterView'
|
||||
],
|
||||
function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) {
|
||||
|
||||
describe('edx.student.account.RegisterView', function() {
|
||||
describe('edx.student.account.RegisterView', function() {
|
||||
var model = null,
|
||||
view = null,
|
||||
requests = null,
|
||||
authComplete = false,
|
||||
PLATFORM_NAME = 'edX',
|
||||
COURSE_ID = 'edX/DemoX/Fall',
|
||||
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: [
|
||||
{
|
||||
id: 'oa2-google-oauth2',
|
||||
name: 'Google',
|
||||
iconClass: 'fa-google-plus',
|
||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
},
|
||||
{
|
||||
id: 'oa2-facebook',
|
||||
name: 'Facebook',
|
||||
iconClass: 'fa-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: [
|
||||
{
|
||||
placeholder: 'username@domain.com',
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
defaultValue: '',
|
||||
type: 'email',
|
||||
required: true,
|
||||
instructions: 'Enter your email.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: 'Jane Doe',
|
||||
name: 'name',
|
||||
label: 'Full Name',
|
||||
defaultValue: '',
|
||||
type: 'text',
|
||||
required: true,
|
||||
instructions: 'Enter your username.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: 'JaneDoe',
|
||||
name: 'username',
|
||||
label: 'Username',
|
||||
defaultValue: '',
|
||||
type: 'text',
|
||||
required: true,
|
||||
instructions: 'Enter your username.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'password',
|
||||
label: 'Password',
|
||||
defaultValue: '',
|
||||
type: 'password',
|
||||
required: true,
|
||||
instructions: 'Enter your password.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
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: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
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: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
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: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'mailing_address',
|
||||
label: 'Mailing Address',
|
||||
defaultValue: '',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
instructions: 'Enter your mailing address.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'goals',
|
||||
label: 'Goals',
|
||||
defaultValue: '',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
instructions: "If you'd like, tell us why you're interested in edX.",
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
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 model = null,
|
||||
view = null,
|
||||
requests = null,
|
||||
authComplete = false,
|
||||
PLATFORM_NAME = 'edX',
|
||||
COURSE_ID = "edX/DemoX/Fall",
|
||||
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: [
|
||||
{
|
||||
id: 'oa2-google-oauth2',
|
||||
name: 'Google',
|
||||
iconClass: 'fa-google-plus',
|
||||
loginUrl: '/auth/login/google-oauth2/?auth_entry=account_login',
|
||||
registerUrl: '/auth/login/google-oauth2/?auth_entry=account_register'
|
||||
},
|
||||
{
|
||||
id: 'oa2-facebook',
|
||||
name: 'Facebook',
|
||||
iconClass: 'fa-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: [
|
||||
{
|
||||
placeholder: 'username@domain.com',
|
||||
name: 'email',
|
||||
label: 'Email',
|
||||
defaultValue: '',
|
||||
type: 'email',
|
||||
required: true,
|
||||
instructions: 'Enter your email.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: 'Jane Doe',
|
||||
name: 'name',
|
||||
label: 'Full Name',
|
||||
defaultValue: '',
|
||||
type: 'text',
|
||||
required: true,
|
||||
instructions: 'Enter your username.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: 'JaneDoe',
|
||||
name: 'username',
|
||||
label: 'Username',
|
||||
defaultValue: '',
|
||||
type: 'text',
|
||||
required: true,
|
||||
instructions: 'Enter your username.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'password',
|
||||
label: 'Password',
|
||||
defaultValue: '',
|
||||
type: 'password',
|
||||
required: true,
|
||||
instructions: 'Enter your password.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
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: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
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: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
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: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'mailing_address',
|
||||
label: 'Mailing Address',
|
||||
defaultValue: '',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
instructions: 'Enter your mailing address.',
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
name: 'goals',
|
||||
label: 'Goals',
|
||||
defaultValue: '',
|
||||
type: 'textarea',
|
||||
required: false,
|
||||
instructions: "If you'd like, tell us why you're interested in edX.",
|
||||
restrictions: {}
|
||||
},
|
||||
{
|
||||
placeholder: '',
|
||||
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) {
|
||||
// Initialize the register model
|
||||
model = new RegisterModel({}, {
|
||||
url: FORM_DESCRIPTION.submit_url,
|
||||
method: FORM_DESCRIPTION.method
|
||||
});
|
||||
|
||||
// Initialize the register view
|
||||
view = new RegisterView({
|
||||
fields: FORM_DESCRIPTION.fields,
|
||||
model: model,
|
||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||
platformName: PLATFORM_NAME
|
||||
});
|
||||
|
||||
// Spy on AJAX requests
|
||||
requests = AjaxHelpers.requests(that);
|
||||
|
||||
// Intercept events from the view
|
||||
authComplete = false;
|
||||
view.on("auth-complete", function() {
|
||||
authComplete = true;
|
||||
});
|
||||
};
|
||||
|
||||
var createRegisterView = function(that) {
|
||||
// Initialize the register model
|
||||
model = new RegisterModel({}, {
|
||||
url: FORM_DESCRIPTION.submit_url,
|
||||
method: FORM_DESCRIPTION.method
|
||||
});
|
||||
var submitForm = function(validationSuccess) {
|
||||
// Create a fake click event
|
||||
var clickEvent = $.Event('click');
|
||||
|
||||
// Initialize the register view
|
||||
view = new RegisterView({
|
||||
fields: FORM_DESCRIPTION.fields,
|
||||
model: model,
|
||||
thirdPartyAuth: THIRD_PARTY_AUTH,
|
||||
platformName: PLATFORM_NAME
|
||||
});
|
||||
// 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);
|
||||
|
||||
// Spy on AJAX requests
|
||||
requests = AjaxHelpers.requests(that);
|
||||
// Check the honor code checkbox
|
||||
$('#register-honor_code').prop('checked', USER_DATA.honor_code);
|
||||
|
||||
// Intercept events from the view
|
||||
authComplete = false;
|
||||
view.on("auth-complete", function() {
|
||||
authComplete = true;
|
||||
});
|
||||
};
|
||||
|
||||
var submitForm = function(validationSuccess) {
|
||||
// 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);
|
||||
|
||||
// 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() {
|
||||
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);
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
});
|
||||
|
||||
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 );
|
||||
// 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');
|
||||
});
|
||||
|
||||
// Attempt to register
|
||||
submitForm( true );
|
||||
it('registers a new user', function() {
|
||||
createRegisterView(this);
|
||||
|
||||
// Verify that the client sent the course ID for analytics
|
||||
var expectedData = {course_id: COURSE_ID};
|
||||
$.extend(expectedData, USER_DATA);
|
||||
// Submit the form, with successful validation
|
||||
submitForm(true);
|
||||
|
||||
AjaxHelpers.expectRequest(
|
||||
requests, 'POST',
|
||||
FORM_DESCRIPTION.submit_url,
|
||||
$.param( expectedData )
|
||||
);
|
||||
});
|
||||
// Verify that the client contacts the server with the expected data
|
||||
AjaxHelpers.expectRequest(
|
||||
requests, 'POST',
|
||||
FORM_DESCRIPTION.submit_url,
|
||||
$.param( USER_DATA )
|
||||
);
|
||||
|
||||
it('displays third-party auth registration buttons', function() {
|
||||
createRegisterView(this);
|
||||
// Respond with status code 200
|
||||
AjaxHelpers.respondWithJson(requests, {});
|
||||
|
||||
// Verify that Google and Facebook registration buttons are displayed
|
||||
expect($('.button-oa2-google-oauth2')).toBeVisible();
|
||||
expect($('.button-oa2-facebook')).toBeVisible();
|
||||
});
|
||||
// Verify that auth complete is triggered
|
||||
expect(authComplete).toBe(true);
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
});
|
||||
|
||||
it('validates registration form fields', function() {
|
||||
createRegisterView(this);
|
||||
it('sends analytics info containing the enrolled course ID', function() {
|
||||
var expectedData;
|
||||
|
||||
// Submit the form, with successful validation
|
||||
submitForm(true);
|
||||
createRegisterView(this);
|
||||
|
||||
// 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]);
|
||||
// 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 );
|
||||
}
|
||||
});
|
||||
|
||||
// Verify that no submission errors are visible
|
||||
expect(view.$errors).toHaveClass('hidden');
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
});
|
||||
// Attempt to register
|
||||
submitForm( true );
|
||||
|
||||
it('displays registration form validation errors', function() {
|
||||
createRegisterView(this);
|
||||
// Verify that the client sent the course ID for analytics
|
||||
expectedData = {course_id: COURSE_ID};
|
||||
$.extend(expectedData, USER_DATA);
|
||||
|
||||
// Submit the form, with failed validation
|
||||
submitForm(false);
|
||||
AjaxHelpers.expectRequest(
|
||||
requests, 'POST',
|
||||
FORM_DESCRIPTION.submit_url,
|
||||
$.param( expectedData )
|
||||
);
|
||||
});
|
||||
|
||||
// Verify that submission errors are visible
|
||||
expect(view.$errors).not.toHaveClass('hidden');
|
||||
it('displays third-party auth registration buttons', function() {
|
||||
createRegisterView(this);
|
||||
|
||||
// Expect that auth complete is NOT triggered
|
||||
expect(authComplete).toBe(false);
|
||||
// Form button should be re-enabled on error.
|
||||
expect(view.$submitButton).not.toHaveAttr('disabled');
|
||||
});
|
||||
// Verify that Google and Facebook registration buttons are displayed
|
||||
expect($('.button-oa2-google-oauth2')).toBeVisible();
|
||||
expect($('.button-oa2-facebook')).toBeVisible();
|
||||
});
|
||||
|
||||
it('displays an error if the server returns an error while registering', function() {
|
||||
createRegisterView(this);
|
||||
it('validates registration form fields', function() {
|
||||
createRegisterView(this);
|
||||
|
||||
// Submit the form, with successful validation
|
||||
submitForm(true);
|
||||
// Submit the form, with successful validation
|
||||
submitForm(true);
|
||||
|
||||
// Simulate an error from the LMS servers
|
||||
AjaxHelpers.respondWithError(requests);
|
||||
// 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]);
|
||||
|
||||
// Expect that an error is displayed and that auth complete is NOT triggered
|
||||
expect(view.$errors).not.toHaveClass('hidden');
|
||||
expect(authComplete).toBe(false);
|
||||
// Verify that no submission errors are visible
|
||||
expect(view.$errors).toHaveClass('hidden');
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
});
|
||||
|
||||
// If we try again and succeed, the error should go away
|
||||
submitForm();
|
||||
it('displays registration form validation errors', function() {
|
||||
createRegisterView(this);
|
||||
|
||||
// This time, respond with status code 200
|
||||
AjaxHelpers.respondWithJson(requests, {});
|
||||
// Submit the form, with failed validation
|
||||
submitForm(false);
|
||||
|
||||
// Expect that the error is hidden and that auth complete is triggered
|
||||
expect(view.$errors).toHaveClass('hidden');
|
||||
expect(authComplete).toBe(true);
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
// Verify that submission errors are visible
|
||||
expect(view.$errors).not.toHaveClass('hidden');
|
||||
|
||||
// Expect that auth complete is NOT triggered
|
||||
expect(authComplete).toBe(false);
|
||||
// Form button should be re-enabled on error.
|
||||
expect(view.$submitButton).not.toHaveAttr('disabled');
|
||||
});
|
||||
|
||||
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);
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($) {
|
||||
'use strict';
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
|
||||
var container = $('#login-and-registration-container');
|
||||
|
||||
return new edx.student.account.AccessView({
|
||||
mode: container.data('initial-mode'),
|
||||
thirdPartyAuth: container.data('third-party-auth'),
|
||||
thirdPartyAuthHint: container.data('third-party-auth-hint'),
|
||||
nextUrl: container.data('next-url'),
|
||||
platformName: container.data('platform-name'),
|
||||
loginFormDesc: container.data('login-form-desc'),
|
||||
registrationFormDesc: container.data('registration-form-desc'),
|
||||
passwordResetFormDesc: container.data('password-reset-form-desc')
|
||||
});
|
||||
})(jQuery);
|
||||
15
lms/static/js/student_account/logistration_factory.js
Normal file
15
lms/static/js/student_account/logistration_factory.js
Normal file
@@ -0,0 +1,15 @@
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'js/student_account/views/AccessView'
|
||||
],
|
||||
function($, AccessView) {
|
||||
return function(options) {
|
||||
var $logistrationElement = $('#login-and-registration-container');
|
||||
|
||||
new AccessView(_.extend(options, {el: $logistrationElement}));
|
||||
};
|
||||
}
|
||||
);
|
||||
}).call(this, define || RequireJS.define);
|
||||
@@ -1,58 +1,57 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, Backbone) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'backbone',
|
||||
'jquery.url'
|
||||
], function ($, Backbone) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return Backbone.Model.extend({
|
||||
defaults: {
|
||||
email: '',
|
||||
password: '',
|
||||
remember: false
|
||||
},
|
||||
|
||||
edx.student.account.LoginModel = Backbone.Model.extend({
|
||||
ajaxType: '',
|
||||
urlRoot: '',
|
||||
|
||||
defaults: {
|
||||
email: '',
|
||||
password: '',
|
||||
remember: false
|
||||
},
|
||||
initialize: function (attributes, options) {
|
||||
this.ajaxType = options.method;
|
||||
this.urlRoot = options.url;
|
||||
},
|
||||
|
||||
ajaxType: '',
|
||||
sync: function (method, model) {
|
||||
var headers = {'X-CSRFToken': $.cookie('csrftoken')},
|
||||
data = {},
|
||||
analytics,
|
||||
courseId = $.url('?course_id');
|
||||
|
||||
urlRoot: '',
|
||||
// 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)
|
||||
});
|
||||
}
|
||||
|
||||
initialize: function( attributes, options ) {
|
||||
this.ajaxType = options.method;
|
||||
this.urlRoot = options.url;
|
||||
},
|
||||
// Include all form fields and analytics info in the data sent to the server
|
||||
$.extend(data, model.attributes, {analytics: analytics});
|
||||
|
||||
sync: function(method, model) {
|
||||
var headers = { '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 )
|
||||
$.ajax({
|
||||
url: model.urlRoot,
|
||||
type: model.ajaxType,
|
||||
data: data,
|
||||
headers: headers,
|
||||
success: function () {
|
||||
model.trigger('sync');
|
||||
},
|
||||
error: function (error) {
|
||||
model.trigger('error', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Include all form fields and analytics info in the data sent to the server
|
||||
$.extend( data, model.attributes, { analytics: analytics });
|
||||
|
||||
$.ajax({
|
||||
url: model.urlRoot,
|
||||
type: model.ajaxType,
|
||||
data: data,
|
||||
headers: headers,
|
||||
success: function() {
|
||||
model.trigger('sync');
|
||||
},
|
||||
error: function( error ) {
|
||||
model.trigger('error', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery, Backbone);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,44 +1,39 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, Backbone) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define(['jquery', 'backbone'],
|
||||
function($, Backbone) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return Backbone.Model.extend({
|
||||
defaults: {
|
||||
email: ''
|
||||
},
|
||||
ajaxType: '',
|
||||
urlRoot: '',
|
||||
|
||||
edx.student.account.PasswordResetModel = Backbone.Model.extend({
|
||||
initialize: function( attributes, options ) {
|
||||
this.ajaxType = options.method;
|
||||
this.urlRoot = options.url;
|
||||
},
|
||||
|
||||
defaults: {
|
||||
email: ''
|
||||
},
|
||||
sync: function( method, model ) {
|
||||
var headers = {
|
||||
'X-CSRFToken': $.cookie('csrftoken')
|
||||
};
|
||||
|
||||
ajaxType: '',
|
||||
|
||||
urlRoot: '',
|
||||
|
||||
initialize: function( attributes, options ) {
|
||||
this.ajaxType = options.method;
|
||||
this.urlRoot = options.url;
|
||||
},
|
||||
|
||||
sync: function( method, model ) {
|
||||
var headers = {
|
||||
'X-CSRFToken': $.cookie('csrftoken')
|
||||
};
|
||||
|
||||
// Only expects an email address.
|
||||
$.ajax({
|
||||
url: model.urlRoot,
|
||||
type: model.ajaxType,
|
||||
data: model.attributes,
|
||||
headers: headers,
|
||||
success: function() {
|
||||
model.trigger('sync');
|
||||
},
|
||||
error: function( error ) {
|
||||
model.trigger('error', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
// Only expects an email address.
|
||||
$.ajax({
|
||||
url: model.urlRoot,
|
||||
type: model.ajaxType,
|
||||
data: model.attributes,
|
||||
headers: headers,
|
||||
success: function() {
|
||||
model.trigger('sync');
|
||||
},
|
||||
error: function( error ) {
|
||||
model.trigger('error', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery, Backbone);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,61 +1,56 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, Backbone) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define(['jquery', 'backbone', 'jquery.url'],
|
||||
function($, Backbone) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return Backbone.Model.extend({
|
||||
defaults: {
|
||||
email: '',
|
||||
name: '',
|
||||
username: '',
|
||||
password: '',
|
||||
level_of_education: '',
|
||||
gender: '',
|
||||
year_of_birth: '',
|
||||
mailing_address: '',
|
||||
goals: ''
|
||||
},
|
||||
ajaxType: '',
|
||||
urlRoot: '',
|
||||
|
||||
edx.student.account.RegisterModel = Backbone.Model.extend({
|
||||
initialize: function( attributes, options ) {
|
||||
this.ajaxType = options.method;
|
||||
this.urlRoot = options.url;
|
||||
},
|
||||
|
||||
defaults: {
|
||||
email: '',
|
||||
name: '',
|
||||
username: '',
|
||||
password: '',
|
||||
level_of_education: '',
|
||||
gender: '',
|
||||
year_of_birth: '',
|
||||
mailing_address: '',
|
||||
goals: '',
|
||||
},
|
||||
sync: function(method, model) {
|
||||
var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
|
||||
data = {},
|
||||
courseId = $.url( '?course_id' );
|
||||
|
||||
ajaxType: '',
|
||||
|
||||
urlRoot: '',
|
||||
|
||||
initialize: function( attributes, options ) {
|
||||
this.ajaxType = options.method;
|
||||
this.urlRoot = options.url;
|
||||
},
|
||||
|
||||
sync: function(method, model) {
|
||||
var headers = { 'X-CSRFToken': $.cookie('csrftoken') },
|
||||
data = {},
|
||||
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 ) {
|
||||
data.course_id = decodeURIComponent(courseId);
|
||||
}
|
||||
|
||||
// Include all form fields and analytics info in the data sent to the server
|
||||
$.extend( data, model.attributes);
|
||||
|
||||
$.ajax({
|
||||
url: model.urlRoot,
|
||||
type: model.ajaxType,
|
||||
data: data,
|
||||
headers: headers,
|
||||
success: function() {
|
||||
model.trigger('sync');
|
||||
},
|
||||
error: function( error ) {
|
||||
model.trigger('error', error);
|
||||
// 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 ) {
|
||||
data.course_id = decodeURIComponent(courseId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Include all form fields and analytics info in the data sent to the server
|
||||
$.extend( data, model.attributes);
|
||||
|
||||
$.ajax({
|
||||
url: model.urlRoot,
|
||||
type: model.ajaxType,
|
||||
data: data,
|
||||
headers: headers,
|
||||
success: function() {
|
||||
model.trigger('sync');
|
||||
},
|
||||
error: function( error ) {
|
||||
model.trigger('error', error);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery, Backbone);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,280 +1,294 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, _, _s, Backbone, History) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'utility',
|
||||
'underscore',
|
||||
'underscore.string',
|
||||
'backbone',
|
||||
'js/student_account/models/LoginModel',
|
||||
'js/student_account/models/PasswordResetModel',
|
||||
'js/student_account/models/RegisterModel',
|
||||
'js/student_account/views/LoginView',
|
||||
'js/student_account/views/PasswordResetView',
|
||||
'js/student_account/views/RegisterView',
|
||||
'js/student_account/views/InstitutionLoginView',
|
||||
'js/student_account/views/HintedLoginView',
|
||||
'js/vendor/history'
|
||||
],
|
||||
function($, utility, _, _s, Backbone, LoginModel, PasswordResetModel, RegisterModel, LoginView,
|
||||
PasswordResetView, RegisterView, InstitutionLoginView, HintedLoginView) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
if (_.isUndefined(_s)) {
|
||||
_s = _.str;
|
||||
}
|
||||
|
||||
edx.student.account.AccessView = Backbone.View.extend({
|
||||
el: '#login-and-registration-container',
|
||||
return Backbone.View.extend({
|
||||
tpl: '#access-tpl',
|
||||
events: {
|
||||
'click .form-toggle': 'toggleForm'
|
||||
},
|
||||
subview: {
|
||||
login: {},
|
||||
register: {},
|
||||
passwordHelp: {},
|
||||
institutionLogin: {},
|
||||
hintedLogin: {}
|
||||
},
|
||||
nextUrl: '/dashboard',
|
||||
// The form currently loaded
|
||||
activeForm: '',
|
||||
|
||||
tpl: '#access-tpl',
|
||||
initialize: function( options ) {
|
||||
|
||||
events: {
|
||||
'click .form-toggle': 'toggleForm'
|
||||
},
|
||||
/* Mix non-conflicting functions from underscore.string
|
||||
* (all but include, contains, and reverse) into the
|
||||
* Underscore namespace
|
||||
*/
|
||||
_.mixin( _s.exports() );
|
||||
|
||||
subview: {
|
||||
login: {},
|
||||
register: {},
|
||||
passwordHelp: {},
|
||||
institutionLogin: {},
|
||||
hintedLogin: {}
|
||||
},
|
||||
this.tpl = $(this.tpl).html();
|
||||
|
||||
nextUrl: '/dashboard',
|
||||
this.activeForm = options.initial_mode || 'login';
|
||||
|
||||
// The form currently loaded
|
||||
activeForm: '',
|
||||
this.thirdPartyAuth = options.third_party_auth || {
|
||||
currentProvider: null,
|
||||
providers: []
|
||||
};
|
||||
|
||||
initialize: function( obj ) {
|
||||
/* Mix non-conflicting functions from underscore.string
|
||||
* (all but include, contains, and reverse) into the
|
||||
* Underscore namespace
|
||||
*/
|
||||
_.mixin( _s.exports() );
|
||||
this.thirdPartyAuthHint = options.third_party_auth_hint || null;
|
||||
|
||||
this.tpl = $(this.tpl).html();
|
||||
|
||||
this.activeForm = obj.mode || 'login';
|
||||
|
||||
this.thirdPartyAuth = obj.thirdPartyAuth || {
|
||||
currentProvider: null,
|
||||
providers: []
|
||||
};
|
||||
|
||||
this.thirdPartyAuthHint = obj.thirdPartyAuthHint || null;
|
||||
|
||||
if (obj.nextUrl) {
|
||||
// Ensure that the next URL is internal for security reasons
|
||||
if ( ! window.isExternal( obj.nextUrl ) ) {
|
||||
this.nextUrl = obj.nextUrl;
|
||||
if (options.login_redirect_url) {
|
||||
// Ensure that the next URL is internal for security reasons
|
||||
if ( ! window.isExternal( options.login_redirect_url ) ) {
|
||||
this.nextUrl = options.login_redirect_url;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.formDescriptions = {
|
||||
login: obj.loginFormDesc,
|
||||
register: obj.registrationFormDesc,
|
||||
reset: obj.passwordResetFormDesc,
|
||||
institution_login: null,
|
||||
hinted_login: null
|
||||
};
|
||||
this.formDescriptions = {
|
||||
login: options.login_form_desc,
|
||||
register: options.registration_form_desc,
|
||||
reset: options.password_reset_form_desc,
|
||||
institution_login: null,
|
||||
hinted_login: null
|
||||
};
|
||||
|
||||
this.platformName = obj.platformName;
|
||||
this.platformName = options.platform_name;
|
||||
|
||||
// The login view listens for 'sync' events from the reset model
|
||||
this.resetModel = new edx.student.account.PasswordResetModel({}, {
|
||||
method: 'GET',
|
||||
url: '#'
|
||||
});
|
||||
|
||||
this.render();
|
||||
|
||||
// Once the third party error message has been shown once,
|
||||
// there is no need to show it again, if the user changes mode:
|
||||
this.thirdPartyAuth.errorMessage = null;
|
||||
},
|
||||
|
||||
render: function() {
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
mode: this.activeForm
|
||||
}));
|
||||
|
||||
this.postRender();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
postRender: function() {
|
||||
//get & check current url hash part & load form accordingly
|
||||
if (Backbone.history.getHash() === "forgot-password-modal") {
|
||||
this.resetPassword();
|
||||
} else {
|
||||
this.loadForm(this.activeForm);
|
||||
}
|
||||
},
|
||||
|
||||
loadForm: function( type ) {
|
||||
var loadFunc = _.bind( this.load[type], this );
|
||||
loadFunc( this.formDescriptions[type] );
|
||||
},
|
||||
|
||||
load: {
|
||||
login: function( data ) {
|
||||
var model = new edx.student.account.LoginModel({}, {
|
||||
method: data.method,
|
||||
url: data.submit_url
|
||||
// The login view listens for 'sync' events from the reset model
|
||||
this.resetModel = new PasswordResetModel({}, {
|
||||
method: 'GET',
|
||||
url: '#'
|
||||
});
|
||||
|
||||
this.subview.login = new edx.student.account.LoginView({
|
||||
fields: data.fields,
|
||||
model: model,
|
||||
resetModel: this.resetModel,
|
||||
thirdPartyAuth: this.thirdPartyAuth,
|
||||
platformName: this.platformName
|
||||
});
|
||||
|
||||
// Listen for 'password-help' event to toggle sub-views
|
||||
this.listenTo( this.subview.login, 'password-help', this.resetPassword );
|
||||
|
||||
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
|
||||
this.listenTo( this.subview.login, 'auth-complete', this.authComplete );
|
||||
this.render();
|
||||
|
||||
// Once the third party error message has been shown once,
|
||||
// there is no need to show it again, if the user changes mode:
|
||||
this.thirdPartyAuth.errorMessage = null;
|
||||
},
|
||||
|
||||
reset: function( data ) {
|
||||
this.resetModel.ajaxType = data.method;
|
||||
this.resetModel.urlRoot = data.submit_url;
|
||||
render: function() {
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
mode: this.activeForm
|
||||
}));
|
||||
|
||||
this.subview.passwordHelp = new edx.student.account.PasswordResetView({
|
||||
fields: data.fields,
|
||||
model: this.resetModel
|
||||
this.postRender();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
postRender: function() {
|
||||
//get & check current url hash part & load form accordingly
|
||||
if (Backbone.history.getHash() === 'forgot-password-modal') {
|
||||
this.resetPassword();
|
||||
} else {
|
||||
this.loadForm(this.activeForm);
|
||||
}
|
||||
},
|
||||
|
||||
loadForm: function( type ) {
|
||||
var loadFunc = _.bind( this.load[type], this );
|
||||
loadFunc( this.formDescriptions[type] );
|
||||
},
|
||||
|
||||
load: {
|
||||
login: function( data ) {
|
||||
var model = new LoginModel({}, {
|
||||
method: data.method,
|
||||
url: data.submit_url
|
||||
});
|
||||
|
||||
this.subview.login = new LoginView({
|
||||
fields: data.fields,
|
||||
model: model,
|
||||
resetModel: this.resetModel,
|
||||
thirdPartyAuth: this.thirdPartyAuth,
|
||||
platformName: this.platformName
|
||||
});
|
||||
|
||||
// Listen for 'password-help' event to toggle sub-views
|
||||
this.listenTo( this.subview.login, 'password-help', this.resetPassword );
|
||||
|
||||
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
|
||||
this.listenTo( this.subview.login, 'auth-complete', this.authComplete );
|
||||
|
||||
},
|
||||
|
||||
reset: function( data ) {
|
||||
this.resetModel.ajaxType = data.method;
|
||||
this.resetModel.urlRoot = data.submit_url;
|
||||
|
||||
this.subview.passwordHelp = new PasswordResetView({
|
||||
fields: data.fields,
|
||||
model: this.resetModel
|
||||
});
|
||||
|
||||
// Listen for 'password-email-sent' event to toggle sub-views
|
||||
this.listenTo( this.subview.passwordHelp, 'password-email-sent', this.passwordEmailSent );
|
||||
|
||||
// Focus on the form
|
||||
$('.password-reset-form').focus();
|
||||
},
|
||||
|
||||
register: function( data ) {
|
||||
var model = new RegisterModel({}, {
|
||||
method: data.method,
|
||||
url: data.submit_url
|
||||
});
|
||||
|
||||
this.subview.register = new RegisterView({
|
||||
fields: data.fields,
|
||||
model: model,
|
||||
thirdPartyAuth: this.thirdPartyAuth,
|
||||
platformName: this.platformName
|
||||
});
|
||||
|
||||
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
|
||||
this.listenTo( this.subview.register, 'auth-complete', this.authComplete );
|
||||
},
|
||||
|
||||
institution_login: function ( unused ) {
|
||||
this.subview.institutionLogin = new InstitutionLoginView({
|
||||
thirdPartyAuth: this.thirdPartyAuth,
|
||||
platformName: this.platformName,
|
||||
mode: this.activeForm
|
||||
});
|
||||
|
||||
this.subview.institutionLogin.render();
|
||||
},
|
||||
|
||||
hinted_login: function ( unused ) {
|
||||
this.subview.hintedLogin = new HintedLoginView({
|
||||
thirdPartyAuth: this.thirdPartyAuth,
|
||||
hintedProvider: this.thirdPartyAuthHint,
|
||||
platformName: this.platformName
|
||||
});
|
||||
|
||||
this.subview.hintedLogin.render();
|
||||
}
|
||||
},
|
||||
|
||||
passwordEmailSent: function() {
|
||||
var $loginAnchorElement = $('#login-anchor');
|
||||
this.element.hide( $(this.el).find('#password-reset-anchor') );
|
||||
this.element.show( $loginAnchorElement );
|
||||
this.element.scrollTop( $loginAnchorElement );
|
||||
},
|
||||
|
||||
resetPassword: function() {
|
||||
window.analytics.track('edx.bi.password_reset_form.viewed', {
|
||||
category: 'user-engagement'
|
||||
});
|
||||
|
||||
// Listen for 'password-email-sent' event to toggle sub-views
|
||||
this.listenTo( this.subview.passwordHelp, 'password-email-sent', this.passwordEmailSent );
|
||||
this.element.hide( $(this.el).find('#login-anchor') );
|
||||
this.loadForm('reset');
|
||||
this.element.scrollTop( $('#password-reset-anchor') );
|
||||
},
|
||||
|
||||
toggleForm: function( e ) {
|
||||
var type = $(e.currentTarget).data('type'),
|
||||
$form = $('#' + type + '-form'),
|
||||
$anchor = $('#' + type + '-anchor'),
|
||||
queryParams = url('?'),
|
||||
queryStr = queryParams.length > 0 ? '?' + queryParams : '';
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
window.analytics.track('edx.bi.' + type + '_form.toggled', {
|
||||
category: 'user-engagement'
|
||||
});
|
||||
|
||||
// Load the form. Institution login is always refreshed since it changes based on the previous form.
|
||||
if ( !this.form.isLoaded( $form ) || type == 'institution_login') {
|
||||
this.loadForm( type );
|
||||
}
|
||||
this.activeForm = type;
|
||||
|
||||
this.element.hide( $(this.el).find('.submission-success') );
|
||||
this.element.hide( $(this.el).find('.form-wrapper') );
|
||||
this.element.show( $form );
|
||||
this.element.scrollTop( $anchor );
|
||||
|
||||
// Update url without reloading page
|
||||
if (type != 'institution_login') {
|
||||
History.pushState( null, document.title, '/' + type + queryStr );
|
||||
}
|
||||
analytics.page( 'login_and_registration', type );
|
||||
|
||||
// Focus on the form
|
||||
$('.password-reset-form').focus();
|
||||
$('#' + type).focus();
|
||||
},
|
||||
|
||||
register: function( data ) {
|
||||
var model = new edx.student.account.RegisterModel({}, {
|
||||
method: data.method,
|
||||
url: data.submit_url
|
||||
});
|
||||
|
||||
this.subview.register = new edx.student.account.RegisterView({
|
||||
fields: data.fields,
|
||||
model: model,
|
||||
thirdPartyAuth: this.thirdPartyAuth,
|
||||
platformName: this.platformName
|
||||
});
|
||||
|
||||
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
|
||||
this.listenTo( this.subview.register, 'auth-complete', this.authComplete );
|
||||
/**
|
||||
* Once authentication has completed successfully:
|
||||
*
|
||||
* If we're in a third party auth pipeline, we must complete the pipeline.
|
||||
* Otherwise, redirect to the specified next step.
|
||||
*
|
||||
*/
|
||||
authComplete: function() {
|
||||
if (this.thirdPartyAuth && this.thirdPartyAuth.finishAuthUrl) {
|
||||
this.redirect(this.thirdPartyAuth.finishAuthUrl);
|
||||
// Note: the third party auth URL likely contains another redirect URL embedded inside
|
||||
} else {
|
||||
this.redirect(this.nextUrl);
|
||||
}
|
||||
},
|
||||
|
||||
institution_login: function ( unused ) {
|
||||
this.subview.institutionLogin = new edx.student.account.InstitutionLoginView({
|
||||
thirdPartyAuth: this.thirdPartyAuth,
|
||||
platformName: this.platformName,
|
||||
mode: this.activeForm
|
||||
});
|
||||
|
||||
this.subview.institutionLogin.render();
|
||||
/**
|
||||
* Redirect to a URL. Mainly useful for mocking out in tests.
|
||||
* @param {string} url The URL to redirect to.
|
||||
*/
|
||||
redirect: function( url ) {
|
||||
window.location.replace(url);
|
||||
},
|
||||
|
||||
hinted_login: function ( unused ) {
|
||||
this.subview.hintedLogin = new edx.student.account.HintedLoginView({
|
||||
thirdPartyAuth: this.thirdPartyAuth,
|
||||
hintedProvider: this.thirdPartyAuthHint,
|
||||
platformName: this.platformName
|
||||
});
|
||||
|
||||
this.subview.hintedLogin.render();
|
||||
}
|
||||
},
|
||||
|
||||
passwordEmailSent: function() {
|
||||
this.element.hide( $(this.el).find('#password-reset-anchor') );
|
||||
this.element.show( $('#login-anchor') );
|
||||
this.element.scrollTop( $('#login-anchor') );
|
||||
},
|
||||
|
||||
resetPassword: function() {
|
||||
window.analytics.track('edx.bi.password_reset_form.viewed', {
|
||||
category: 'user-engagement'
|
||||
});
|
||||
|
||||
this.element.hide( $(this.el).find('#login-anchor') );
|
||||
this.loadForm('reset');
|
||||
this.element.scrollTop( $('#password-reset-anchor') );
|
||||
},
|
||||
|
||||
toggleForm: function( e ) {
|
||||
var type = $(e.currentTarget).data('type'),
|
||||
$form = $('#' + type + '-form'),
|
||||
$anchor = $('#' + type + '-anchor'),
|
||||
queryParams = url('?'),
|
||||
queryStr = queryParams.length > 0 ? '?' + queryParams : '';
|
||||
|
||||
e.preventDefault();
|
||||
|
||||
window.analytics.track('edx.bi.' + type + '_form.toggled', {
|
||||
category: 'user-engagement'
|
||||
});
|
||||
|
||||
// Load the form. Institution login is always refreshed since it changes based on the previous form.
|
||||
if ( !this.form.isLoaded( $form ) || type == "institution_login") {
|
||||
this.loadForm( type );
|
||||
}
|
||||
this.activeForm = type;
|
||||
|
||||
this.element.hide( $(this.el).find('.submission-success') );
|
||||
this.element.hide( $(this.el).find('.form-wrapper') );
|
||||
this.element.show( $form );
|
||||
this.element.scrollTop( $anchor );
|
||||
|
||||
// Update url without reloading page
|
||||
if (type != "institution_login") {
|
||||
History.pushState( null, document.title, '/' + type + queryStr );
|
||||
}
|
||||
analytics.page( 'login_and_registration', type );
|
||||
|
||||
// Focus on the form
|
||||
$("#" + type).focus();
|
||||
},
|
||||
|
||||
/**
|
||||
* Once authentication has completed successfully:
|
||||
*
|
||||
* If we're in a third party auth pipeline, we must complete the pipeline.
|
||||
* Otherwise, redirect to the specified next step.
|
||||
*
|
||||
*/
|
||||
authComplete: function() {
|
||||
if (this.thirdPartyAuth && this.thirdPartyAuth.finishAuthUrl) {
|
||||
this.redirect(this.thirdPartyAuth.finishAuthUrl);
|
||||
// Note: the third party auth URL likely contains another redirect URL embedded inside
|
||||
} else {
|
||||
this.redirect(this.nextUrl);
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Redirect to a URL. Mainly useful for mocking out in tests.
|
||||
* @param {string} url The URL to redirect to.
|
||||
*/
|
||||
redirect: function( url ) {
|
||||
window.location.replace(url);
|
||||
},
|
||||
|
||||
form: {
|
||||
isLoaded: function( $form ) {
|
||||
return $form.html().length > 0;
|
||||
}
|
||||
},
|
||||
|
||||
/* Helper method to toggle display
|
||||
* including accessibility considerations
|
||||
*/
|
||||
element: {
|
||||
hide: function( $el ) {
|
||||
$el.addClass('hidden');
|
||||
form: {
|
||||
isLoaded: function( $form ) {
|
||||
return $form.html().length > 0;
|
||||
}
|
||||
},
|
||||
|
||||
scrollTop: function( $el ) {
|
||||
// Scroll to top of selected element
|
||||
$('html,body').animate({
|
||||
scrollTop: $el.offset().top
|
||||
},'slow');
|
||||
},
|
||||
/* Helper method to toggle display
|
||||
* including accessibility considerations
|
||||
*/
|
||||
element: {
|
||||
hide: function( $el ) {
|
||||
$el.addClass('hidden');
|
||||
},
|
||||
|
||||
show: function( $el ) {
|
||||
$el.removeClass('hidden');
|
||||
scrollTop: function( $el ) {
|
||||
// Scroll to top of selected element
|
||||
$('html,body').animate({
|
||||
scrollTop: $el.offset().top
|
||||
},'slow');
|
||||
},
|
||||
|
||||
show: function( $el ) {
|
||||
$el.removeClass('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery, _, _.str, Backbone, History);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,270 +1,271 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, _, Backbone, gettext) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'backbone',
|
||||
'js/utils/edx.utils.validate'
|
||||
],
|
||||
function($, _, Backbone, EdxUtilsValidate) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return Backbone.View.extend({
|
||||
tagName: 'form',
|
||||
|
||||
edx.student.account.FormView = Backbone.View.extend({
|
||||
tagName: 'form',
|
||||
el: '',
|
||||
|
||||
el: '',
|
||||
tpl: '',
|
||||
|
||||
tpl: '',
|
||||
fieldTpl: '#form_field-tpl',
|
||||
|
||||
fieldTpl: '#form_field-tpl',
|
||||
events: {},
|
||||
|
||||
events: {},
|
||||
errors: [],
|
||||
|
||||
errors: [],
|
||||
formType: '',
|
||||
|
||||
formType: '',
|
||||
$form: {},
|
||||
|
||||
$form: {},
|
||||
fields: [],
|
||||
|
||||
fields: [],
|
||||
// String to append to required label fields
|
||||
requiredStr: '*',
|
||||
|
||||
// String to append to required label fields
|
||||
requiredStr: '*',
|
||||
submitButton: '',
|
||||
|
||||
submitButton: '',
|
||||
initialize: function( data ) {
|
||||
this.model = data.model;
|
||||
this.preRender( data );
|
||||
|
||||
initialize: function( data ) {
|
||||
this.model = data.model;
|
||||
this.preRender( data );
|
||||
this.tpl = $(this.tpl).html();
|
||||
this.fieldTpl = $(this.fieldTpl).html();
|
||||
this.buildForm( data.fields );
|
||||
|
||||
this.tpl = $(this.tpl).html();
|
||||
this.fieldTpl = $(this.fieldTpl).html();
|
||||
this.buildForm( data.fields );
|
||||
|
||||
this.listenTo( this.model, 'error', this.saveError );
|
||||
},
|
||||
|
||||
/* Allows extended views to add custom
|
||||
* init steps without needing to repeat
|
||||
* default init steps
|
||||
*/
|
||||
preRender: function( data ) {
|
||||
/* Custom code goes here */
|
||||
return data;
|
||||
},
|
||||
|
||||
render: function( html ) {
|
||||
var fields = html || '';
|
||||
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
fields: fields
|
||||
}));
|
||||
|
||||
this.postRender();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
postRender: function() {
|
||||
var $container = $(this.el);
|
||||
|
||||
this.$form = $container.find('form');
|
||||
this.$errors = $container.find('.submission-error');
|
||||
this.$submitButton = $container.find(this.submitButton);
|
||||
},
|
||||
|
||||
buildForm: function( data ) {
|
||||
var html = [],
|
||||
i,
|
||||
len = data.length,
|
||||
fieldTpl = this.fieldTpl;
|
||||
|
||||
this.fields = data;
|
||||
|
||||
for ( i=0; i<len; i++ ) {
|
||||
if ( data[i].errorMessages ) {
|
||||
data[i].errorMessages = this.escapeStrings( data[i].errorMessages );
|
||||
}
|
||||
|
||||
html.push( _.template( fieldTpl, $.extend( data[i], {
|
||||
form: this.formType,
|
||||
requiredStr: this.requiredStr
|
||||
}) ) );
|
||||
}
|
||||
|
||||
this.render( html.join('') );
|
||||
},
|
||||
|
||||
/* Helper method to toggle display
|
||||
* including accessibility considerations
|
||||
*/
|
||||
element: {
|
||||
hide: function( $el ) {
|
||||
if ( $el ) {
|
||||
$el.addClass('hidden');
|
||||
}
|
||||
this.listenTo( this.model, 'error', this.saveError );
|
||||
},
|
||||
|
||||
scrollTop: function( $el ) {
|
||||
// Scroll to top of selected element
|
||||
$('html,body').animate({
|
||||
scrollTop: $el.offset().top
|
||||
},'slow');
|
||||
/* Allows extended views to add custom
|
||||
* init steps without needing to repeat
|
||||
* default init steps
|
||||
*/
|
||||
preRender: function( data ) {
|
||||
/* Custom code goes here */
|
||||
return data;
|
||||
},
|
||||
|
||||
show: function( $el ) {
|
||||
if ( $el ) {
|
||||
$el.removeClass('hidden');
|
||||
render: function( html ) {
|
||||
var fields = html || '';
|
||||
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
fields: fields
|
||||
}));
|
||||
|
||||
this.postRender();
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
postRender: function() {
|
||||
var $container = $(this.el);
|
||||
this.$form = $container.find('form');
|
||||
this.$errors = $container.find('.submission-error');
|
||||
this.$submitButton = $container.find(this.submitButton);
|
||||
},
|
||||
|
||||
buildForm: function( data ) {
|
||||
var html = [],
|
||||
i,
|
||||
len = data.length,
|
||||
fieldTpl = this.fieldTpl;
|
||||
|
||||
this.fields = data;
|
||||
|
||||
for ( i=0; i<len; i++ ) {
|
||||
if ( data[i].errorMessages ) {
|
||||
data[i].errorMessages = this.escapeStrings( data[i].errorMessages );
|
||||
}
|
||||
|
||||
html.push( _.template( fieldTpl, $.extend( data[i], {
|
||||
form: this.formType,
|
||||
requiredStr: this.requiredStr
|
||||
}) ) );
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
escapeStrings: function( obj ) {
|
||||
_.each( obj, function( val, key ) {
|
||||
obj[key] = _.escape( val );
|
||||
});
|
||||
this.render( html.join('') );
|
||||
},
|
||||
|
||||
return obj;
|
||||
},
|
||||
/* Helper method to toggle display
|
||||
* including accessibility considerations
|
||||
*/
|
||||
element: {
|
||||
hide: function( $el ) {
|
||||
if ( $el ) {
|
||||
$el.addClass('hidden');
|
||||
}
|
||||
},
|
||||
|
||||
focusFirstError: function() {
|
||||
var $error = this.$form.find('.error').first(),
|
||||
$field = {},
|
||||
$parent = {};
|
||||
scrollTop: function( $el ) {
|
||||
// Scroll to top of selected element
|
||||
$('html,body').animate({
|
||||
scrollTop: $el.offset().top
|
||||
},'slow');
|
||||
},
|
||||
|
||||
if ( $error.is('label') ) {
|
||||
$parent = $error.parent('.form-field');
|
||||
$error = $parent.find('input') || $parent.find('select');
|
||||
} else {
|
||||
$field = $error;
|
||||
}
|
||||
|
||||
$error.focus();
|
||||
},
|
||||
|
||||
forgotPassword: function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
this.trigger('password-help');
|
||||
},
|
||||
|
||||
getFormData: function() {
|
||||
var obj = {},
|
||||
$form = this.$form,
|
||||
elements = $form[0].elements,
|
||||
i,
|
||||
len = elements.length,
|
||||
$el,
|
||||
$label,
|
||||
key = '',
|
||||
errors = [],
|
||||
test = {};
|
||||
|
||||
for ( i=0; i<len; i++ ) {
|
||||
|
||||
$el = $( elements[i] );
|
||||
$label = $form.find('label[for=' + $el.attr('id') + ']');
|
||||
key = $el.attr('name') || false;
|
||||
|
||||
if ( key ) {
|
||||
test = this.validate( elements[i] );
|
||||
if ( test.isValid ) {
|
||||
obj[key] = $el.attr('type') === 'checkbox' ? $el.is(':checked') : $el.val();
|
||||
$el.removeClass('error');
|
||||
$label.removeClass('error');
|
||||
} else {
|
||||
errors.push( test.message );
|
||||
$el.addClass('error');
|
||||
$label.addClass('error');
|
||||
show: function( $el ) {
|
||||
if ( $el ) {
|
||||
$el.removeClass('hidden');
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
this.errors = _.uniq( errors );
|
||||
escapeStrings: function( obj ) {
|
||||
_.each( obj, function( val, key ) {
|
||||
obj[key] = _.escape( val );
|
||||
});
|
||||
|
||||
return obj;
|
||||
},
|
||||
return obj;
|
||||
},
|
||||
|
||||
saveError: function( error ) {
|
||||
this.errors = ['<li>' + error.responseText + '</li>'];
|
||||
this.setErrors();
|
||||
this.toggleDisableButton(false);
|
||||
},
|
||||
focusFirstError: function() {
|
||||
var $error = this.$form.find('.error').first(),
|
||||
$field = {},
|
||||
$parent = {};
|
||||
|
||||
setErrors: function() {
|
||||
var $msg = this.$errors.find('.message-copy'),
|
||||
html = [],
|
||||
errors = this.errors,
|
||||
i,
|
||||
len = errors.length;
|
||||
if ( $error.is('label') ) {
|
||||
$parent = $error.parent('.form-field');
|
||||
$error = $parent.find('input') || $parent.find('select');
|
||||
} else {
|
||||
$field = $error;
|
||||
}
|
||||
|
||||
for ( i=0; i<len; i++ ) {
|
||||
html.push( errors[i] );
|
||||
}
|
||||
$error.focus();
|
||||
},
|
||||
|
||||
$msg.html( html.join('') );
|
||||
|
||||
this.element.show( this.$errors );
|
||||
|
||||
// Scroll to error messages
|
||||
$('html,body').animate({
|
||||
scrollTop: this.$errors.offset().top
|
||||
},'slow');
|
||||
|
||||
// Focus on first error field
|
||||
this.focusFirstError();
|
||||
},
|
||||
|
||||
submitForm: function( event ) {
|
||||
var data = this.getFormData();
|
||||
|
||||
if (!_.isUndefined(event)) {
|
||||
forgotPassword: function( event ) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
this.toggleDisableButton(true);
|
||||
this.trigger('password-help');
|
||||
},
|
||||
|
||||
if ( !_.compact(this.errors).length ) {
|
||||
this.model.set( data );
|
||||
this.model.save();
|
||||
this.toggleErrorMsg( false );
|
||||
} else {
|
||||
this.toggleErrorMsg( true );
|
||||
}
|
||||
getFormData: function() {
|
||||
var obj = {},
|
||||
$form = this.$form,
|
||||
elements = $form[0].elements,
|
||||
i,
|
||||
len = elements.length,
|
||||
$el,
|
||||
$label,
|
||||
key = '',
|
||||
errors = [],
|
||||
test = {};
|
||||
|
||||
this.postFormSubmission();
|
||||
},
|
||||
for ( i=0; i<len; i++ ) {
|
||||
|
||||
/* Allows extended views to add custom
|
||||
* code after form submission
|
||||
*/
|
||||
postFormSubmission: function() {
|
||||
return true;
|
||||
},
|
||||
$el = $( elements[i] );
|
||||
$label = $form.find('label[for=' + $el.attr('id') + ']');
|
||||
key = $el.attr('name') || false;
|
||||
|
||||
toggleErrorMsg: function( show ) {
|
||||
if ( show ) {
|
||||
if ( key ) {
|
||||
test = this.validate( elements[i] );
|
||||
if ( test.isValid ) {
|
||||
obj[key] = $el.attr('type') === 'checkbox' ? $el.is(':checked') : $el.val();
|
||||
$el.removeClass('error');
|
||||
$label.removeClass('error');
|
||||
} else {
|
||||
errors.push( test.message );
|
||||
$el.addClass('error');
|
||||
$label.addClass('error');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.errors = _.uniq( errors );
|
||||
|
||||
return obj;
|
||||
},
|
||||
|
||||
saveError: function( error ) {
|
||||
this.errors = ['<li>' + error.responseText + '</li>'];
|
||||
this.setErrors();
|
||||
this.toggleDisableButton(false);
|
||||
} else {
|
||||
this.element.hide( this.$errors );
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
/**
|
||||
* If a form button is defined for this form, this will disable the button on
|
||||
* submit, and re-enable the button if an error occurs.
|
||||
*
|
||||
* Args:
|
||||
* disabled (boolean): If set to TRUE, disable the button.
|
||||
*
|
||||
*/
|
||||
toggleDisableButton: function ( disabled ) {
|
||||
if (this.$submitButton) {
|
||||
this.$submitButton.attr('disabled', disabled);
|
||||
}
|
||||
},
|
||||
setErrors: function() {
|
||||
var $msg = this.$errors.find('.message-copy'),
|
||||
html = [],
|
||||
errors = this.errors,
|
||||
i,
|
||||
len = errors.length;
|
||||
|
||||
validate: function( $el ) {
|
||||
return edx.utils.validate( $el );
|
||||
}
|
||||
for ( i=0; i<len; i++ ) {
|
||||
html.push( errors[i] );
|
||||
}
|
||||
|
||||
$msg.html( html.join('') );
|
||||
|
||||
this.element.show( this.$errors );
|
||||
|
||||
// Scroll to error messages
|
||||
$('html,body').animate({
|
||||
scrollTop: this.$errors.offset().top
|
||||
},'slow');
|
||||
|
||||
// Focus on first error field
|
||||
this.focusFirstError();
|
||||
},
|
||||
|
||||
submitForm: function( event ) {
|
||||
var data = this.getFormData();
|
||||
|
||||
if (!_.isUndefined(event)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
this.toggleDisableButton(true);
|
||||
|
||||
if ( !_.compact(this.errors).length ) {
|
||||
this.model.set( data );
|
||||
this.model.save();
|
||||
this.toggleErrorMsg( false );
|
||||
} else {
|
||||
this.toggleErrorMsg( true );
|
||||
}
|
||||
|
||||
this.postFormSubmission();
|
||||
},
|
||||
|
||||
/* Allows extended views to add custom
|
||||
* code after form submission
|
||||
*/
|
||||
postFormSubmission: function() {
|
||||
return true;
|
||||
},
|
||||
|
||||
toggleErrorMsg: function( show ) {
|
||||
if ( show ) {
|
||||
this.setErrors();
|
||||
this.toggleDisableButton(false);
|
||||
} else {
|
||||
this.element.hide( this.$errors );
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* If a form button is defined for this form, this will disable the button on
|
||||
* submit, and re-enable the button if an error occurs.
|
||||
*
|
||||
* Args:
|
||||
* disabled (boolean): If set to TRUE, disable the button.
|
||||
*
|
||||
*/
|
||||
toggleDisableButton: function ( disabled ) {
|
||||
if (this.$submitButton) {
|
||||
this.$submitButton.attr('disabled', disabled);
|
||||
}
|
||||
},
|
||||
|
||||
validate: function( $el ) {
|
||||
return EdxUtilsValidate.validate( $el );
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery, _, Backbone, gettext);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,48 +1,46 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, _, gettext) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define(['jquery', 'underscore', 'backbone'],
|
||||
function($, _, Backbone) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return Backbone.View.extend({
|
||||
el: '#hinted-login-form',
|
||||
|
||||
edx.student.account.HintedLoginView = Backbone.View.extend({
|
||||
el: '#hinted-login-form',
|
||||
tpl: '#hinted_login-tpl',
|
||||
|
||||
tpl: '#hinted_login-tpl',
|
||||
events: {
|
||||
'click .proceed-button': 'proceedWithHintedAuth'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .proceed-button': 'proceedWithHintedAuth'
|
||||
},
|
||||
formType: 'hinted-login',
|
||||
|
||||
formType: 'hinted-login',
|
||||
initialize: function( data ) {
|
||||
this.tpl = $(this.tpl).html();
|
||||
this.hintedProvider = (
|
||||
_.findWhere(data.thirdPartyAuth.providers, {id: data.hintedProvider}) ||
|
||||
_.findWhere(data.thirdPartyAuth.secondaryProviders, {id: data.hintedProvider})
|
||||
);
|
||||
},
|
||||
|
||||
initialize: function( data ) {
|
||||
this.tpl = $(this.tpl).html();
|
||||
this.hintedProvider = (
|
||||
_.findWhere(data.thirdPartyAuth.providers, {id: data.hintedProvider}) ||
|
||||
_.findWhere(data.thirdPartyAuth.secondaryProviders, {id: data.hintedProvider})
|
||||
);
|
||||
},
|
||||
render: function() {
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
hintedProvider: this.hintedProvider
|
||||
}));
|
||||
|
||||
render: function() {
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
hintedProvider: this.hintedProvider
|
||||
}));
|
||||
return this;
|
||||
},
|
||||
|
||||
return this;
|
||||
},
|
||||
proceedWithHintedAuth: function( event ) {
|
||||
this.redirect(this.hintedProvider.loginUrl);
|
||||
},
|
||||
|
||||
proceedWithHintedAuth: function( event ) {
|
||||
this.redirect(this.hintedProvider.loginUrl);
|
||||
},
|
||||
|
||||
/**
|
||||
* Redirect to a URL. Mainly useful for mocking out in tests.
|
||||
* @param {string} url The URL to redirect to.
|
||||
*/
|
||||
redirect: function( url ) {
|
||||
window.location.href = url;
|
||||
}
|
||||
/**
|
||||
* Redirect to a URL. Mainly useful for mocking out in tests.
|
||||
* @param {string} url The URL to redirect to.
|
||||
*/
|
||||
redirect: function( url ) {
|
||||
window.location.href = url;
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery, _, gettext);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,30 +1,28 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, _, Backbone) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define(['jquery', 'underscore', 'backbone'],
|
||||
function($, _, Backbone) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return Backbone.View.extend({
|
||||
el: '#institution_login-form',
|
||||
|
||||
edx.student.account.InstitutionLoginView = Backbone.View.extend({
|
||||
el: '#institution_login-form',
|
||||
initialize: function( data ) {
|
||||
var tpl = data.mode == "register" ? '#institution_register-tpl' : '#institution_login-tpl';
|
||||
this.tpl = $(tpl).html();
|
||||
this.providers = data.thirdPartyAuth.secondaryProviders || [];
|
||||
this.platformName = data.platformName;
|
||||
},
|
||||
|
||||
initialize: function( data ) {
|
||||
var tpl = data.mode == "register" ? '#institution_register-tpl' : '#institution_login-tpl';
|
||||
this.tpl = $(tpl).html();
|
||||
this.providers = data.thirdPartyAuth.secondaryProviders || [];
|
||||
this.platformName = data.platformName;
|
||||
},
|
||||
render: function() {
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
// We pass the context object to the template so that
|
||||
// we can perform variable interpolation using sprintf
|
||||
providers: this.providers,
|
||||
platformName: this.platformName
|
||||
}));
|
||||
|
||||
render: function() {
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
// We pass the context object to the template so that
|
||||
// we can perform variable interpolation using sprintf
|
||||
providers: this.providers,
|
||||
platformName: this.platformName
|
||||
}));
|
||||
|
||||
return this;
|
||||
}
|
||||
return this;
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery, _, Backbone);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,131 +1,129 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, _, gettext) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'js/student_account/views/FormView'
|
||||
],
|
||||
function($, _, FormView) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return FormView.extend({
|
||||
el: '#login-form',
|
||||
tpl: '#login-tpl',
|
||||
events: {
|
||||
'click .js-login': 'submitForm',
|
||||
'click .forgot-password': 'forgotPassword',
|
||||
'click .login-provider': 'thirdPartyAuth'
|
||||
},
|
||||
formType: 'login',
|
||||
requiredStr: '',
|
||||
submitButton: '.js-login',
|
||||
|
||||
edx.student.account.LoginView = edx.student.account.FormView.extend({
|
||||
el: '#login-form',
|
||||
preRender: function( data ) {
|
||||
this.providers = data.thirdPartyAuth.providers || [];
|
||||
this.hasSecondaryProviders = (
|
||||
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
|
||||
);
|
||||
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
|
||||
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
|
||||
this.platformName = data.platformName;
|
||||
this.resetModel = data.resetModel;
|
||||
|
||||
tpl: '#login-tpl',
|
||||
this.listenTo( this.model, 'sync', this.saveSuccess );
|
||||
this.listenTo( this.resetModel, 'sync', this.resetEmail );
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .js-login': 'submitForm',
|
||||
'click .forgot-password': 'forgotPassword',
|
||||
'click .login-provider': 'thirdPartyAuth'
|
||||
},
|
||||
render: function( html ) {
|
||||
var fields = html || '';
|
||||
|
||||
formType: 'login',
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
// We pass the context object to the template so that
|
||||
// we can perform variable interpolation using sprintf
|
||||
context: {
|
||||
fields: fields,
|
||||
currentProvider: this.currentProvider,
|
||||
errorMessage: this.errorMessage,
|
||||
providers: this.providers,
|
||||
hasSecondaryProviders: this.hasSecondaryProviders,
|
||||
platformName: this.platformName
|
||||
}
|
||||
}));
|
||||
|
||||
requiredStr: '',
|
||||
this.postRender();
|
||||
|
||||
submitButton: '.js-login',
|
||||
return this;
|
||||
},
|
||||
|
||||
preRender: function( data ) {
|
||||
this.providers = data.thirdPartyAuth.providers || [];
|
||||
this.hasSecondaryProviders = (
|
||||
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
|
||||
);
|
||||
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
|
||||
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
|
||||
this.platformName = data.platformName;
|
||||
this.resetModel = data.resetModel;
|
||||
postRender: function() {
|
||||
this.$container = $(this.el);
|
||||
|
||||
this.listenTo( this.model, 'sync', this.saveSuccess );
|
||||
this.listenTo( this.resetModel, 'sync', this.resetEmail );
|
||||
},
|
||||
this.$form = this.$container.find('form');
|
||||
this.$errors = this.$container.find('.submission-error');
|
||||
this.$resetSuccess = this.$container.find('.js-reset-success');
|
||||
this.$authError = this.$container.find('.already-authenticated-msg');
|
||||
this.$submitButton = this.$container.find(this.submitButton);
|
||||
|
||||
render: function( html ) {
|
||||
var fields = html || '';
|
||||
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
// We pass the context object to the template so that
|
||||
// we can perform variable interpolation using sprintf
|
||||
context: {
|
||||
fields: fields,
|
||||
currentProvider: this.currentProvider,
|
||||
errorMessage: this.errorMessage,
|
||||
providers: this.providers,
|
||||
hasSecondaryProviders: this.hasSecondaryProviders,
|
||||
platformName: this.platformName
|
||||
/* If we're already authenticated with a third-party
|
||||
* provider, try logging in. The easiest way to do this
|
||||
* is to simply submit the form.
|
||||
*/
|
||||
if (this.currentProvider) {
|
||||
this.model.save();
|
||||
}
|
||||
}));
|
||||
},
|
||||
|
||||
this.postRender();
|
||||
forgotPassword: function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
return this;
|
||||
},
|
||||
this.trigger('password-help');
|
||||
this.element.hide( this.$resetSuccess );
|
||||
},
|
||||
|
||||
postRender: function() {
|
||||
this.$container = $(this.el);
|
||||
postFormSubmission: function() {
|
||||
this.element.hide( this.$resetSuccess );
|
||||
},
|
||||
|
||||
this.$form = this.$container.find('form');
|
||||
this.$errors = this.$container.find('.submission-error');
|
||||
this.$resetSuccess = this.$container.find('.js-reset-success');
|
||||
this.$authError = this.$container.find('.already-authenticated-msg');
|
||||
this.$submitButton = this.$container.find(this.submitButton);
|
||||
|
||||
/* If we're already authenticated with a third-party
|
||||
* provider, try logging in. The easiest way to do this
|
||||
* is to simply submit the form.
|
||||
*/
|
||||
if (this.currentProvider) {
|
||||
this.model.save();
|
||||
}
|
||||
},
|
||||
|
||||
forgotPassword: function( event ) {
|
||||
event.preventDefault();
|
||||
|
||||
this.trigger('password-help');
|
||||
this.element.hide( this.$resetSuccess );
|
||||
},
|
||||
|
||||
postFormSubmission: function() {
|
||||
this.element.hide( this.$resetSuccess );
|
||||
},
|
||||
|
||||
resetEmail: function() {
|
||||
this.element.hide( this.$errors );
|
||||
this.element.show( this.$resetSuccess );
|
||||
},
|
||||
|
||||
thirdPartyAuth: function( event ) {
|
||||
var providerUrl = $(event.currentTarget).data('provider-url') || '';
|
||||
|
||||
if (providerUrl) {
|
||||
window.location.href = providerUrl;
|
||||
}
|
||||
},
|
||||
|
||||
saveSuccess: function() {
|
||||
this.trigger('auth-complete');
|
||||
this.element.hide( this.$resetSuccess );
|
||||
},
|
||||
|
||||
saveError: function( error ) {
|
||||
this.errors = ['<li>' + error.responseText + '</li>'];
|
||||
this.setErrors();
|
||||
this.element.hide( this.$resetSuccess );
|
||||
|
||||
/* If we've gotten a 403 error, it means that we've successfully
|
||||
* authenticated with a third-party provider, but we haven't
|
||||
* linked the account to an EdX account. In this case,
|
||||
* we need to prompt the user to enter a little more information
|
||||
* to complete the registration process.
|
||||
*/
|
||||
if ( error.status === 403 &&
|
||||
error.responseText === 'third-party-auth' &&
|
||||
this.currentProvider ) {
|
||||
this.element.show( this.$authError );
|
||||
resetEmail: function() {
|
||||
this.element.hide( this.$errors );
|
||||
} else {
|
||||
this.element.hide( this.$authError );
|
||||
this.element.show( this.$errors );
|
||||
this.element.show( this.$resetSuccess );
|
||||
},
|
||||
|
||||
thirdPartyAuth: function( event ) {
|
||||
var providerUrl = $(event.currentTarget).data('provider-url') || '';
|
||||
|
||||
if (providerUrl) {
|
||||
window.location.href = providerUrl;
|
||||
}
|
||||
},
|
||||
|
||||
saveSuccess: function() {
|
||||
this.trigger('auth-complete');
|
||||
this.element.hide( this.$resetSuccess );
|
||||
},
|
||||
|
||||
saveError: function( error ) {
|
||||
this.errors = ['<li>' + error.responseText + '</li>'];
|
||||
this.setErrors();
|
||||
this.element.hide( this.$resetSuccess );
|
||||
|
||||
/* If we've gotten a 403 error, it means that we've successfully
|
||||
* authenticated with a third-party provider, but we haven't
|
||||
* linked the account to an EdX account. In this case,
|
||||
* we need to prompt the user to enter a little more information
|
||||
* to complete the registration process.
|
||||
*/
|
||||
if ( error.status === 403 &&
|
||||
error.responseText === 'third-party-auth' &&
|
||||
this.currentProvider ) {
|
||||
this.element.show( this.$authError );
|
||||
this.element.hide( this.$errors );
|
||||
} else {
|
||||
this.element.hide( this.$authError );
|
||||
this.element.show( this.$errors );
|
||||
}
|
||||
this.toggleDisableButton(false);
|
||||
}
|
||||
this.toggleDisableButton(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
})(jQuery, _, gettext);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
|
||||
@@ -1,48 +1,48 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, gettext) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'js/student_account/views/FormView'
|
||||
],
|
||||
function($, FormView) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return FormView.extend({
|
||||
el: '#password-reset-form',
|
||||
|
||||
edx.student.account.PasswordResetView = edx.student.account.FormView.extend({
|
||||
el: '#password-reset-form',
|
||||
tpl: '#password_reset-tpl',
|
||||
|
||||
tpl: '#password_reset-tpl',
|
||||
events: {
|
||||
'click .js-reset': 'submitForm'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .js-reset': 'submitForm'
|
||||
},
|
||||
formType: 'password-reset',
|
||||
|
||||
formType: 'password-reset',
|
||||
requiredStr: '',
|
||||
|
||||
requiredStr: '',
|
||||
submitButton: '.js-reset',
|
||||
|
||||
submitButton: '.js-reset',
|
||||
preRender: function() {
|
||||
this.element.show( $( this.el ) );
|
||||
this.element.show( $( this.el ).parent() );
|
||||
this.listenTo( this.model, 'sync', this.saveSuccess );
|
||||
},
|
||||
|
||||
preRender: function() {
|
||||
this.element.show( $( this.el ) );
|
||||
this.element.show( $( this.el ).parent() );
|
||||
this.listenTo( this.model, 'sync', this.saveSuccess );
|
||||
},
|
||||
toggleErrorMsg: function( show ) {
|
||||
if ( show ) {
|
||||
this.setErrors();
|
||||
this.toggleDisableButton(false);
|
||||
} else {
|
||||
this.element.hide( this.$errors );
|
||||
}
|
||||
},
|
||||
|
||||
toggleErrorMsg: function( show ) {
|
||||
if ( show ) {
|
||||
this.setErrors();
|
||||
this.toggleDisableButton(false);
|
||||
} else {
|
||||
this.element.hide( this.$errors );
|
||||
saveSuccess: function() {
|
||||
this.trigger('password-email-sent');
|
||||
|
||||
// Destroy the view (but not el) and unbind events
|
||||
this.$el.empty().off();
|
||||
this.stopListening();
|
||||
}
|
||||
},
|
||||
|
||||
saveSuccess: function() {
|
||||
this.trigger('password-email-sent');
|
||||
|
||||
// Destroy the view (but not el) and unbind events
|
||||
this.$el.empty().off();
|
||||
this.stopListening();
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
})(jQuery, gettext);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -1,100 +1,102 @@
|
||||
var edx = edx || {};
|
||||
|
||||
(function($, _, gettext) {
|
||||
;(function (define) {
|
||||
'use strict';
|
||||
define([
|
||||
'jquery',
|
||||
'underscore',
|
||||
'js/student_account/views/FormView'
|
||||
],
|
||||
function($, _, FormView) {
|
||||
|
||||
edx.student = edx.student || {};
|
||||
edx.student.account = edx.student.account || {};
|
||||
return FormView.extend({
|
||||
el: '#register-form',
|
||||
|
||||
edx.student.account.RegisterView = edx.student.account.FormView.extend({
|
||||
el: '#register-form',
|
||||
tpl: '#register-tpl',
|
||||
|
||||
tpl: '#register-tpl',
|
||||
events: {
|
||||
'click .js-register': 'submitForm',
|
||||
'click .login-provider': 'thirdPartyAuth'
|
||||
},
|
||||
|
||||
events: {
|
||||
'click .js-register': 'submitForm',
|
||||
'click .login-provider': 'thirdPartyAuth'
|
||||
},
|
||||
formType: 'register',
|
||||
|
||||
formType: 'register',
|
||||
submitButton: '.js-register',
|
||||
|
||||
submitButton: '.js-register',
|
||||
preRender: function( data ) {
|
||||
this.providers = data.thirdPartyAuth.providers || [];
|
||||
this.hasSecondaryProviders = (
|
||||
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
|
||||
);
|
||||
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
|
||||
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
|
||||
this.platformName = data.platformName;
|
||||
this.autoSubmit = data.thirdPartyAuth.autoSubmitRegForm;
|
||||
|
||||
preRender: function( data ) {
|
||||
this.providers = data.thirdPartyAuth.providers || [];
|
||||
this.hasSecondaryProviders = (
|
||||
data.thirdPartyAuth.secondaryProviders && data.thirdPartyAuth.secondaryProviders.length
|
||||
);
|
||||
this.currentProvider = data.thirdPartyAuth.currentProvider || '';
|
||||
this.errorMessage = data.thirdPartyAuth.errorMessage || '';
|
||||
this.platformName = data.platformName;
|
||||
this.autoSubmit = data.thirdPartyAuth.autoSubmitRegForm;
|
||||
this.listenTo( this.model, 'sync', this.saveSuccess );
|
||||
},
|
||||
|
||||
this.listenTo( this.model, 'sync', this.saveSuccess );
|
||||
},
|
||||
render: function( html ) {
|
||||
var fields = html || '';
|
||||
|
||||
render: function( html ) {
|
||||
var fields = html || '';
|
||||
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
/* We pass the context object to the template so that
|
||||
* we can perform variable interpolation using sprintf
|
||||
*/
|
||||
context: {
|
||||
fields: fields,
|
||||
currentProvider: this.currentProvider,
|
||||
errorMessage: this.errorMessage,
|
||||
providers: this.providers,
|
||||
hasSecondaryProviders: this.hasSecondaryProviders,
|
||||
platformName: this.platformName
|
||||
}
|
||||
}));
|
||||
|
||||
this.postRender();
|
||||
|
||||
if (this.autoSubmit) {
|
||||
$(this.el).hide();
|
||||
$('#register-honor_code').prop('checked', true);
|
||||
this.submitForm();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
thirdPartyAuth: function( event ) {
|
||||
var providerUrl = $(event.currentTarget).data('provider-url') || '';
|
||||
|
||||
if ( providerUrl ) {
|
||||
window.location.href = providerUrl;
|
||||
}
|
||||
},
|
||||
|
||||
saveSuccess: function() {
|
||||
this.trigger('auth-complete');
|
||||
},
|
||||
|
||||
saveError: function( error ) {
|
||||
$(this.el).show(); // Show in case the form was hidden for auto-submission
|
||||
this.errors = _.flatten(
|
||||
_.map(
|
||||
JSON.parse(error.responseText),
|
||||
function(error_list) {
|
||||
return _.map(
|
||||
error_list,
|
||||
function(error) { return "<li>" + error.user_message + "</li>"; }
|
||||
);
|
||||
$(this.el).html( _.template( this.tpl, {
|
||||
/* We pass the context object to the template so that
|
||||
* we can perform variable interpolation using sprintf
|
||||
*/
|
||||
context: {
|
||||
fields: fields,
|
||||
currentProvider: this.currentProvider,
|
||||
errorMessage: this.errorMessage,
|
||||
providers: this.providers,
|
||||
hasSecondaryProviders: this.hasSecondaryProviders,
|
||||
platformName: this.platformName
|
||||
}
|
||||
)
|
||||
);
|
||||
this.setErrors();
|
||||
this.toggleDisableButton(false);
|
||||
},
|
||||
}));
|
||||
|
||||
postFormSubmission: function() {
|
||||
if (_.compact(this.errors).length) {
|
||||
// The form did not get submitted due to validation errors.
|
||||
this.postRender();
|
||||
|
||||
if (this.autoSubmit) {
|
||||
$(this.el).hide();
|
||||
$('#register-honor_code').prop('checked', true);
|
||||
this.submitForm();
|
||||
}
|
||||
|
||||
return this;
|
||||
},
|
||||
|
||||
thirdPartyAuth: function( event ) {
|
||||
var providerUrl = $(event.currentTarget).data('provider-url') || '';
|
||||
|
||||
if ( providerUrl ) {
|
||||
window.location.href = providerUrl;
|
||||
}
|
||||
},
|
||||
|
||||
saveSuccess: function() {
|
||||
this.trigger('auth-complete');
|
||||
},
|
||||
|
||||
saveError: function( error ) {
|
||||
$(this.el).show(); // Show in case the form was hidden for auto-submission
|
||||
this.errors = _.flatten(
|
||||
_.map(
|
||||
JSON.parse(error.responseText),
|
||||
function(error_list) {
|
||||
return _.map(
|
||||
error_list,
|
||||
function(error) { return '<li>' + error.user_message + '</li>'; }
|
||||
);
|
||||
}
|
||||
)
|
||||
);
|
||||
this.setErrors();
|
||||
this.toggleDisableButton(false);
|
||||
},
|
||||
|
||||
postFormSubmission: function() {
|
||||
if (_.compact(this.errors).length) {
|
||||
// The form did not get submitted due to validation errors.
|
||||
$(this.el).show(); // Show in case the form was hidden for auto-submission
|
||||
}
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
})(jQuery, _, gettext);
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
@@ -63,6 +63,7 @@ lib_paths:
|
||||
- xmodule_js/common_static/js/test/i18n.js
|
||||
- xmodule_js/common_static/js/vendor/date.js
|
||||
- xmodule_js/common_static/js/vendor/moment.min.js
|
||||
- xmodule_js/common_static/js/utils/edx.utils.validate.js
|
||||
|
||||
# Paths to source JavaScript files
|
||||
src_paths:
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
'js/groups/views/cohorts_dashboard_factory',
|
||||
'js/search/course/course_search_factory',
|
||||
'js/search/dashboard/dashboard_search_factory',
|
||||
'js/student_account/logistration_factory',
|
||||
'js/student_account/views/account_settings_factory',
|
||||
'js/student_account/views/finish_auth_factory',
|
||||
'js/student_profile/views/learner_profile_factory',
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
<%! from django.utils.translation import ugettext as _ %>
|
||||
<%!
|
||||
import json
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.core.lib.json_utils import EscapedEdxJSONEncoder
|
||||
%>
|
||||
<%namespace name='static' file='/static_content.html'/>
|
||||
|
||||
<%inherit file="../main.html" />
|
||||
@@ -6,9 +10,10 @@
|
||||
<%block name="pagetitle">${_("Sign in or Register")}</%block>
|
||||
|
||||
<%block name="js_extra">
|
||||
<script src="${static.url('js/vendor/underscore.string.min.js')}"></script>
|
||||
<script src="${static.url('js/vendor/history.js')}"></script>
|
||||
<%static:js group='student_account'/>
|
||||
<%static:require_module module_name="js/student_account/logistration_factory" class_name="LogistrationFactory">
|
||||
var options = ${ json.dumps(data, cls=EscapedEdxJSONEncoder) };
|
||||
LogistrationFactory(options);
|
||||
</%static:require_module>
|
||||
</%block>
|
||||
|
||||
<%block name="header_extras">
|
||||
@@ -20,17 +25,7 @@
|
||||
</%block>
|
||||
|
||||
<div class="section-bkg-wrapper">
|
||||
<div id="login-and-registration-container"
|
||||
class="login-register"
|
||||
data-initial-mode="${initial_mode}"
|
||||
data-third-party-auth='${third_party_auth|h}'
|
||||
data-third-party-auth-hint='${third_party_auth_hint}'
|
||||
data-next-url='${login_redirect_url|h}'
|
||||
data-platform-name='${platform_name}'
|
||||
data-login-form-desc='${login_form_desc|h}'
|
||||
data-registration-form-desc='${registration_form_desc|h}'
|
||||
data-password-reset-form-desc='${password_reset_form_desc|h}'
|
||||
/>
|
||||
<div id="login-and-registration-container" class="login-register" />
|
||||
</div>
|
||||
|
||||
% if settings.FEATURES.get('ENABLE_COMBINED_LOGIN_REGISTRATION'):
|
||||
|
||||
Reference in New Issue
Block a user