diff --git a/lti_consumer/static/js/xblock_lti_consumer.js b/lti_consumer/static/js/xblock_lti_consumer.js index db835be4f4995022507c72b2ae97ce4e1be4b578..1a38d644bb7135a8f78c027d84c2e88a0d01cc62 100644 --- a/lti_consumer/static/js/xblock_lti_consumer.js +++ b/lti_consumer/static/js/xblock_lti_consumer.js @@ -8,51 +8,51 @@ function LtiConsumerXBlock(runtime, element) { iframeModal: function (options) { var $trigger = $(this); var modal_id = $trigger.data("target"); - var defaults = {top: 100, overlay: 0.5, closeButton: null}; + var defaults = { top: 100, overlay: 0.5, closeButton: null }; var overlay_id = (modal_id + '_lean-overlay').replace('#', ''); var overlay = $("<div id='" + overlay_id + "' class='lean-overlay'></div>"); $("body").append(overlay); options = $.extend(defaults, options); return this.each(function () { var o = options; - $(this).click(function (e) { - var $modal = $(modal_id); - // If we are already in an iframe, skip creation of the modal, since - // it won't look good, anyway. Instead, we post a message to the parent - // window, requesting creation of a modal there. - // This is used by the courseware microfrontend. - if (window !== window.parent) { - window.parent.postMessage( - { - 'type': 'plugin.modal', - 'payload': { - 'url': window.location.origin + $modal.data('launch-url'), - 'title': $modal.find('iframe').attr('title'), - 'width': $modal.data('width') - } - }, - document.referrer - ); - return; - } - // Set iframe src attribute to launch LTI provider - $modal.find('iframe').attr('src', $modal.data('launch-url')); - $("#" + overlay_id).click(function () { - close_modal(modal_id) - }); - $(o.closeButton).click(function () { - close_modal(modal_id) - }); - var modal_height = $(modal_id).outerHeight(); - var modal_width = $(modal_id).outerWidth(); - $("#" + overlay_id).css({"display": "block", opacity: 0}); - $("#" + overlay_id).fadeTo(200, o.overlay); - $(modal_id).css({ - "display": "block" - }); - $(modal_id).fadeTo(200, 1); - $(modal_id).attr('aria-hidden', false); - $('body').css('overflow', 'hidden'); + + var $modal = $(modal_id); + // If we are already in an iframe, skip creation of the modal, since + // it won't look good, anyway. Instead, we post a message to the parent + // window, requesting creation of a modal there. + // This is used by the courseware microfrontend. + if (window !== window.parent) { + window.parent.postMessage( + { + 'type': 'plugin.modal', + 'payload': { + 'url': window.location.origin + $modal.data('launch-url'), + 'title': $modal.find('iframe').attr('title'), + 'width': $modal.data('width') + } + }, + document.referrer + ); + return; + } + // Set iframe src attribute to launch LTI provider + $modal.find('iframe').attr('src', $modal.data('launch-url')); + $("#" + overlay_id).click(function () { + close_modal(modal_id) + }); + $(o.closeButton).click(function () { + close_modal(modal_id) + }); + var modal_height = $(modal_id).outerHeight(); + var modal_width = $(modal_id).outerWidth(); + $("#" + overlay_id).css({ "display": "block", opacity: 0 }); + $("#" + overlay_id).fadeTo(200, o.overlay); + $(modal_id).css({ + "display": "block" + }); + $(modal_id).fadeTo(200, 1); + $(modal_id).attr('aria-hidden', false); + $('body').css('overflow', 'hidden'); e.preventDefault(); @@ -71,19 +71,19 @@ function LtiConsumerXBlock(runtime, element) { } }); - /* Redirect non-iframe tab to close button */ - var $inputs = $('select, input, textarea, button, a').filter(':visible').not(o.closeButton); - $inputs.on('focus', function(e) { - e.preventDefault(); - $(options.closeButton).focus(); - }); + /* Redirect non-iframe tab to close button */ + var $inputs = $('select, input, textarea, button, a').filter(':visible').not(o.closeButton); + $inputs.on('focus', function (e) { + e.preventDefault(); + $(options.closeButton).focus(); }); + }); function close_modal(modal_id) { var $modal = $(modal_id); $('select, input, textarea, button, a').off('focus'); $("#" + overlay_id).fadeOut(200); - $modal.css({"display": "none"}); + $modal.css({ "display": "none" }); $modal.attr('aria-hidden', true); $modal.find('iframe').attr('src', ''); $('body').css('overflow', 'auto'); @@ -92,68 +92,98 @@ function LtiConsumerXBlock(runtime, element) { } }); + function confirmDialog(message, triggerElement, showCancelButton) { + var def = $.Deferred(); + // Hide the button that triggered the event, i.e. the launch button. + triggerElement.hide(); + + $('<div id="dialog-container"></div>').insertAfter(triggerElement) // TODO: this will need some cute styling. It looks like trash but it works. + .append('<p>' + message + '</p>') + if (showCancelButton) { + $('#dialog-container') + .append('<button style="margin-right:1rem" id="cancel-button">Cancel</button>'); + } + $('#dialog-container').append('<button id="confirm-button">OK</button>'); + + // When a learner clicks "OK" or "Cancel" in the consent dialog, remove the consent dialog, show the launch + // button, and resolve the promise. + $('#confirm-button').click(function () { + // Show the button that triggered the event, i.e. the launch button. + triggerElement.show(); + $("#dialog-container").remove() + $('body').append('<h1>Confirm Dialog Result: <i>Yes</i></h1>'); + def.resolve("OK"); + }) + $('#cancel-button').click(function () { + // Hide the button that triggered the event, i.e. the launch button. + triggerElement.show() + $("#dialog-container").remove() + $('body').append('<h1>Confirm Dialog Result: <i>No</i></h1>'); + def.resolve("Cancel"); + }) + return def.promise(); + }; + var $element = $(element); var $ltiContainer = $element.find('.lti-consumer-container'); var askToSendUsername = $ltiContainer.data('ask-to-send-username') == 'True'; var askToSendEmail = $ltiContainer.data('ask-to-send-email') == 'True'; - // Apply click handler to modal launch button - $element.find('.btn-lti-modal').iframeModal({top: 200, closeButton: '.close-modal'}); - - // Apply click handler to new window launch button - $element.find('.btn-lti-new-window').click(function(){ - - // If this instance is configured to require username and/or email, ask user if it is okay to send them - // Do not launch if it is not okay - var destination = $(this).data('target') - - function confirmDialog(message) { - var def = $.Deferred(); - $('<div></div>').appendTo('body') // TODO: this will need some cute styling. It looks like trash but it works. - .html('<div><p>' + message + '</p></div>') - .dialog({ - modal: true, - title: 'Confirm', - zIndex: 10000, - autoOpen: true, - width: 'auto', - resizable: false, - dialogClass: 'confirm-dialog', - buttons: { - OK: function() { - $('body').append('<h1>Confirm Dialog Result: <i>Yes</i></h1>'); - def.resolve("OK"); - $(this).dialog("close"); - }, - Cancel: function() { - $('body').append('<h1>Confirm Dialog Result: <i>No</i></h1>'); - def.resolve("Cancel"); - $(this).dialog("close"); - } - }, - close: function(event, ui) { - $(this).remove(); - } - }).prev().css('background', 'white').css('color', '#000').css('border-color', 'transparent'); - return def.promise(); - }; - - if(askToSendUsername && askToSendEmail) { + function renderPIIConsentPromptIfRequired(onSuccess, showCancelButton=true) { + if (askToSendUsername && askToSendEmail) { msg = gettext("Click OK to have your username and e-mail address sent to a 3rd party application.\n\nClick Cancel to return to this page without sending your information."); } else if (askToSendUsername) { msg = gettext("Click OK to have your username sent to a 3rd party application.\n\nClick Cancel to return to this page without sending your information."); } else if (askToSendEmail) { msg = gettext("Click OK to have your e-mail address sent to a 3rd party application.\n\nClick Cancel to return to this page without sending your information."); } else { - window.open(destination); + onSuccess("OK"); + return; } - $.when(confirmDialog(msg)).then( - function(status) { + $.when(confirmDialog(msg, $(this), showCancelButton)).then(onSuccess); + } + + // Render consent dialog for inline elements immediately. + var $ltiIframeContainerElement = $element.find('#lti-iframe-container'); + $ltiIframeContainerElement.each(function () { + var ltiIframeTarget = $ltiIframeContainerElement.data('target') + renderPIIConsentPromptIfRequired.apply(this, [ + function (status) { + if (status === 'OK') { + // After getting consent to share PII, set the src attribute of the iframe to start the launch. + $ltiIframeContainerElement.find('iframe').attr('src', ltiIframeTarget); + } + }, + false + ]); + }) + + // Apply click handler to modal launch button. + var $ltiModalButton = $element.find('.btn-lti-modal'); + $ltiModalButton.click(function () { + renderPIIConsentPromptIfRequired.apply(this, [ + function (status) { + if (status === 'OK') { + $ltiModalButton.iframeModal({ + top: 200, closeButton: '.close-modal' + }) + } + } + ]); + }); + + // Apply click handler to new window launch button. + var $ltiNewWindowButton = $element.find('.btn-lti-new-window'); + $ltiNewWindowButton.click(function () { + renderPIIConsentPromptIfRequired.apply(this, [ + function (status) { if (status == "OK") { - window.open(destination); + window.open( + $ltiNewWindowButton.data('target') + ); } } - ); + ]); }); }); } diff --git a/lti_consumer/templates/html/student.html b/lti_consumer/templates/html/student.html index bd14c773c8c6e0d1441a2c51cf452d7a084a8187..05afb6a6f6b1c7ab0988ef2176c6fc49f2d3c9fb 100644 --- a/lti_consumer/templates/html/student.html +++ b/lti_consumer/templates/html/student.html @@ -68,9 +68,11 @@ </section> % endif % if launch_target == 'iframe': - <div style="height:${inline_height}px;"> + <div id="lti-iframe-container" data-target="${form_url}" style="height:${inline_height}px;"> ## The result of the LTI launch form submit will be rendered here. - <%include file="templates/html/lti_iframe.html" args="initial_launch_url=form_url"/> + ## Don't pass in the initial_launch_url. Let the Javascript set the src, so we can get PII sharing consent + ## before the launch occurs, if needed. + <%include file="templates/html/lti_iframe.html" args="initial_launch_url=''"/> </div> % endif % elif not hide_launch: