Commit 7437bcfe by Braden MacDonald

New provider config options, New Institution Login Menu - PR 8603

parent 5bf0b179
......@@ -1498,6 +1498,13 @@ def create_account_with_params(request, params):
dog_stats_api.increment("common.student.account_created")
# If the user is registering via 3rd party auth, track which provider they use
third_party_provider = None
running_pipeline = None
if third_party_auth.is_enabled() and pipeline.running(request):
running_pipeline = pipeline.get(request)
third_party_provider = provider.Registry.get_from_pipeline(running_pipeline)
# Track the user's registration
if settings.FEATURES.get('SEGMENT_IO_LMS') and hasattr(settings, 'SEGMENT_IO_LMS_KEY'):
tracking_context = tracker.get_tracker().resolve_context()
......@@ -1506,20 +1513,13 @@ def create_account_with_params(request, params):
'username': user.username,
})
# If the user is registering via 3rd party auth, track which provider they use
provider_name = None
if third_party_auth.is_enabled() and pipeline.running(request):
running_pipeline = pipeline.get(request)
current_provider = provider.Registry.get_from_pipeline(running_pipeline)
provider_name = current_provider.name
analytics.track(
user.id,
"edx.bi.user.account.registered",
{
'category': 'conversion',
'label': params.get('course_id'),
'provider': provider_name
'provider': third_party_provider.name if third_party_provider else None
},
context={
'Google Analytics': {
......@@ -1536,6 +1536,7 @@ def create_account_with_params(request, params):
# 2. Random user generation for other forms of testing.
# 3. External auth bypassing activation.
# 4. Have the platform configured to not require e-mail activation.
# 5. Registering a new user using a trusted third party provider (with skip_email_verification=True)
#
# Note that this feature is only tested as a flag set one way or
# the other for *new* systems. we need to be careful about
......@@ -1544,7 +1545,11 @@ def create_account_with_params(request, params):
send_email = (
not settings.FEATURES.get('SKIP_EMAIL_VALIDATION', None) and
not settings.FEATURES.get('AUTOMATIC_AUTH_FOR_TESTING') and
not (do_external_auth and settings.FEATURES.get('BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH'))
not (do_external_auth and settings.FEATURES.get('BYPASS_ACTIVATION_EMAIL_FOR_EXTAUTH')) and
not (
third_party_provider and third_party_provider.skip_email_verification and
user.email == running_pipeline['kwargs'].get('details', {}).get('email')
)
)
if send_email:
context = {
......
......@@ -8,7 +8,7 @@ from django.conf import settings
from django.core.exceptions import ValidationError
from django.db import models
from django.utils import timezone
from django.utils.translation import ugettext as _
from django.utils.translation import ugettext_lazy as _
import json
import logging
from social.backends.base import BaseAuth
......@@ -54,7 +54,7 @@ class AuthNotConfigured(SocialAuthBaseException):
self.provider_name = provider_name
def __str__(self):
return _('Authentication with {} is currently unavailable.').format(
return _('Authentication with {} is currently unavailable.').format( # pylint: disable=no-member
self.provider_name
)
......@@ -68,10 +68,34 @@ class ProviderConfig(ConfigurationModel):
help_text=(
'The Font Awesome (or custom) icon class to use on the login button for this provider. '
'Examples: fa-google-plus, fa-facebook, fa-linkedin, fa-sign-in, fa-university'
))
),
)
name = models.CharField(max_length=50, blank=False, help_text="Name of this provider (shown to users)")
secondary = models.BooleanField(
default=False,
help_text=_(
'Secondary providers are displayed less prominently, '
'in a separate list of "Institution" login providers.'
),
)
skip_registration_form = models.BooleanField(
default=False,
help_text=_(
"If this option is enabled, users will not be asked to confirm their details "
"(name, email, etc.) during the registration process. Only select this option "
"for trusted providers that are known to provide accurate user information."
),
)
skip_email_verification = models.BooleanField(
default=False,
help_text=_(
"If this option is selected, users will not be required to confirm their "
"email, and their account will be activated immediately upon registration."
),
)
prefix = None # used for provider_id. Set to a string value in subclass
backend_name = None # Set to a field or fixed value in subclass
# "enabled" field is inherited from ConfigurationModel
class Meta(object): # pylint: disable=missing-docstring
......
......@@ -503,12 +503,19 @@ def ensure_user_information(strategy, auth_entry, backend=None, user=None, socia
"""Redirects to the registration page."""
return redirect(AUTH_DISPATCH_URLS[AUTH_ENTRY_REGISTER])
def should_force_account_creation():
""" For some third party providers, we auto-create user accounts """
current_provider = provider.Registry.get_from_pipeline({'backend': backend.name, 'kwargs': kwargs})
return current_provider and current_provider.skip_email_verification
if not user:
if auth_entry in [AUTH_ENTRY_LOGIN_API, AUTH_ENTRY_REGISTER_API]:
return HttpResponseBadRequest()
elif auth_entry in [AUTH_ENTRY_LOGIN, AUTH_ENTRY_LOGIN_2]:
# User has authenticated with the third party provider but we don't know which edX
# account corresponds to them yet, if any.
if should_force_account_creation():
return dispatch_to_register()
return dispatch_to_login()
elif auth_entry in [AUTH_ENTRY_REGISTER, AUTH_ENTRY_REGISTER_2]:
# User has authenticated with the third party provider and now wants to finish
......
"""
Third_party_auth integration tests using a mock version of the TestShib provider
"""
from django.contrib.auth.models import User
from django.core.urlresolvers import reverse
import httpretty
from mock import patch
......@@ -62,8 +63,6 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
self.assertEqual(response.status_code, 200)
self.assertIn('Authentication with TestShib is currently unavailable.', response.content)
# Note: the following patch is only needed until https://github.com/edx/edx-platform/pull/8262 is merged
@patch.dict("django.conf.settings.FEATURES", {"AUTOMATIC_AUTH_FOR_TESTING": True})
def test_register(self):
self._configure_testshib_provider()
self._freeze_time(timestamp=1434326820) # This is the time when the saved request/response was recorded.
......@@ -107,6 +106,7 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
# Now check that we can login again:
self.client.logout()
self._verify_user_email('myself@testshib.org')
self._test_return_login()
def test_login(self):
......@@ -222,3 +222,9 @@ class TestShibIntegrationTest(testutil.SAMLTestCase):
content_type='application/x-www-form-urlencoded',
data=self._read_data_file('testshib_response.txt'),
)
def _verify_user_email(self, email):
""" Mark the user with the given email as verified """
user = User.objects.get(email=email)
user.is_active = True
user.save()
......@@ -359,6 +359,7 @@ class StudentAccountLoginAndRegistrationTest(ThirdPartyAuthTestMixin, UrlResetMi
json.dumps({
"currentProvider": current_provider,
"providers": providers,
"secondaryProviders": [],
"finishAuthUrl": finish_auth_url,
"errorMessage": None,
})
......
......@@ -164,13 +164,14 @@ def _third_party_auth_context(request, redirect_to):
context = {
"currentProvider": None,
"providers": [],
"secondaryProviders": [],
"finishAuthUrl": None,
"errorMessage": None,
}
if third_party_auth.is_enabled():
context["providers"] = [
{
for enabled in third_party_auth.provider.Registry.enabled():
info = {
"id": enabled.provider_id,
"name": enabled.name,
"iconClass": enabled.icon_class,
......@@ -185,8 +186,7 @@ def _third_party_auth_context(request, redirect_to):
redirect_url=redirect_to,
),
}
for enabled in third_party_auth.provider.Registry.enabled()
]
context["providers" if not enabled.secondary else "secondaryProviders"].append(info)
running_pipeline = pipeline.get(request)
if running_pipeline is not None:
......@@ -194,6 +194,10 @@ def _third_party_auth_context(request, redirect_to):
context["currentProvider"] = current_provider.name
context["finishAuthUrl"] = pipeline.get_complete_url(current_provider.backend_name)
if current_provider.skip_registration_form:
# As a reliable way of "skipping" the registration form, we just submit it automatically
context["autoSubmitRegForm"] = True
# Check for any error messages we may want to display:
for msg in messages.get_messages(request):
if msg.extra_tags.split()[0] == "social-auth":
......
......@@ -1274,6 +1274,7 @@ student_account_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',
]
......
......@@ -84,6 +84,7 @@
'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',
......@@ -410,6 +411,14 @@
'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']
......@@ -450,6 +459,7 @@
'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',
......@@ -613,6 +623,7 @@
'lms/include/js/spec/student_account/access_spec.js',
'lms/include/js/spec/student_account/finish_auth_spec.js',
'lms/include/js/spec/student_account/login_spec.js',
'lms/include/js/spec/student_account/institution_login_spec.js',
'lms/include/js/spec/student_account/register_spec.js',
'lms/include/js/spec/student_account/password_reset_spec.js',
'lms/include/js/spec/student_account/enrollment_spec.js',
......
......@@ -58,6 +58,7 @@ define([
thirdPartyAuth: {
currentProvider: null,
providers: [],
secondaryProviders: [{name: "provider"}],
finishAuthUrl: finishAuthUrl
},
nextUrl: nextUrl, // undefined for default
......@@ -97,6 +98,8 @@ define([
TemplateHelpers.installTemplate('templates/student_account/register');
TemplateHelpers.installTemplate('templates/student_account/password_reset');
TemplateHelpers.installTemplate('templates/student_account/form_field');
TemplateHelpers.installTemplate('templates/student_account/institution_login');
TemplateHelpers.installTemplate('templates/student_account/institution_register');
// Stub analytics tracking
window.analytics = jasmine.createSpyObj('analytics', ['track', 'page', 'pageview', 'trackLink']);
......@@ -135,6 +138,30 @@ define([
assertForms('#login-form', '#register-form');
});
it('toggles between the login and institution login view', function() {
ajaxSpyAndInitialize(this, 'login');
// Simulate clicking on institution login button
$('#login-form .button-secondary-login[data-type="institution_login"]').click();
assertForms('#institution_login-form', '#login-form');
// Simulate selection of the login form
selectForm('login');
assertForms('#login-form', '#institution_login-form');
});
it('toggles between the register and institution register view', function() {
ajaxSpyAndInitialize(this, 'register');
// Simulate clicking on institution login button
$('#register-form .button-secondary-login[data-type="institution_login"]').click();
assertForms('#institution_login-form', '#register-form');
// Simulate selection of the login form
selectForm('register');
assertForms('#register-form', '#institution_login-form');
});
it('displays the reset password form', function() {
ajaxSpyAndInitialize(this, 'login');
......
define([
'jquery',
'underscore',
'common/js/spec_helpers/template_helpers',
'js/student_account/views/InstitutionLoginView',
], function($, _, TemplateHelpers, InstitutionLoginView) {
'use strict';
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();
};
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'
);
});
});
});
......@@ -18,7 +18,8 @@ var edx = edx || {};
subview: {
login: {},
register: {},
passwordHelp: {}
passwordHelp: {},
institutionLogin: {}
},
nextUrl: '/dashboard',
......@@ -52,7 +53,8 @@ var edx = edx || {};
this.formDescriptions = {
login: obj.loginFormDesc,
register: obj.registrationFormDesc,
reset: obj.passwordResetFormDesc
reset: obj.passwordResetFormDesc,
institution_login: null
};
this.platformName = obj.platformName;
......@@ -148,6 +150,16 @@ var edx = edx || {};
// 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 edx.student.account.InstitutionLoginView({
thirdPartyAuth: this.thirdPartyAuth,
platformName: this.platformName,
mode: this.activeForm
});
this.subview.institutionLogin.render();
}
},
......@@ -180,9 +192,11 @@ var edx = edx || {};
category: 'user-engagement'
});
if ( !this.form.isLoaded( $form ) ) {
// 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') );
......@@ -190,11 +204,13 @@ var edx = edx || {};
this.element.scrollTop( $anchor );
// Update url without reloading page
History.pushState( null, document.title, '/' + type + queryStr );
if (type != "institution_login") {
History.pushState( null, document.title, '/' + type + queryStr );
}
analytics.page( 'login_and_registration', type );
// Focus on the form
document.getElementById(type).focus();
$("#" + type).focus();
},
/**
......
......@@ -215,7 +215,9 @@ var edx = edx || {};
submitForm: function( event ) {
var data = this.getFormData();
event.preventDefault();
if (!_.isUndefined(event)) {
event.preventDefault();
}
this.toggleDisableButton(true);
......
var edx = edx || {};
(function($, _, Backbone) {
'use strict';
edx.student = edx.student || {};
edx.student.account = edx.student.account || {};
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;
},
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;
}
});
})(jQuery, _, Backbone);
......@@ -25,6 +25,9 @@ var edx = edx || {};
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;
......@@ -45,6 +48,7 @@ var edx = edx || {};
currentProvider: this.currentProvider,
errorMessage: this.errorMessage,
providers: this.providers,
hasSecondaryProviders: this.hasSecondaryProviders,
platformName: this.platformName
}
}));
......
......@@ -22,9 +22,13 @@ var edx = edx || {};
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 );
},
......@@ -41,12 +45,19 @@ var edx = edx || {};
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;
},
......@@ -63,6 +74,7 @@ var edx = edx || {};
},
saveError: function( error ) {
$(this.el).show(); // Show in case the form was hidden for auto-submission
this.errors = _.flatten(
_.map(
JSON.parse(error.responseText),
......@@ -76,6 +88,13 @@ var edx = edx || {};
);
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);
......@@ -14,6 +14,7 @@ $sm-btn-linkedin: #0077b5;
background: $white;
min-height: 100%;
width: 100%;
$third-party-button-height: ($baseline*1.75);
h2 {
@extend %t-title5;
......@@ -22,6 +23,10 @@ $sm-btn-linkedin: #0077b5;
font-family: $sans-serif;
}
.instructions {
@extend %t-copy-base;
}
/* Temp. fix until applied globally */
> {
@include box-sizing(border-box);
......@@ -67,10 +72,11 @@ $sm-btn-linkedin: #0077b5;
}
}
form {
form,
.wrapper-other-login {
border: 1px solid $gray-l4;
border-radius: 5px;
padding: 0px 25px 20px 25px;
border-radius: ($baseline/4);
padding: 0 ($baseline*1.25) $baseline ($baseline*1.25);
}
.section-title {
......@@ -106,16 +112,20 @@ $sm-btn-linkedin: #0077b5;
}
}
.nav-btn {
%nav-btn-base {
@extend %btn-secondary-blue-outline;
width: 100%;
height: ($baseline*2);
text-transform: none;
text-shadow: none;
font-weight: 600;
letter-spacing: normal;
}
.nav-btn {
@extend %nav-btn-base;
@extend %t-strong;
}
.form-type,
.toggle-form {
@include box-sizing(border-box);
......@@ -348,29 +358,31 @@ $sm-btn-linkedin: #0077b5;
.login-provider {
@extend %btn-secondary-grey-outline;
width: 130px;
padding: 0 0 0 ($baseline*2);
height: 34px;
text-align: left;
text-shadow: none;
text-transform: none;
@extend %t-action4;
@include padding(0, 0, 0, $baseline*2);
@include text-align(left);
position: relative;
font-size: 0.8em;
margin-right: ($baseline/4);
margin-bottom: $baseline;
border-color: $lightGrey1;
&:nth-of-type(odd) {
margin-right: 13px;
}
width: $baseline*6.5;
height: $third-party-button-height;
text-shadow: none;
text-transform: none;
.icon {
color: white;
@include left(0);
position: absolute;
top: -1px;
left: 0;
width: 30px;
height: 34px;
line-height: 34px;
bottom: -1px;
background: $m-blue-d3;
line-height: $third-party-button-height;
text-align: center;
color: $white;
}
&:hover,
......@@ -378,16 +390,12 @@ $sm-btn-linkedin: #0077b5;
background-image: none;
.icon {
height: 32px;
line-height: 32px;
top: 0;
bottom: 0;
line-height: ($third-party-button-height - 2px);
}
}
&:last-child {
margin-bottom: $baseline;
}
&.button-oa2-google-oauth2 {
color: $sm-btn-google;
......@@ -447,6 +455,19 @@ $sm-btn-linkedin: #0077b5;
}
.button-secondary-login {
@extend %nav-btn-base;
@extend %t-action4;
@extend %t-regular;
border-color: $lightGrey1;
padding: 0;
height: $third-party-button-height;
&:hover {
border-color: $m-blue-d3;
}
}
/** Error Container - from _account.scss **/
.status {
@include box-sizing(border-box);
......@@ -503,6 +524,13 @@ $sm-btn-linkedin: #0077b5;
}
}
.institution-list {
.institution {
@extend %t-copy-base;
}
}
@include media( max-width 330px) {
.form-type {
width: 98%;
......
......@@ -9,3 +9,7 @@
<section id="password-reset-anchor" class="form-type">
<div id="password-reset-form" class="form-wrapper hidden" aria-hidden="true"></div>
</section>
<section id="institution_login-anchor" class="form-type">
<div id="institution_login-form" class="form-wrapper hidden" aria-hidden="true"></div>
</section>
<div class="wrapper-other-login">
<div class="section-title lines">
<h2>
<span class="text">
<%- gettext("Sign in with Institution/Campus Credentials") %>
</span>
</h2>
</div>
<p class="instructions"><%- gettext("Choose your institution from the list below:") %></p>
<ul class="institution-list">
<% _.each( _.sortBy(providers, "name"), function( provider ) {
if ( provider.loginUrl ) { %>
<li class="institution">
<a class="institution-login-link" href="<%- provider.loginUrl %>"><%- provider.name %></a>
</li>
<% }
}); %>
</ul>
<div class="section-title lines">
<h2>
<span class="text"><%- gettext("or") %></span>
</h2>
</div>
<div class="toggle-form">
<button class="nav-btn form-toggle" data-type="login"><%- gettext("Back to sign in") %></button>
</div>
</div>
<div class="wrapper-other-login">
<div class="section-title lines">
<h2>
<span class="text">
<%- gettext("Register with Institution/Campus Credentials") %>
</span>
</h2>
</div>
<p class="instructions"><%- gettext("Choose your institution from the list below:") %></p>
<ul class="institution-list">
<% _.each( _.sortBy(providers, "name"), function( provider ) {
if ( provider.registerUrl ) { %>
<li class="institution">
<a class="institution-login-link" href="<%- provider.registerUrl %>"><%- provider.name %></a>
</li>
<% }
}); %>
</ul>
<div class="section-title lines">
<h2>
<span class="text"><%- gettext("or") %></span>
</h2>
</div>
<div class="toggle-form">
<button class="nav-btn form-toggle" data-type="register"><%- gettext("Register through edX") %></button>
</div>
</div>
......@@ -39,7 +39,7 @@
<button type="submit" class="action action-primary action-update js-login login-button"><%- gettext("Sign in") %></button>
<% if ( context.providers.length > 0 && !context.currentProvider ) { %>
<% if ( context.providers.length > 0 && !context.currentProvider || context.hasSecondaryProviders ) { %>
<div class="login-providers">
<div class="section-title lines">
<h2>
......@@ -55,6 +55,12 @@
</button>
<% }
}); %>
<% if ( context.hasSecondaryProviders ) { %>
<button type="button" class="button-secondary-login form-toggle" data-type="institution_login">
<%- gettext("Use my institution/campus credentials") %>
</button>
<% } %>
</div>
<% } %>
</form>
......
......@@ -15,7 +15,7 @@
</%block>
<%block name="header_extras">
% for template_name in ["account", "access", "form_field", "login", "register", "password_reset"]:
% for template_name in ["account", "access", "form_field", "login", "register", "institution_login", "institution_register", "password_reset"]:
<script type="text/template" id="${template_name}-tpl">
<%static:include path="student_account/${template_name}.underscore" />
</script>
......
......@@ -19,7 +19,7 @@
<%- _.sprintf( gettext("We just need a little more information before you start learning with %(platformName)s."), context ) %>
</p>
</div>
<% } else if ( context.providers.length > 0 ) { %>
<% } else if ( context.providers.length > 0 || context.hasSecondaryProviders ) { %>
<div class="login-providers">
<div class="section-title lines">
<h2>
......@@ -35,6 +35,12 @@
</button>
<% }
}); %>
<% if ( context.hasSecondaryProviders ) { %>
<button type="button" class="button-secondary-login form-toggle" data-type="institution_login">
<%- gettext("Use my institution/campus credentials") %>
</button>
<% } %>
</div>
<div class="section-title lines">
<h2>
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment