Added Terms of Service modal
This commit is contained in:
@@ -46,7 +46,11 @@ def render(request, template):
|
||||
content_type, __ = mimetypes.guess_type(template)
|
||||
|
||||
try:
|
||||
return render_to_response('static_templates/' + template, {}, content_type=content_type)
|
||||
context = {}
|
||||
# This is necessary for the dialog presented with the TOS in /register
|
||||
if template == 'honor.html':
|
||||
context['allow_iframing'] = True
|
||||
return render_to_response('static_templates/' + template, context, content_type=content_type)
|
||||
except TopLevelLookupException:
|
||||
raise Http404
|
||||
|
||||
|
||||
@@ -6,7 +6,8 @@
|
||||
'common/js/spec_helpers/template_helpers',
|
||||
'edx-ui-toolkit/js/utils/spec-helpers/ajax-helpers',
|
||||
'js/student_account/models/RegisterModel',
|
||||
'js/student_account/views/RegisterView'
|
||||
'js/student_account/views/RegisterView',
|
||||
'js/student_account/tos_modal'
|
||||
],
|
||||
function($, _, TemplateHelpers, AjaxHelpers, RegisterModel, RegisterView) {
|
||||
describe('edx.student.account.RegisterView', function() {
|
||||
@@ -178,7 +179,9 @@
|
||||
type: 'checkbox',
|
||||
required: true,
|
||||
instructions: '',
|
||||
restrictions: {}
|
||||
restrictions: {},
|
||||
supplementalLink: '/honor',
|
||||
supplementalText: 'Review the Terms of Service and Honor Code'
|
||||
}
|
||||
]
|
||||
};
|
||||
@@ -366,6 +369,50 @@
|
||||
// Form button should be disabled on success.
|
||||
expect(view.$submitButton).toHaveAttr('disabled');
|
||||
});
|
||||
|
||||
it('displays a modal with the terms of service', function() {
|
||||
var $modal,
|
||||
$content;
|
||||
|
||||
createRegisterView(this);
|
||||
|
||||
// Check there is no modal container initially
|
||||
expect($('.tos-modal').length).toEqual(0);
|
||||
|
||||
// And no modal is being displayed
|
||||
expect($('body').hasClass('open-modal')).toBe(false);
|
||||
|
||||
// Click TOS button
|
||||
$('.checkbox-honor_code .supplemental-link a').click();
|
||||
|
||||
// TOS modal container has been added and is visible
|
||||
$modal = $('.tos-modal');
|
||||
expect($modal.length).toEqual(1);
|
||||
expect($modal).toBeVisible();
|
||||
expect($('body').hasClass('open-modal')).toBe(true);
|
||||
|
||||
// The modal has a content area, a Close button and a title matching the TOS link
|
||||
$content = $modal.find('.modal-content');
|
||||
expect($content.length).toEqual(1);
|
||||
expect($content.find('.modal-close-button').text()).toEqual('Close');
|
||||
expect($content.find('#modal-header-text').text()).toEqual(
|
||||
'Terms of Service and Honor Code'
|
||||
);
|
||||
|
||||
// The content area has an iframe displaying the TOS link
|
||||
expect($content.find('iframe').length).toEqual(1);
|
||||
expect($content.find('iframe').attr('src').endsWith('/honor')).toBe(true);
|
||||
|
||||
// Click the close button
|
||||
$('.modal-close-button').click();
|
||||
|
||||
// The modal is now hidden
|
||||
expect($modal).toBeHidden();
|
||||
expect($('body').hasClass('open-modal')).toBe(false);
|
||||
|
||||
// The iframe has been deleted
|
||||
expect($content.find('iframe').length).toEqual(0);
|
||||
});
|
||||
});
|
||||
});
|
||||
}).call(this, define || RequireJS.define);
|
||||
|
||||
178
lms/static/js/student_account/tos_modal.js
Normal file
178
lms/static/js/student_account/tos_modal.js
Normal file
@@ -0,0 +1,178 @@
|
||||
/**
|
||||
* Modal for displaying the Terms of Service in the Register page.
|
||||
*/
|
||||
|
||||
(function($, gettext) {
|
||||
'use strict';
|
||||
|
||||
var focusableElementsSelector = [
|
||||
'a[href], area[href], input:not([disabled]), select:not([disabled]),',
|
||||
'textarea:not([disabled]), button:not([disabled]), iframe, object, embed,',
|
||||
'*[tabindex], *[contenteditable]'
|
||||
].join(' ');
|
||||
|
||||
var disableTabIndexingOn = function(containerSelector) {
|
||||
var $container = $(containerSelector),
|
||||
$focusableItems = $container.find('*').filter(focusableElementsSelector).filter(':visible');
|
||||
$container.attr('aria-hidden', 'true');
|
||||
$focusableItems.attr('tabindex', '-1');
|
||||
};
|
||||
|
||||
var enableTabIndexingOn = function(containerSelector) {
|
||||
var $container = $(containerSelector),
|
||||
$focusableItems = $container.find('*').filter(focusableElementsSelector).filter(':visible');
|
||||
$container.attr('aria-hidden', 'false');
|
||||
$focusableItems.attr('tabindex', '0');
|
||||
};
|
||||
|
||||
var showModal = function(modalSelector) {
|
||||
$(modalSelector).attr('aria-hidden', 'false');
|
||||
$(modalSelector).show();
|
||||
disableTabIndexingOn('.window-wrap');
|
||||
// Prevent scrolling of the background
|
||||
$('body').addClass('open-modal');
|
||||
};
|
||||
|
||||
var hideModal = function(modalSelector, tosLinkSelector) {
|
||||
$(modalSelector).attr('aria-hidden', 'true');
|
||||
$(modalSelector).hide();
|
||||
enableTabIndexingOn('.window-wrap');
|
||||
$('body').removeClass('open-modal');
|
||||
$(modalSelector).find('iframe').remove();
|
||||
$(tosLinkSelector).focus();
|
||||
};
|
||||
|
||||
var buildModal = function(modalClass, contentClass, closeButtonClass) {
|
||||
// Create the modal container
|
||||
var modalTitle = gettext('Terms of Service and Honor Code'),
|
||||
closeLabel = gettext('Close'),
|
||||
titleId = 'modal-header-text',
|
||||
$modal = $('<div>', {
|
||||
class: modalClass,
|
||||
'aria-hidden': 'true'
|
||||
}),
|
||||
$content = $('<div>', {
|
||||
class: contentClass,
|
||||
role: 'dialog',
|
||||
'aria-modal': 'true',
|
||||
'aria-labelledby': titleId
|
||||
}),
|
||||
$header = $('<div>', {
|
||||
class: 'header'
|
||||
}),
|
||||
$closeButton = $('<button>', {
|
||||
'aria-label': closeLabel,
|
||||
class: closeButtonClass
|
||||
}),
|
||||
$title = $('<h1>', {
|
||||
id: titleId
|
||||
});
|
||||
$closeButton.text(closeLabel);
|
||||
$title.text(modalTitle);
|
||||
$header.append($title);
|
||||
$header.append($closeButton);
|
||||
$content.append($header);
|
||||
$modal.append($content);
|
||||
return $modal;
|
||||
};
|
||||
|
||||
var buildIframe = function(link, modalSelector, contentSelector, tosLinkSelector) {
|
||||
// Create an iframe with contents from the link and set its height to match the content area
|
||||
return $('<iframe>', {
|
||||
src: link.href,
|
||||
load: function() {
|
||||
var $iframeHead = $(this).contents().find('head'),
|
||||
$iframeBody = $(this).contents().find('body');
|
||||
// Overwrite styles in child page to hide top navigation and footer
|
||||
var $style = $('<style>', {type: 'text/css'}),
|
||||
styleContent = [
|
||||
'/* Default honor.html template */',
|
||||
'.nav-skip, header#global-navigation, .wrapper-footer {',
|
||||
' display: none;',
|
||||
'}',
|
||||
'.container.about {',
|
||||
' min-width: auto;',
|
||||
'}',
|
||||
'/* https://www.edx.org/edx-terms-service */',
|
||||
'.edx-header, #skip-link, footer {',
|
||||
' display: none;',
|
||||
'}',
|
||||
'.region-banner, #breadcrumb + .region-column-wrapper, .edx-header + .region-column-wrapper {',
|
||||
' margin-top: 0;',
|
||||
' padding-top: 10px;',
|
||||
'}',
|
||||
'body.node-type-page h1.field-page-tagline {',
|
||||
' font-size: 16px;',
|
||||
'}',
|
||||
'/* edx-themes */',
|
||||
'.page-heading, .footer-main {',
|
||||
' display: none;',
|
||||
'}'
|
||||
].join('\n');
|
||||
$style.text(styleContent);
|
||||
$iframeHead.append($style);
|
||||
// Set the iframe's height to fill the available space
|
||||
$(this).css({
|
||||
height: $(contentSelector).height()
|
||||
});
|
||||
// Hide the modal when ESC is pressed and the iframe is focused
|
||||
$iframeBody.keydown(function(event) {
|
||||
if ($(modalSelector).is(':visible') && event.keyCode === 27) {
|
||||
event.preventDefault();
|
||||
hideModal(modalSelector, tosLinkSelector);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
var tosLinkSelector = '.checkbox-honor_code .supplemental-link a',
|
||||
closeButtonClass = 'modal-close-button',
|
||||
closeButtonSelector = '.' + closeButtonClass,
|
||||
contentClass = 'modal-content',
|
||||
contentSelector = '.' + contentClass,
|
||||
modalClass = 'tos-modal',
|
||||
modalSelector = '.' + modalClass;
|
||||
|
||||
$('body').on('click', tosLinkSelector, function(event) {
|
||||
var link = event.target,
|
||||
$modal,
|
||||
$iframe;
|
||||
event.preventDefault();
|
||||
// Ignore disabled TOS
|
||||
if (link.href.endsWith('#')) {
|
||||
return;
|
||||
}
|
||||
// Add the modal if it doesn't exist yet
|
||||
if ($(modalSelector).length < 1) {
|
||||
$modal = buildModal(modalClass, contentClass, closeButtonClass);
|
||||
$('body').append($modal);
|
||||
}
|
||||
// Add a new iframe to the content area
|
||||
$iframe = buildIframe(link, modalSelector, contentSelector, tosLinkSelector);
|
||||
$(contentSelector).append($iframe);
|
||||
showModal(modalSelector);
|
||||
$(closeButtonSelector).focus();
|
||||
});
|
||||
|
||||
$('body').on('click', closeButtonSelector, function() {
|
||||
hideModal(modalSelector, tosLinkSelector);
|
||||
});
|
||||
|
||||
// Hide the modal when clicking outside its content
|
||||
$('body').on('click', modalSelector, function(event) {
|
||||
if ($(event.target).hasClass(modalClass)) {
|
||||
hideModal(modalSelector, tosLinkSelector);
|
||||
}
|
||||
});
|
||||
|
||||
// Hide the modal when ESC is pressed and the modal is focused
|
||||
$(document).keydown(function(event) {
|
||||
if ($(modalSelector).is(':visible') && event.keyCode === 27) {
|
||||
event.preventDefault();
|
||||
hideModal(modalSelector, tosLinkSelector);
|
||||
}
|
||||
});
|
||||
});
|
||||
}(jQuery, gettext));
|
||||
@@ -683,3 +683,91 @@
|
||||
color: $gray-d2;
|
||||
}
|
||||
}
|
||||
|
||||
.tos-modal {
|
||||
background-color: $black-t1;
|
||||
display: none;
|
||||
height: 120%;
|
||||
left: -10%;
|
||||
overflow: auto;
|
||||
position: fixed;
|
||||
top: -10%;
|
||||
width: 120%;
|
||||
z-index: 1;
|
||||
|
||||
.modal-content {
|
||||
background-color: $white;
|
||||
bottom: auto;
|
||||
left: 50%;
|
||||
margin: auto;
|
||||
max-width: 650px;
|
||||
min-height: 540px;
|
||||
min-width: 300px;
|
||||
position: fixed;
|
||||
right: auto;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 100%;
|
||||
|
||||
.header {
|
||||
height: 50px;
|
||||
position: relative;
|
||||
|
||||
h1#modal-header-text {
|
||||
float: left;
|
||||
font-family: $sans-serif;
|
||||
font-size: font-size(large);
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
padding-left: 20px;
|
||||
position: absolute;
|
||||
text-align: left;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
.modal-close-button {
|
||||
background: $white;
|
||||
border: none;
|
||||
border-radius: 0;
|
||||
box-shadow: none;
|
||||
box-sizing: border-box;
|
||||
color: $uxpl-blue-base;
|
||||
float: right;
|
||||
font-size: font-size(large);
|
||||
height: 48px;
|
||||
letter-spacing: normal;
|
||||
padding: 0;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
text-shadow: none;
|
||||
text-transform: lowercase;
|
||||
top: 50%;
|
||||
transform: translate(0, -50%);
|
||||
width: 80px;
|
||||
}
|
||||
|
||||
.modal-close-button:hover {
|
||||
background: $white;
|
||||
color: $uxpl-blue-hover-active;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.modal-close-button:focus {
|
||||
color: $uxpl-blue-hover-active;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
iframe {
|
||||
border: 0;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
body.open-modal {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
<%page expression_filter="h"/>
|
||||
<%!
|
||||
import json
|
||||
from django.utils.translation import ugettext as _
|
||||
from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers
|
||||
from openedx.core.djangolib.js_utils import dump_js_escaped_json
|
||||
%>
|
||||
<%namespace name='static' file='/static_content.html'/>
|
||||
@@ -22,6 +24,9 @@
|
||||
newrelic.addPageAction('xfinished');
|
||||
}
|
||||
</%static:require_module>
|
||||
% if configuration_helpers.get_value('DISPLAY_TOS_IN_MODAL_ON_REGISTRATION_PAGE', False):
|
||||
<script type="text/javascript" src="${static.url('js/student_account/tos_modal.js')}"></script>
|
||||
% endif
|
||||
</%block>
|
||||
|
||||
<%block name="header_extras">
|
||||
|
||||
Reference in New Issue
Block a user