Merge pull request #6690 from edx/will/inline-user-api-forms
Inline the user api form description calls
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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'),
|
||||
|
||||
@@ -1,9 +1,3 @@
|
||||
<section id="form-load-fail" class="form-type hidden">
|
||||
<div class="status submission-error">
|
||||
<p class="message-copy"><%- gettext("Sorry, we're having some technical problems. Wait a few minutes and try again.") %></p>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section id="login-anchor" class="form-type">
|
||||
<div id="login-form" class="form-wrapper <% if ( mode !== 'login' ) { %>hidden<% } %>"></div>
|
||||
</section>
|
||||
|
||||
@@ -26,8 +26,11 @@
|
||||
<div id="login-and-registration-container"
|
||||
class="login-register"
|
||||
data-initial-mode="${initial_mode}"
|
||||
data-third-party-auth='${third_party_auth}'
|
||||
data-third-party-auth='${third_party_auth|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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user