diff --git a/lms/djangoapps/student_account/test/test_views.py b/lms/djangoapps/student_account/test/test_views.py
index e715e2ceef..9c2076dc50 100644
--- a/lms/djangoapps/student_account/test/test_views.py
+++ b/lms/djangoapps/student_account/test/test_views.py
@@ -8,6 +8,7 @@ import json
import mock
import ddt
+import markupsafe
from django.test import TestCase
from django.conf import settings
from django.core.urlresolvers import reverse
@@ -551,12 +552,16 @@ class StudentAccountLoginAndRegistrationTest(ModuleStoreTestCase):
def _assert_third_party_auth_data(self, response, current_provider, providers):
"""Verify that third party auth info is rendered correctly in a DOM data attribute. """
- expected_data = u"data-third-party-auth='{auth_info}'".format(
- auth_info=json.dumps({
+ auth_info = markupsafe.escape(
+ json.dumps({
"currentProvider": current_provider,
"providers": providers
})
)
+
+ expected_data = u"data-third-party-auth='{auth_info}'".format(
+ auth_info=auth_info
+ )
self.assertContains(response, expected_data)
def _third_party_login_url(self, backend_name, auth_entry, course_id=None, redirect_url=None):
diff --git a/lms/djangoapps/student_account/views.py b/lms/djangoapps/student_account/views.py
index 9460f5fe01..8af87b358e 100644
--- a/lms/djangoapps/student_account/views.py
+++ b/lms/djangoapps/student_account/views.py
@@ -7,7 +7,8 @@ from django.http import (
HttpResponse, HttpResponseBadRequest, HttpResponseForbidden
)
from django.shortcuts import redirect
-from django.core.urlresolvers import reverse
+from django.http import HttpRequest
+from django.core.urlresolvers import reverse, resolve
from django.core.mail import send_mail
from django.utils.translation import ugettext as _
from django_future.csrf import ensure_csrf_cookie
@@ -68,13 +69,24 @@ def login_and_registration_form(request, initial_mode="login"):
if request.user.is_authenticated():
return redirect(reverse('dashboard'))
+ # Retrieve the form descriptions from the user API
+ form_descriptions = _get_form_descriptions(request)
+
# Otherwise, render the combined login/registration page
context = {
'disable_courseware_js': True,
'initial_mode': initial_mode,
'third_party_auth': json.dumps(_third_party_auth_context(request)),
'platform_name': settings.PLATFORM_NAME,
- 'responsive': True
+ 'responsive': 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'],
}
return render_to_response('student_account/login_and_register.html', context)
@@ -317,3 +329,51 @@ def _third_party_auth_context(request):
context["currentProvider"] = current_provider.NAME
return context
+
+
+def _get_form_descriptions(request):
+ """Retrieve form descriptions from the user API.
+
+ Arguments:
+ request (HttpRequest): The original request, used to retrieve session info.
+
+ Returns:
+ dict: Keys are 'login', 'registration', and 'password_reset';
+ values are the JSON-serialized form descriptions.
+
+ """
+ return {
+ 'login': _local_server_get('/user_api/v1/account/login_session/', request.session),
+ 'registration': _local_server_get('/user_api/v1/account/registration/', request.session),
+ 'password_reset': _local_server_get('/user_api/v1/account/password_reset/', request.session)
+ }
+
+
+def _local_server_get(url, session):
+ """Simulate a server-server GET request for an in-process API.
+
+ Arguments:
+ url (str): The URL of the request (excluding the protocol and domain)
+ session (SessionStore): The session of the original request,
+ used to get past the CSRF checks.
+
+ Returns:
+ str: The content of the response
+
+ """
+ # Since the user API is currently run in-process,
+ # we simulate the server-server API call by constructing
+ # our own request object. We don't need to include much
+ # information in the request except for the session
+ # (to get past through CSRF validation)
+ request = HttpRequest()
+ request.method = "GET"
+ request.session = session
+
+ # Call the Django view function, simulating
+ # the server-server API call
+ view, args, kwargs = resolve(url)
+ response = view(request, *args, **kwargs)
+
+ # Return the content of the response
+ return response.content
diff --git a/lms/static/js/spec/student_account/access_spec.js b/lms/static/js/spec/student_account/access_spec.js
index 212269ca45..5a0ac71d1a 100644
--- a/lms/static/js/spec/student_account/access_spec.js
+++ b/lms/static/js/spec/student_account/access_spec.js
@@ -13,20 +13,6 @@ define([
var requests = null,
view = null,
- AJAX_INFO = {
- register: {
- url: '/user_api/v1/account/registration/',
- requestIndex: 1
- },
- login: {
- url: '/user_api/v1/account/login_session/',
- requestIndex: 0
- },
- password_reset: {
- url: '/user_api/v1/account/password_reset/',
- requestIndex: 1
- }
- },
FORM_DESCRIPTION = {
method: 'post',
submit_url: '/submit',
@@ -58,16 +44,6 @@ define([
FORWARD_URL = '/courseware/next',
COURSE_KEY = 'edx/DemoX/Fall';
- var ajaxAssertAndRespond = function(url, requestIndex) {
- // Verify that the client contacts the server as expected
- AjaxHelpers.expectJsonRequest(requests, 'GET', url, null, requestIndex);
-
- /* Simulate a response from the server containing
- /* a dummy form description
- */
- AjaxHelpers.respondWithJson(requests, FORM_DESCRIPTION);
- };
-
var ajaxSpyAndInitialize = function(that, mode) {
// Spy on AJAX requests
requests = AjaxHelpers.requests(that);
@@ -79,7 +55,10 @@ define([
currentProvider: null,
providers: []
},
- platformName: 'edX'
+ platformName: 'edX',
+ loginFormDesc: FORM_DESCRIPTION,
+ registrationFormDesc: FORM_DESCRIPTION,
+ passwordResetFormDesc: FORM_DESCRIPTION
});
// Mock the redirect call
@@ -88,9 +67,6 @@ define([
// Mock the enrollment and shopping cart interfaces
spyOn( EnrollmentInterface, 'enroll' ).andCallFake( function() {} );
spyOn( ShoppingCartInterface, 'addCourseToCart' ).andCallFake( function() {} );
-
- // Initialize the subview
- ajaxAssertAndRespond(AJAX_INFO[mode].url);
};
var assertForms = function(visibleType, hiddenType) {
@@ -106,8 +82,6 @@ define([
// Load form corresponding to the change event
view.toggleForm(changeEvent);
-
- ajaxAssertAndRespond(AJAX_INFO[type].url, AJAX_INFO[type].requestIndex);
};
/**
@@ -175,11 +149,6 @@ define([
// Simulate a click on the reset password link
view.resetPassword();
- ajaxAssertAndRespond(
- AJAX_INFO.password_reset.url,
- AJAX_INFO.password_reset.requestIndex
- );
-
// Verify that the password reset wrapper is populated
expect($('#password-reset-wrapper')).not.toBeEmpty();
});
@@ -253,26 +222,6 @@ define([
expect( view.redirect ).toHaveBeenCalledWith( "/dashboard" );
});
- it('displays an error if a form definition could not be loaded', function() {
- // Spy on AJAX requests
- requests = AjaxHelpers.requests(this);
-
- // Init AccessView
- view = new AccessView({
- mode: 'login',
- thirdPartyAuth: {
- currentProvider: null,
- providers: []
- },
- platformName: 'edX'
- });
-
- // Simulate an error from the LMS servers
- AjaxHelpers.respondWithError(requests);
-
- // Error message should be displayed
- expect( $('#form-load-fail').hasClass('hidden') ).toBe(false);
- });
});
}
);
diff --git a/lms/static/js/student_account/accessApp.js b/lms/static/js/student_account/accessApp.js
index 9b41e41439..4c67707fcf 100644
--- a/lms/static/js/student_account/accessApp.js
+++ b/lms/static/js/student_account/accessApp.js
@@ -6,9 +6,14 @@ var edx = edx || {};
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
+ var container = $('#login-and-registration-container');
+
return new edx.student.account.AccessView({
- mode: $('#login-and-registration-container').data('initial-mode'),
- thirdPartyAuth: $('#login-and-registration-container').data('third-party-auth'),
- platformName: $('#login-and-registration-container').data('platform-name')
+ mode: container.data('initial-mode'),
+ thirdPartyAuth: container.data('third-party-auth'),
+ 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);
diff --git a/lms/static/js/student_account/views/AccessView.js b/lms/static/js/student_account/views/AccessView.js
index 7bcc13a4c5..d36e9385fe 100644
--- a/lms/static/js/student_account/views/AccessView.js
+++ b/lms/static/js/student_account/views/AccessView.js
@@ -40,12 +40,20 @@ var edx = edx || {};
_.mixin( _s.exports() );
this.tpl = $(this.tpl).html();
+
this.activeForm = obj.mode || 'login';
+
this.thirdPartyAuth = obj.thirdPartyAuth || {
currentProvider: null,
providers: []
};
+ this.formDescriptions = {
+ login: obj.loginFormDesc,
+ register: obj.registrationFormDesc,
+ reset: obj.passwordResetFormDesc
+ };
+
this.platformName = obj.platformName;
// The login view listens for 'sync' events from the reset model
@@ -73,82 +81,64 @@ var edx = edx || {};
},
loadForm: function( type ) {
- this.getFormData( type, this );
+ var loadFunc = _.bind( this.load[type], this );
+ loadFunc( this.formDescriptions[type] );
},
load: {
- login: function( data, context ) {
+ login: function( data ) {
var model = new edx.student.account.LoginModel({}, {
method: data.method,
url: data.submit_url
});
- context.subview.login = new edx.student.account.LoginView({
+ this.subview.login = new edx.student.account.LoginView({
fields: data.fields,
model: model,
- resetModel: context.resetModel,
- thirdPartyAuth: context.thirdPartyAuth,
- platformName: context.platformName
+ resetModel: this.resetModel,
+ thirdPartyAuth: this.thirdPartyAuth,
+ platformName: this.platformName
});
// Listen for 'password-help' event to toggle sub-views
- context.listenTo( context.subview.login, 'password-help', context.resetPassword );
+ this.listenTo( this.subview.login, 'password-help', this.resetPassword );
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
- context.listenTo( context.subview.login, 'auth-complete', context.authComplete );
+ this.listenTo( this.subview.login, 'auth-complete', this.authComplete );
},
- reset: function( data, context ) {
- context.resetModel.ajaxType = data.method;
- context.resetModel.urlRoot = data.submit_url;
+ reset: function( data ) {
+ this.resetModel.ajaxType = data.method;
+ this.resetModel.urlRoot = data.submit_url;
- context.subview.passwordHelp = new edx.student.account.PasswordResetView({
+ this.subview.passwordHelp = new edx.student.account.PasswordResetView({
fields: data.fields,
- model: context.resetModel
+ model: this.resetModel
});
// Listen for 'password-email-sent' event to toggle sub-views
- context.listenTo( context.subview.passwordHelp, 'password-email-sent', context.passwordEmailSent );
+ this.listenTo( this.subview.passwordHelp, 'password-email-sent', this.passwordEmailSent );
},
- register: function( data, context ) {
+ register: function( data ) {
var model = new edx.student.account.RegisterModel({}, {
method: data.method,
url: data.submit_url
});
- context.subview.register = new edx.student.account.RegisterView({
+ this.subview.register = new edx.student.account.RegisterView({
fields: data.fields,
model: model,
- thirdPartyAuth: context.thirdPartyAuth,
- platformName: context.platformName
+ thirdPartyAuth: this.thirdPartyAuth,
+ platformName: this.platformName
});
// Listen for 'auth-complete' event so we can enroll/redirect the user appropriately.
- context.listenTo( context.subview.register, 'auth-complete', context.authComplete );
+ this.listenTo( this.subview.register, 'auth-complete', this.authComplete );
}
},
- getFormData: function( type, context ) {
- var urls = {
- login: 'login_session',
- register: 'registration',
- reset: 'password_reset'
- };
-
- $.ajax({
- url: '/user_api/v1/account/' + urls[type] + '/',
- type: 'GET',
- dataType: 'json',
- context: this,
- success: function( data ) {
- this.load[type]( data, context );
- },
- error: this.showFormError
- });
- },
-
passwordEmailSent: function() {
this.element.hide( $(this.el).find('#password-reset-anchor') );
this.element.show( $('#login-anchor') );
@@ -165,10 +155,6 @@ var edx = edx || {};
this.element.scrollTop( $('#password-reset-anchor') );
},
- showFormError: function() {
- this.element.show( $('#form-load-fail') );
- },
-
toggleForm: function( e ) {
var type = $(e.currentTarget).data('type'),
$form = $('#' + type + '-form'),
diff --git a/lms/templates/student_account/access.underscore b/lms/templates/student_account/access.underscore
index 3bff3137c9..2eee3a2a3d 100644
--- a/lms/templates/student_account/access.underscore
+++ b/lms/templates/student_account/access.underscore
@@ -1,9 +1,3 @@
-
-