From 10e16c69c8f52d735ab8ff6b46bcdd2dac4f96f0 Mon Sep 17 00:00:00 2001 From: Claude Vervoort Date: Thu, 6 Feb 2020 10:48:11 -0500 Subject: [PATCH] MDL-67301 mod_lti: dynamic registration --- mod/lti/amd/build/tool_card_controller.min.js | 2 +- .../amd/build/tool_card_controller.min.js.map | 2 +- .../build/tool_configure_controller.min.js | 2 +- .../tool_configure_controller.min.js.map | 2 +- mod/lti/amd/src/tool_card_controller.js | 5 +- mod/lti/amd/src/tool_configure_controller.js | 83 ++++- mod/lti/certs.php | 19 +- .../classes/local/ltiopenid/jwks_helper.php | 72 ++++ .../ltiopenid/registration_exception.php | 32 ++ .../local/ltiopenid/registration_helper.php | 345 ++++++++++++++++++ .../classes/local/ltiservice/service_base.php | 13 +- mod/lti/lang/en/lti.php | 2 + mod/lti/locallib.php | 15 +- mod/lti/openid-configuration.php | 61 ++++ mod/lti/openid-registration.php | 62 ++++ .../classes/local/service/basicoutcomes.php | 9 + .../local/service/gradebookservices.php | 10 + .../classes/local/service/memberships.php | 9 + .../classes/local/service/toolsettings.php | 9 + mod/lti/startltiadvregistration.php | 51 +++ .../cartridge_registration_form.mustache | 8 +- .../templates/external_registration.mustache | 8 +- mod/lti/templates/loader.mustache | 4 +- mod/lti/templates/tool_configure.mustache | 19 +- mod/lti/tests/behat/toolconfigure.feature | 6 +- mod/lti/tests/openidregistration_test.php | 269 ++++++++++++++ 26 files changed, 1056 insertions(+), 63 deletions(-) create mode 100644 mod/lti/classes/local/ltiopenid/jwks_helper.php create mode 100644 mod/lti/classes/local/ltiopenid/registration_exception.php create mode 100644 mod/lti/classes/local/ltiopenid/registration_helper.php create mode 100644 mod/lti/openid-configuration.php create mode 100644 mod/lti/openid-registration.php create mode 100644 mod/lti/startltiadvregistration.php create mode 100644 mod/lti/tests/openidregistration_test.php diff --git a/mod/lti/amd/build/tool_card_controller.min.js b/mod/lti/amd/build/tool_card_controller.min.js index 75f566f8e27..075fa9e3863 100644 --- a/mod/lti/amd/build/tool_card_controller.min.js +++ b/mod/lti/amd/build/tool_card_controller.min.js @@ -1,2 +1,2 @@ -define ("mod_lti/tool_card_controller",["jquery","core/ajax","core/notification","core/templates","core/modal_factory","mod_lti/tool_type","mod_lti/events","mod_lti/keys","core/str"],function(a,b,c,d,e,f,g,h,i){var j={DELETE_BUTTON:".delete",NAME_ELEMENT:".name",DESCRIPTION_ELEMENT:".description",CAPABILITIES_CONTAINER:".capabilities-container",ACTIVATE_BUTTON:".tool-card-footer a.activate"},k=2e3,l=function(a){return a.find(j.DELETE_BUTTON)},m=function(a){return a.find(j.NAME_ELEMENT)},n=function(a){return a.find(j.DESCRIPTION_ELEMENT)},o=function(a){return a.find(j.ACTIVATE_BUTTON)},p=function(a){return o(a).length?!0:!1},q=function(a){return a.find(j.CAPABILITIES_CONTAINER)},r=function(a){return q(a).length?!0:!1},s=function(a){return a.attr("data-type-id")},t=function(a){a.removeClass("announcement loading success fail capabilities")},u=function(a){t(a);a.addClass("announcement loading")},v=function(a){a.removeClass("announcement loading")},w=function(b){var c=a.Deferred();t(b);b.addClass("announcement success");setTimeout(function(){b.removeClass("announcement success");c.resolve()},k);return c},x=function(b){var c=a.Deferred();t(b);b.addClass("announcement fail");setTimeout(function(){b.removeClass("announcement fail");c.resolve()},k);return c},y=function(b){var d=a.Deferred(),e=s(b);u(b);if(""===e){return a.Deferred().resolve()}i.get_strings([{key:"delete",component:"mod_lti"},{key:"delete_confirmation",component:"mod_lti"},{key:"delete",component:"mod_lti"},{key:"cancel",component:"core"}]).done(function(a){c.confirm(a[0],a[1],a[2],a[3],function(){f.delete(e).done(function(){v(b);w(b).done(function(){b.remove()}).fail(c.exception).always(function(){d.resolve()})}).fail(function(a){x(b);d.reject(a)})},function(){v(b);d.resolve()})}).fail(function(a){v(b);c.exception(a);d.reject(a)});return d},z=function(a,b){a.attr("data-val-snapshot",b)},A=function(a){return a.attr("data-val-snapshot")},B=function(a){var b=n(a);if(b.hasClass("loading")){return}var c=b.text().trim();z(b,c)},C=function(b){var d=s(b);if(""===d){return a.Deferred().resolve()}var e=n(b);if(e.hasClass("loading")){return a.Deferred().resolve()}var g=e.text().trim(),h=A(e);if(h==g){return a.Deferred().resolve()}e.addClass("loading");var i=f.update({id:d,description:g});i.done(function(a){e.removeClass("loading");e.text(a.description)}).fail(c.exception);i.fail(function(){e.removeClass("loading")});return i},D=function(a){var b=m(a);if(b.hasClass("loading")){return}var c=b.text().trim();z(b,c)},E=function(b){var c=s(b);if(""===c){return a.Deferred().resolve()}var d=m(b);if(d.hasClass("loading")){return a.Deferred().resolve()}var e=d.text().trim(),g=A(d);if(g==e){return a.Deferred().resolve()}d.addClass("loading");var h=f.update({id:c,name:e});h.done(function(a){d.removeClass("loading");d.text(a.name)});h.fail(function(){d.removeClass("loading")});return h},F=function(b){var c=s(b);if(""===c){return a.Deferred().resolve()}u(b);var e=f.update({id:c,state:f.constants.state.configured});e.then(function(a){v(b);w(b);return a}).then(function(a){return d.render("mod_lti/tool_card",a)}).then(function(a){var c=a[0],e=a[1];d.replaceNode(b,c,e)}).catch(function(){v(b);x(b)});return e},G=function(a){a.addClass("announcement capabilities")},H=function(a){a.removeClass("announcement capabilities")},I=function(a){if(r(a)){G(a)}else{F(a)}},J=function(a){var b=l(a);b.click(function(b){b.preventDefault();y(a)});b.keypress(function(a){if(!a.metaKey&&!a.shiftKey&&!a.altKey&&!a.ctrlKey){if(a.keyCode==h.ENTER||a.keyCode==h.SPACE){a.preventDefault();b.click()}}});var c=n(a);c.focus(function(b){b.preventDefault();B(a)});c.blur(function(b){b.preventDefault();C(a)});c.keypress(function(a){if(!a.metaKey&&!a.shiftKey&&!a.altKey&&!a.ctrlKey){if(a.keyCode==h.ENTER){a.preventDefault();c.blur()}}});var d=m(a);d.focus(function(b){b.preventDefault();D(a)});d.blur(function(b){b.preventDefault();E(a)});d.keypress(function(a){if(!a.metaKey&&!a.shiftKey&&!a.altKey&&!a.ctrlKey){if(a.keyCode==h.ENTER){a.preventDefault();d.blur()}}});if(p(a)){var f=o(a);f.click(function(b){b.preventDefault();I(a)});f.keypress(function(a){if(!a.metaKey&&!a.shiftKey&&!a.altKey&&!a.ctrlKey){if(a.keyCode==h.ENTER||a.keyCode==h.SPACE){a.preventDefault();f.click()}}})}if(r(a)){var i=q(a);i.on(g.CAPABILITIES_AGREE,function(){F(a)});i.on(g.CAPABILITIES_DECLINE,function(){H(a)})}},K=function(b){var c=a("#"+b.data("uniqid")+"-"+b.data("deploymentid")),f={uniqid:b.data("uniqid"),platformid:b.data("platformid"),clientid:b.data("clientid"),deploymentid:b.data("deploymentid"),urls:{publickeyset:b.data("publickeyseturl"),accesstoken:b.data("accesstokenurl"),authrequest:b.data("authrequesturl")}},g=d.render("mod_lti/tool_config_modal_body",f),h="mailto:?subject="+encodeURIComponent(b.data("mailtosubject"))+"&body="+encodeURIComponent(b.data("platformidstr"))+":%20"+encodeURIComponent(b.data("platformid"))+"%0D%0A"+encodeURIComponent(b.data("clientidstr"))+":%20"+encodeURIComponent(b.data("clientid"))+"%0D%0A"+encodeURIComponent(b.data("deploymentidstr"))+":%20"+encodeURIComponent(b.data("deploymentid"))+"%0D%0A"+encodeURIComponent(b.data("publickeyseturlstr"))+":%20"+encodeURIComponent(b.data("publickeyseturl"))+"%0D%0A"+encodeURIComponent(b.data("accesstokenurlstr"))+":%20"+encodeURIComponent(b.data("accesstokenurl"))+"%0D%0A"+encodeURIComponent(b.data("authrequesturlstr"))+":%20"+encodeURIComponent(b.data("authrequesturl"))+"%0D%0A";f={mailto:h};var i=d.render("mod_lti/tool_config_modal_footer",f);e.create({large:!0,title:b.data("modaltitle"),body:g,footer:i},c)};return{init:function init(a){J(a);K(a)}}}); +define ("mod_lti/tool_card_controller",["jquery","core/ajax","core/notification","core/templates","core/modal_factory","mod_lti/tool_type","mod_lti/events","mod_lti/keys","core/str"],function(a,b,c,d,e,f,g,h,i){var j={DELETE_BUTTON:".delete",NAME_ELEMENT:".name",DESCRIPTION_ELEMENT:".description",CAPABILITIES_CONTAINER:".capabilities-container",ACTIVATE_BUTTON:".tool-card-footer a.activate"},k=2e3,l=function(a){return a.find(j.DELETE_BUTTON)},m=function(a){return a.find(j.NAME_ELEMENT)},n=function(a){return a.find(j.DESCRIPTION_ELEMENT)},o=function(a){return a.find(j.ACTIVATE_BUTTON)},p=function(a){return o(a).length?!0:!1},q=function(a){return a.find(j.CAPABILITIES_CONTAINER)},r=function(a){return q(a).length?!0:!1},s=function(a){return a.attr("data-type-id")},t=function(a){a.removeClass("announcement loading success fail capabilities")},u=function(a){t(a);a.addClass("announcement loading")},v=function(a){a.removeClass("announcement loading")},w=function(b){var c=a.Deferred();t(b);b.addClass("announcement success");setTimeout(function(){b.removeClass("announcement success");c.resolve()},k);return c},x=function(b){var c=a.Deferred();t(b);b.addClass("announcement fail");setTimeout(function(){b.removeClass("announcement fail");c.resolve()},k);return c},y=function(b){var d=a.Deferred(),e=s(b);u(b);if(""===e){return a.Deferred().resolve()}i.get_strings([{key:"delete",component:"mod_lti"},{key:"delete_confirmation",component:"mod_lti"},{key:"delete",component:"mod_lti"},{key:"cancel",component:"core"}]).done(function(a){c.confirm(a[0],a[1],a[2],a[3],function(){f.delete(e).done(function(){v(b);w(b).done(function(){b.remove()}).fail(c.exception).always(function(){d.resolve()})}).fail(function(a){x(b);d.reject(a)})},function(){v(b);d.resolve()})}).fail(function(a){v(b);c.exception(a);d.reject(a)});return d},z=function(a,b){a.attr("data-val-snapshot",b)},A=function(a){return a.attr("data-val-snapshot")},B=function(a){var b=n(a);if(b.hasClass("loading")){return}var c=b.text().trim();z(b,c)},C=function(b){var d=s(b);if(""===d){return a.Deferred().resolve()}var e=n(b);if(e.hasClass("loading")){return a.Deferred().resolve()}var g=e.text().trim(),h=A(e);if(h==g){return a.Deferred().resolve()}e.addClass("loading");var i=f.update({id:d,description:g});i.done(function(a){e.removeClass("loading");e.text(a.description)}).fail(c.exception);i.fail(function(){e.removeClass("loading")});return i},D=function(a){var b=m(a);if(b.hasClass("loading")){return}var c=b.text().trim();z(b,c)},E=function(b){var c=s(b);if(""===c){return a.Deferred().resolve()}var d=m(b);if(d.hasClass("loading")){return a.Deferred().resolve()}var e=d.text().trim(),g=A(d);if(g==e){return a.Deferred().resolve()}d.addClass("loading");var h=f.update({id:c,name:e});h.done(function(a){d.removeClass("loading");d.text(a.name)});h.fail(function(){d.removeClass("loading")});return h},F=function(b){var c=s(b);if(""===c){return a.Deferred().resolve()}u(b);var e=f.update({id:c,state:f.constants.state.configured});e.then(function(a){v(b);w(b);return a}).then(function(a){return d.render("mod_lti/tool_card",a)}).then(function(a,c){d.replaceNode(b,a,c)}).catch(function(){v(b);x(b)});return e},G=function(a){a.addClass("announcement capabilities")},H=function(a){a.removeClass("announcement capabilities")},I=function(a){if(r(a)){G(a)}else{F(a)}},J=function(a){var b=l(a);b.click(function(b){b.preventDefault();y(a)});b.keypress(function(a){if(!a.metaKey&&!a.shiftKey&&!a.altKey&&!a.ctrlKey){if(a.keyCode==h.ENTER||a.keyCode==h.SPACE){a.preventDefault();b.click()}}});var c=n(a);c.focus(function(b){b.preventDefault();B(a)});c.blur(function(b){b.preventDefault();C(a)});c.keypress(function(a){if(!a.metaKey&&!a.shiftKey&&!a.altKey&&!a.ctrlKey){if(a.keyCode==h.ENTER){a.preventDefault();c.blur()}}});var d=m(a);d.focus(function(b){b.preventDefault();D(a)});d.blur(function(b){b.preventDefault();E(a)});d.keypress(function(a){if(!a.metaKey&&!a.shiftKey&&!a.altKey&&!a.ctrlKey){if(a.keyCode==h.ENTER){a.preventDefault();d.blur()}}});if(p(a)){var f=o(a);f.click(function(b){b.preventDefault();I(a)});f.keypress(function(a){if(!a.metaKey&&!a.shiftKey&&!a.altKey&&!a.ctrlKey){if(a.keyCode==h.ENTER||a.keyCode==h.SPACE){a.preventDefault();f.click()}}})}if(r(a)){var i=q(a);i.on(g.CAPABILITIES_AGREE,function(){F(a)});i.on(g.CAPABILITIES_DECLINE,function(){H(a)})}},K=function(b){var c=a("#"+b.data("uniqid")+"-"+b.data("deploymentid")),f={uniqid:b.data("uniqid"),platformid:b.data("platformid"),clientid:b.data("clientid"),deploymentid:b.data("deploymentid"),urls:{publickeyset:b.data("publickeyseturl"),accesstoken:b.data("accesstokenurl"),authrequest:b.data("authrequesturl")}},g=d.render("mod_lti/tool_config_modal_body",f),h="mailto:?subject="+encodeURIComponent(b.data("mailtosubject"))+"&body="+encodeURIComponent(b.data("platformidstr"))+":%20"+encodeURIComponent(b.data("platformid"))+"%0D%0A"+encodeURIComponent(b.data("clientidstr"))+":%20"+encodeURIComponent(b.data("clientid"))+"%0D%0A"+encodeURIComponent(b.data("deploymentidstr"))+":%20"+encodeURIComponent(b.data("deploymentid"))+"%0D%0A"+encodeURIComponent(b.data("publickeyseturlstr"))+":%20"+encodeURIComponent(b.data("publickeyseturl"))+"%0D%0A"+encodeURIComponent(b.data("accesstokenurlstr"))+":%20"+encodeURIComponent(b.data("accesstokenurl"))+"%0D%0A"+encodeURIComponent(b.data("authrequesturlstr"))+":%20"+encodeURIComponent(b.data("authrequesturl"))+"%0D%0A";f={mailto:h};var i=d.render("mod_lti/tool_config_modal_footer",f);e.create({large:!0,title:b.data("modaltitle"),body:g,footer:i},c)};return{init:function init(a){J(a);K(a)}}}); //# sourceMappingURL=tool_card_controller.min.js.map diff --git a/mod/lti/amd/build/tool_card_controller.min.js.map b/mod/lti/amd/build/tool_card_controller.min.js.map index bfe3b34df06..e99b87b774b 100644 --- a/mod/lti/amd/build/tool_card_controller.min.js.map +++ b/mod/lti/amd/build/tool_card_controller.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/tool_card_controller.js"],"names":["define","$","ajax","notification","templates","modalFactory","toolType","ltiEvents","KEYS","str","SELECTORS","DELETE_BUTTON","NAME_ELEMENT","DESCRIPTION_ELEMENT","CAPABILITIES_CONTAINER","ACTIVATE_BUTTON","ANNOUNCEMENT_TIMEOUT","getDeleteButton","element","find","getNameElement","getDescriptionElement","getActivateButton","hasActivateButton","length","getCapabilitiesContainer","hasCapabilitiesContainer","getTypeId","attr","clearAllAnnouncements","removeClass","startLoading","addClass","stopLoading","announceSuccess","promise","Deferred","setTimeout","resolve","announceFailure","deleteType","typeId","get_strings","key","component","done","strs","confirm","delete","remove","fail","exception","always","error","reject","setValueSnapshot","value","getValueSnapshot","snapshotDescription","descriptionElement","hasClass","description","text","trim","updateDescription","snapshotVal","update","id","type","snapshotName","nameElement","name","updateName","setStatusActive","state","constants","configured","then","toolTypeData","render","renderResult","html","js","replaceNode","catch","displayCapabilitiesApproval","hideCapabilitiesApproval","activateToolType","registerEventListeners","deleteButton","click","e","preventDefault","keypress","metaKey","shiftKey","altKey","ctrlKey","keyCode","ENTER","SPACE","focus","blur","activateButton","capabilitiesContainer","on","CAPABILITIES_AGREE","CAPABILITIES_DECLINE","registerModal","trigger","data","context","bodyPromise","mailTo","encodeURIComponent","footerPromise","create","large","title","body","footer","init"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Controls all of the behaviour and interaction with a tool type card. These are\n * listed on the LTI tool type management page.\n *\n * See template: mod_lti/tool_card\n *\n * @module mod_lti/tool_card_controller\n * @class tool_card_controller\n * @package mod_lti\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\n define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'core/modal_factory',\n 'mod_lti/tool_type', 'mod_lti/events', 'mod_lti/keys',\n 'core/str'],\n function($, ajax, notification, templates, modalFactory, toolType, ltiEvents, KEYS, str) {\n\n var SELECTORS = {\n DELETE_BUTTON: '.delete',\n NAME_ELEMENT: '.name',\n DESCRIPTION_ELEMENT: '.description',\n CAPABILITIES_CONTAINER: '.capabilities-container',\n ACTIVATE_BUTTON: '.tool-card-footer a.activate',\n };\n\n // Timeout in seconds.\n var ANNOUNCEMENT_TIMEOUT = 2000;\n\n /**\n * Return the delete button element.\n *\n * @method getDeleteButton\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getDeleteButton = function(element) {\n return element.find(SELECTORS.DELETE_BUTTON);\n };\n\n /**\n * Return the element representing the tool type name.\n *\n * @method getNameElement\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getNameElement = function(element) {\n return element.find(SELECTORS.NAME_ELEMENT);\n };\n\n /**\n * Return the element representing the tool type description.\n *\n * @method getDescriptionElement\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getDescriptionElement = function(element) {\n return element.find(SELECTORS.DESCRIPTION_ELEMENT);\n };\n\n /**\n * Return the activate button for the type.\n *\n * @method getActivateButton\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {Object} jQuery object\n */\n var getActivateButton = function(element) {\n return element.find(SELECTORS.ACTIVATE_BUTTON);\n };\n\n /**\n * Checks if the type card has an activate button.\n *\n * @method hasActivateButton\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Boolean} true if has active buton\n */\n var hasActivateButton = function(element) {\n return getActivateButton(element).length ? true : false;\n };\n\n /**\n * Return the element that contains the capabilities approval for\n * the user.\n *\n * @method getCapabilitiesContainer\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {Object} The element\n */\n var getCapabilitiesContainer = function(element) {\n return element.find(SELECTORS.CAPABILITIES_CONTAINER);\n };\n\n /**\n * Checks if the tool type has capabilities that need approval. If it\n * does then the container will be present.\n *\n * @method hasCapabilitiesContainer\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Boolean} true if has capbilities.\n */\n var hasCapabilitiesContainer = function(element) {\n return getCapabilitiesContainer(element).length ? true : false;\n };\n\n /**\n * Get the type id.\n *\n * @method getTypeId\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {String} Type ID\n */\n var getTypeId = function(element) {\n return element.attr('data-type-id');\n };\n\n /**\n * Stop any announcement currently visible on the card.\n *\n * @method clearAllAnnouncements\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var clearAllAnnouncements = function(element) {\n element.removeClass('announcement loading success fail capabilities');\n };\n\n /**\n * Show the loading announcement.\n *\n * @method startLoading\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var startLoading = function(element) {\n clearAllAnnouncements(element);\n element.addClass('announcement loading');\n };\n\n /**\n * Hide the loading announcement.\n *\n * @method stopLoading\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var stopLoading = function(element) {\n element.removeClass('announcement loading');\n };\n\n /**\n * Show the success announcement. The announcement is only\n * visible for 2 seconds.\n *\n * @method announceSuccess\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var announceSuccess = function(element) {\n var promise = $.Deferred();\n\n clearAllAnnouncements(element);\n element.addClass('announcement success');\n setTimeout(function() {\n element.removeClass('announcement success');\n promise.resolve();\n }, ANNOUNCEMENT_TIMEOUT);\n\n return promise;\n };\n\n /**\n * Show the failure announcement. The announcement is only\n * visible for 2 seconds.\n *\n * @method announceFailure\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var announceFailure = function(element) {\n var promise = $.Deferred();\n\n clearAllAnnouncements(element);\n element.addClass('announcement fail');\n setTimeout(function() {\n element.removeClass('announcement fail');\n promise.resolve();\n }, ANNOUNCEMENT_TIMEOUT);\n\n return promise;\n };\n\n /**\n * Delete the tool type from the Moodle server. Triggers a success\n * or failure announcement depending on the result.\n *\n * @method deleteType\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var deleteType = function(element) {\n var promise = $.Deferred();\n var typeId = getTypeId(element);\n startLoading(element);\n\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n str.get_strings([\n {\n key: 'delete',\n component: 'mod_lti'\n },\n {\n key: 'delete_confirmation',\n component: 'mod_lti'\n },\n {\n key: 'delete',\n component: 'mod_lti'\n },\n {\n key: 'cancel',\n component: 'core'\n },\n ])\n .done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n toolType.delete(typeId)\n .done(function() {\n stopLoading(element);\n announceSuccess(element)\n .done(function() {\n element.remove();\n })\n .fail(notification.exception)\n .always(function() {\n // Always resolve because even if the announcement fails the type was deleted.\n promise.resolve();\n });\n })\n .fail(function(error) {\n announceFailure(element);\n promise.reject(error);\n });\n }, function() {\n stopLoading(element);\n promise.resolve();\n });\n })\n .fail(function(error) {\n stopLoading(element);\n notification.exception(error);\n promise.reject(error);\n });\n\n return promise;\n };\n\n /**\n * Save a given value in a data attribute on the element.\n *\n * @method setValueSnapshot\n * @private\n * @param {JQuery} element jQuery object representing the element.\n * @param {String} value to be saved.\n */\n var setValueSnapshot = function(element, value) {\n element.attr('data-val-snapshot', value);\n };\n\n /**\n * Return the saved value from the element.\n *\n * @method getValueSnapshot\n * @private\n * @param {JQuery} element jQuery object representing the element.\n * @return {String} the saved value.\n */\n var getValueSnapshot = function(element) {\n return element.attr('data-val-snapshot');\n };\n\n /**\n * Save the current value of the tool description.\n *\n * @method snapshotDescription\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var snapshotDescription = function(element) {\n var descriptionElement = getDescriptionElement(element);\n\n if (descriptionElement.hasClass('loading')) {\n return;\n }\n\n var description = descriptionElement.text().trim();\n setValueSnapshot(descriptionElement, description);\n };\n\n /**\n * Send a request to update the description value for this tool\n * in the Moodle server.\n *\n * @method updateDescription\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var updateDescription = function(element) {\n var typeId = getTypeId(element);\n\n // Return early if we don't have an id because it's\n // required to save the changes.\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n var descriptionElement = getDescriptionElement(element);\n\n // Return early if we're already saving a value.\n if (descriptionElement.hasClass('loading')) {\n return $.Deferred().resolve();\n }\n\n var description = descriptionElement.text().trim();\n var snapshotVal = getValueSnapshot(descriptionElement);\n\n // If the value hasn't change then don't bother sending the\n // update request.\n if (snapshotVal == description) {\n return $.Deferred().resolve();\n }\n\n descriptionElement.addClass('loading');\n\n var promise = toolType.update({id: typeId, description: description});\n\n promise.done(function(type) {\n descriptionElement.removeClass('loading');\n // Make sure the text is updated with the description from the\n // server, just in case the update didn't work.\n descriptionElement.text(type.description);\n }).fail(notification.exception);\n\n // Probably need to handle failures better so that we can revert\n // the value in the input for the user.\n promise.fail(function() {\n descriptionElement.removeClass('loading');\n });\n\n return promise;\n };\n\n /**\n * Save the current value of the tool name.\n *\n * @method snapshotName\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var snapshotName = function(element) {\n var nameElement = getNameElement(element);\n\n if (nameElement.hasClass('loading')) {\n return;\n }\n\n var name = nameElement.text().trim();\n setValueSnapshot(nameElement, name);\n };\n\n /**\n * Send a request to update the name value for this tool\n * in the Moodle server.\n *\n * @method updateName\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var updateName = function(element) {\n var typeId = getTypeId(element);\n\n // Return if we don't have an id.\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n var nameElement = getNameElement(element);\n\n // Return if we're already saving.\n if (nameElement.hasClass('loading')) {\n return $.Deferred().resolve();\n }\n\n var name = nameElement.text().trim();\n var snapshotVal = getValueSnapshot(nameElement);\n\n // If the value hasn't change then don't bother sending the\n // update request.\n if (snapshotVal == name) {\n return $.Deferred().resolve();\n }\n\n nameElement.addClass('loading');\n var promise = toolType.update({id: typeId, name: name});\n\n promise.done(function(type) {\n nameElement.removeClass('loading');\n // Make sure the text is updated with the name from the\n // server, just in case the update didn't work.\n nameElement.text(type.name);\n });\n\n // Probably need to handle failures better so that we can revert\n // the value in the input for the user.\n promise.fail(function() {\n nameElement.removeClass('loading');\n });\n\n return promise;\n };\n\n /**\n * Send a request to update the state for this tool to be configured (active)\n * in the Moodle server. A success or failure announcement is triggered depending\n * on the result.\n *\n * @method setStatusActive\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var setStatusActive = function(element) {\n var id = getTypeId(element);\n\n // Return if we don't have an id.\n if (id === \"\") {\n return $.Deferred().resolve();\n }\n\n startLoading(element);\n\n var promise = toolType.update({\n id: id,\n state: toolType.constants.state.configured\n });\n\n promise.then(function(toolTypeData) {\n stopLoading(element);\n announceSuccess(element);\n return toolTypeData;\n }).then(function(toolTypeData) {\n return templates.render('mod_lti/tool_card', toolTypeData);\n }).then(function(renderResult) {\n var html = renderResult[0];\n var js = renderResult[1];\n\n templates.replaceNode(element, html, js);\n return;\n }).catch(function() {\n stopLoading(element);\n announceFailure(element);\n });\n\n return promise;\n };\n\n /**\n * Show the capabilities approval screen to show which groups of data this\n * type requires access to in Moodle (if any).\n *\n * @method displayCapabilitiesApproval\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var displayCapabilitiesApproval = function(element) {\n element.addClass('announcement capabilities');\n };\n\n /**\n * Hide the capabilities approval screen.\n *\n * @method hideCapabilitiesApproval\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var hideCapabilitiesApproval = function(element) {\n element.removeClass('announcement capabilities');\n };\n\n /**\n * The user wishes to activate this tool so show them the capabilities that\n * they need to agree to or if there are none then set the tool type's state\n * to active.\n *\n * @method activateToolType\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var activateToolType = function(element) {\n if (hasCapabilitiesContainer(element)) {\n displayCapabilitiesApproval(element);\n } else {\n setStatusActive(element);\n }\n };\n\n /**\n * Sets up the listeners for user interaction on this tool type card.\n *\n * @method registerEventListeners\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var registerEventListeners = function(element) {\n var deleteButton = getDeleteButton(element);\n deleteButton.click(function(e) {\n e.preventDefault();\n deleteType(element);\n });\n deleteButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n deleteButton.click();\n }\n }\n });\n\n var descriptionElement = getDescriptionElement(element);\n descriptionElement.focus(function(e) {\n e.preventDefault();\n // Save a copy of the current value for the description so that\n // we can check if the user has changed it before sending a request to\n // the server.\n snapshotDescription(element);\n });\n descriptionElement.blur(function(e) {\n e.preventDefault();\n updateDescription(element);\n });\n descriptionElement.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER) {\n e.preventDefault();\n descriptionElement.blur();\n }\n }\n });\n\n var nameElement = getNameElement(element);\n nameElement.focus(function(e) {\n e.preventDefault();\n // Save a copy of the current value for the name so that\n // we can check if the user has changed it before sending a request to\n // the server.\n snapshotName(element);\n });\n nameElement.blur(function(e) {\n e.preventDefault();\n updateName(element);\n });\n nameElement.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER) {\n e.preventDefault();\n nameElement.blur();\n }\n }\n });\n\n // Only pending tool type cards have an activate button.\n if (hasActivateButton(element)) {\n var activateButton = getActivateButton(element);\n activateButton.click(function(e) {\n e.preventDefault();\n activateToolType(element);\n });\n activateButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n activateButton.click();\n }\n }\n });\n }\n\n if (hasCapabilitiesContainer(element)) {\n var capabilitiesContainer = getCapabilitiesContainer(element);\n\n capabilitiesContainer.on(ltiEvents.CAPABILITIES_AGREE, function() {\n setStatusActive(element);\n });\n\n capabilitiesContainer.on(ltiEvents.CAPABILITIES_DECLINE, function() {\n hideCapabilitiesApproval(element);\n });\n }\n };\n\n /**\n * Sets up the templates for the tool configuration modal on this tool type card.\n *\n * @method registerModal\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var registerModal = function(element) {\n var trigger = $('#' + element.data('uniqid') + '-' + element.data('deploymentid'));\n var context = {\n 'uniqid': element.data('uniqid'),\n 'platformid': element.data('platformid'),\n 'clientid': element.data('clientid'),\n 'deploymentid': element.data('deploymentid'),\n 'urls': {\n 'publickeyset': element.data('publickeyseturl'),\n 'accesstoken': element.data('accesstokenurl'),\n 'authrequest': element.data('authrequesturl')\n }\n };\n var bodyPromise = templates.render('mod_lti/tool_config_modal_body', context);\n var mailTo = 'mailto:?subject=' + encodeURIComponent(element.data('mailtosubject')) +\n '&body=' + encodeURIComponent(element.data('platformidstr')) + ':%20' +\n encodeURIComponent(element.data('platformid')) + '%0D%0A' +\n encodeURIComponent(element.data('clientidstr')) + ':%20' +\n encodeURIComponent(element.data('clientid')) + '%0D%0A' +\n encodeURIComponent(element.data('deploymentidstr')) + ':%20' +\n encodeURIComponent(element.data('deploymentid')) + '%0D%0A' +\n encodeURIComponent(element.data('publickeyseturlstr')) + ':%20' +\n encodeURIComponent(element.data('publickeyseturl')) + '%0D%0A' +\n encodeURIComponent(element.data('accesstokenurlstr')) + ':%20' +\n encodeURIComponent(element.data('accesstokenurl')) + '%0D%0A' +\n encodeURIComponent(element.data('authrequesturlstr')) + ':%20' +\n encodeURIComponent(element.data('authrequesturl')) + '%0D%0A';\n context = {\n 'mailto': mailTo\n };\n var footerPromise = templates.render('mod_lti/tool_config_modal_footer', context);\n modalFactory.create({\n large: true,\n title: element.data('modaltitle'),\n body: bodyPromise,\n footer: footerPromise,\n }, trigger);\n };\n\n return /** @alias module:mod_lti/tool_card_controller */ {\n\n /**\n * Initialise this module.\n *\n * @param {JQuery} element jQuery object representing the tool card.\n */\n init: function(element) {\n registerEventListeners(element);\n registerModal(element);\n }\n };\n});\n"],"file":"tool_card_controller.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/tool_card_controller.js"],"names":["define","$","ajax","notification","templates","modalFactory","toolType","ltiEvents","KEYS","str","SELECTORS","DELETE_BUTTON","NAME_ELEMENT","DESCRIPTION_ELEMENT","CAPABILITIES_CONTAINER","ACTIVATE_BUTTON","ANNOUNCEMENT_TIMEOUT","getDeleteButton","element","find","getNameElement","getDescriptionElement","getActivateButton","hasActivateButton","length","getCapabilitiesContainer","hasCapabilitiesContainer","getTypeId","attr","clearAllAnnouncements","removeClass","startLoading","addClass","stopLoading","announceSuccess","promise","Deferred","setTimeout","resolve","announceFailure","deleteType","typeId","get_strings","key","component","done","strs","confirm","delete","remove","fail","exception","always","error","reject","setValueSnapshot","value","getValueSnapshot","snapshotDescription","descriptionElement","hasClass","description","text","trim","updateDescription","snapshotVal","update","id","type","snapshotName","nameElement","name","updateName","setStatusActive","state","constants","configured","then","toolTypeData","render","html","js","replaceNode","catch","displayCapabilitiesApproval","hideCapabilitiesApproval","activateToolType","registerEventListeners","deleteButton","click","e","preventDefault","keypress","metaKey","shiftKey","altKey","ctrlKey","keyCode","ENTER","SPACE","focus","blur","activateButton","capabilitiesContainer","on","CAPABILITIES_AGREE","CAPABILITIES_DECLINE","registerModal","trigger","data","context","bodyPromise","mailTo","encodeURIComponent","footerPromise","create","large","title","body","footer","init"],"mappings":"AA4BCA,OAAM,gCAAC,CAAC,QAAD,CAAW,WAAX,CAAwB,mBAAxB,CAA6C,gBAA7C,CAA+D,oBAA/D,CACA,mBADA,CACqB,gBADrB,CACuC,cADvC,CAEA,UAFA,CAAD,CAGC,SAASC,CAAT,CAAYC,CAAZ,CAAkBC,CAAlB,CAAgCC,CAAhC,CAA2CC,CAA3C,CAAyDC,CAAzD,CAAmEC,CAAnE,CAA8EC,CAA9E,CAAoFC,CAApF,CAAyF,IAEzFC,CAAAA,CAAS,CAAG,CACZC,aAAa,CAAE,SADH,CAEZC,YAAY,CAAE,OAFF,CAGZC,mBAAmB,CAAE,cAHT,CAIZC,sBAAsB,CAAE,yBAJZ,CAKZC,eAAe,CAAE,8BALL,CAF6E,CAWzFC,CAAoB,CAAG,GAXkE,CAqBzFC,CAAe,CAAG,SAASC,CAAT,CAAkB,CACpC,MAAOA,CAAAA,CAAO,CAACC,IAAR,CAAaT,CAAS,CAACC,aAAvB,CACV,CAvB4F,CAiCzFS,CAAc,CAAG,SAASF,CAAT,CAAkB,CACnC,MAAOA,CAAAA,CAAO,CAACC,IAAR,CAAaT,CAAS,CAACE,YAAvB,CACV,CAnC4F,CA6CzFS,CAAqB,CAAG,SAASH,CAAT,CAAkB,CAC1C,MAAOA,CAAAA,CAAO,CAACC,IAAR,CAAaT,CAAS,CAACG,mBAAvB,CACV,CA/C4F,CAyDzFS,CAAiB,CAAG,SAASJ,CAAT,CAAkB,CACtC,MAAOA,CAAAA,CAAO,CAACC,IAAR,CAAaT,CAAS,CAACK,eAAvB,CACV,CA3D4F,CAqEzFQ,CAAiB,CAAG,SAASL,CAAT,CAAkB,CACtC,MAAOI,CAAAA,CAAiB,CAACJ,CAAD,CAAjB,CAA2BM,MAA3B,MACV,CAvE4F,CAkFzFC,CAAwB,CAAG,SAASP,CAAT,CAAkB,CAC7C,MAAOA,CAAAA,CAAO,CAACC,IAAR,CAAaT,CAAS,CAACI,sBAAvB,CACV,CApF4F,CA+FzFY,CAAwB,CAAG,SAASR,CAAT,CAAkB,CAC7C,MAAOO,CAAAA,CAAwB,CAACP,CAAD,CAAxB,CAAkCM,MAAlC,MACV,CAjG4F,CA2GzFG,CAAS,CAAG,SAAST,CAAT,CAAkB,CAC9B,MAAOA,CAAAA,CAAO,CAACU,IAAR,CAAa,cAAb,CACV,CA7G4F,CAsHzFC,CAAqB,CAAG,SAASX,CAAT,CAAkB,CAC1CA,CAAO,CAACY,WAAR,CAAoB,gDAApB,CACH,CAxH4F,CAiIzFC,CAAY,CAAG,SAASb,CAAT,CAAkB,CACjCW,CAAqB,CAACX,CAAD,CAArB,CACAA,CAAO,CAACc,QAAR,CAAiB,sBAAjB,CACH,CApI4F,CA6IzFC,CAAW,CAAG,SAASf,CAAT,CAAkB,CAChCA,CAAO,CAACY,WAAR,CAAoB,sBAApB,CACH,CA/I4F,CA0JzFI,CAAe,CAAG,SAAShB,CAAT,CAAkB,CACpC,GAAIiB,CAAAA,CAAO,CAAGlC,CAAC,CAACmC,QAAF,EAAd,CAEAP,CAAqB,CAACX,CAAD,CAArB,CACAA,CAAO,CAACc,QAAR,CAAiB,sBAAjB,EACAK,UAAU,CAAC,UAAW,CAClBnB,CAAO,CAACY,WAAR,CAAoB,sBAApB,EACAK,CAAO,CAACG,OAAR,EACH,CAHS,CAGPtB,CAHO,CAAV,CAKA,MAAOmB,CAAAA,CACV,CArK4F,CAgLzFI,CAAe,CAAG,SAASrB,CAAT,CAAkB,CACpC,GAAIiB,CAAAA,CAAO,CAAGlC,CAAC,CAACmC,QAAF,EAAd,CAEAP,CAAqB,CAACX,CAAD,CAArB,CACAA,CAAO,CAACc,QAAR,CAAiB,mBAAjB,EACAK,UAAU,CAAC,UAAW,CAClBnB,CAAO,CAACY,WAAR,CAAoB,mBAApB,EACAK,CAAO,CAACG,OAAR,EACH,CAHS,CAGPtB,CAHO,CAAV,CAKA,MAAOmB,CAAAA,CACV,CA3L4F,CAsMzFK,CAAU,CAAG,SAAStB,CAAT,CAAkB,IAC3BiB,CAAAA,CAAO,CAAGlC,CAAC,CAACmC,QAAF,EADiB,CAE3BK,CAAM,CAAGd,CAAS,CAACT,CAAD,CAFS,CAG/Ba,CAAY,CAACb,CAAD,CAAZ,CAEA,GAAe,EAAX,GAAAuB,CAAJ,CAAmB,CACf,MAAOxC,CAAAA,CAAC,CAACmC,QAAF,GAAaE,OAAb,EACV,CAED7B,CAAG,CAACiC,WAAJ,CAAgB,CACR,CACIC,GAAG,CAAE,QADT,CAEIC,SAAS,CAAE,SAFf,CADQ,CAKR,CACID,GAAG,CAAE,qBADT,CAEIC,SAAS,CAAE,SAFf,CALQ,CASR,CACID,GAAG,CAAE,QADT,CAEIC,SAAS,CAAE,SAFf,CATQ,CAaR,CACID,GAAG,CAAE,QADT,CAEIC,SAAS,CAAE,MAFf,CAbQ,CAAhB,EAkBKC,IAlBL,CAkBU,SAASC,CAAT,CAAe,CACb3C,CAAY,CAAC4C,OAAb,CAAqBD,CAAI,CAAC,CAAD,CAAzB,CAA8BA,CAAI,CAAC,CAAD,CAAlC,CAAuCA,CAAI,CAAC,CAAD,CAA3C,CAAgDA,CAAI,CAAC,CAAD,CAApD,CAAyD,UAAW,CAC5DxC,CAAQ,CAAC0C,MAAT,CAAgBP,CAAhB,EACKI,IADL,CACU,UAAW,CACTZ,CAAW,CAACf,CAAD,CAAX,CACAgB,CAAe,CAAChB,CAAD,CAAf,CACK2B,IADL,CACU,UAAW,CACT3B,CAAO,CAAC+B,MAAR,EACH,CAHT,EAIKC,IAJL,CAIU/C,CAAY,CAACgD,SAJvB,EAKKC,MALL,CAKY,UAAW,CAEXjB,CAAO,CAACG,OAAR,EACH,CART,CASH,CAZT,EAaKY,IAbL,CAaU,SAASG,CAAT,CAAgB,CACdd,CAAe,CAACrB,CAAD,CAAf,CACAiB,CAAO,CAACmB,MAAR,CAAeD,CAAf,CACH,CAhBT,CAiBH,CAlBL,CAkBO,UAAW,CACNpB,CAAW,CAACf,CAAD,CAAX,CACAiB,CAAO,CAACG,OAAR,EACH,CArBT,CAsBH,CAzCT,EA0CKY,IA1CL,CA0CU,SAASG,CAAT,CAAgB,CACdpB,CAAW,CAACf,CAAD,CAAX,CACAf,CAAY,CAACgD,SAAb,CAAuBE,CAAvB,EACAlB,CAAO,CAACmB,MAAR,CAAeD,CAAf,CACH,CA9CT,EAgDA,MAAOlB,CAAAA,CACV,CAhQ4F,CA0QzFoB,CAAgB,CAAG,SAASrC,CAAT,CAAkBsC,CAAlB,CAAyB,CAC5CtC,CAAO,CAACU,IAAR,CAAa,mBAAb,CAAkC4B,CAAlC,CACH,CA5Q4F,CAsRzFC,CAAgB,CAAG,SAASvC,CAAT,CAAkB,CACrC,MAAOA,CAAAA,CAAO,CAACU,IAAR,CAAa,mBAAb,CACV,CAxR4F,CAiSzF8B,CAAmB,CAAG,SAASxC,CAAT,CAAkB,CACxC,GAAIyC,CAAAA,CAAkB,CAAGtC,CAAqB,CAACH,CAAD,CAA9C,CAEA,GAAIyC,CAAkB,CAACC,QAAnB,CAA4B,SAA5B,CAAJ,CAA4C,CACxC,MACH,CAED,GAAIC,CAAAA,CAAW,CAAGF,CAAkB,CAACG,IAAnB,GAA0BC,IAA1B,EAAlB,CACAR,CAAgB,CAACI,CAAD,CAAqBE,CAArB,CACnB,CA1S4F,CAqTzFG,CAAiB,CAAG,SAAS9C,CAAT,CAAkB,CACtC,GAAIuB,CAAAA,CAAM,CAAGd,CAAS,CAACT,CAAD,CAAtB,CAIA,GAAe,EAAX,GAAAuB,CAAJ,CAAmB,CACf,MAAOxC,CAAAA,CAAC,CAACmC,QAAF,GAAaE,OAAb,EACV,CAED,GAAIqB,CAAAA,CAAkB,CAAGtC,CAAqB,CAACH,CAAD,CAA9C,CAGA,GAAIyC,CAAkB,CAACC,QAAnB,CAA4B,SAA5B,CAAJ,CAA4C,CACxC,MAAO3D,CAAAA,CAAC,CAACmC,QAAF,GAAaE,OAAb,EACV,CAdqC,GAgBlCuB,CAAAA,CAAW,CAAGF,CAAkB,CAACG,IAAnB,GAA0BC,IAA1B,EAhBoB,CAiBlCE,CAAW,CAAGR,CAAgB,CAACE,CAAD,CAjBI,CAqBtC,GAAIM,CAAW,EAAIJ,CAAnB,CAAgC,CAC5B,MAAO5D,CAAAA,CAAC,CAACmC,QAAF,GAAaE,OAAb,EACV,CAEDqB,CAAkB,CAAC3B,QAAnB,CAA4B,SAA5B,EAEA,GAAIG,CAAAA,CAAO,CAAG7B,CAAQ,CAAC4D,MAAT,CAAgB,CAACC,EAAE,CAAE1B,CAAL,CAAaoB,WAAW,CAAEA,CAA1B,CAAhB,CAAd,CAEA1B,CAAO,CAACU,IAAR,CAAa,SAASuB,CAAT,CAAe,CACxBT,CAAkB,CAAC7B,WAAnB,CAA+B,SAA/B,EAGA6B,CAAkB,CAACG,IAAnB,CAAwBM,CAAI,CAACP,WAA7B,CACH,CALD,EAKGX,IALH,CAKQ/C,CAAY,CAACgD,SALrB,EASAhB,CAAO,CAACe,IAAR,CAAa,UAAW,CACtBS,CAAkB,CAAC7B,WAAnB,CAA+B,SAA/B,CACD,CAFD,EAIA,MAAOK,CAAAA,CACV,CAhW4F,CAyWzFkC,CAAY,CAAG,SAASnD,CAAT,CAAkB,CACjC,GAAIoD,CAAAA,CAAW,CAAGlD,CAAc,CAACF,CAAD,CAAhC,CAEA,GAAIoD,CAAW,CAACV,QAAZ,CAAqB,SAArB,CAAJ,CAAqC,CACjC,MACH,CAED,GAAIW,CAAAA,CAAI,CAAGD,CAAW,CAACR,IAAZ,GAAmBC,IAAnB,EAAX,CACAR,CAAgB,CAACe,CAAD,CAAcC,CAAd,CACnB,CAlX4F,CA6XzFC,CAAU,CAAG,SAAStD,CAAT,CAAkB,CAC/B,GAAIuB,CAAAA,CAAM,CAAGd,CAAS,CAACT,CAAD,CAAtB,CAGA,GAAe,EAAX,GAAAuB,CAAJ,CAAmB,CACf,MAAOxC,CAAAA,CAAC,CAACmC,QAAF,GAAaE,OAAb,EACV,CAED,GAAIgC,CAAAA,CAAW,CAAGlD,CAAc,CAACF,CAAD,CAAhC,CAGA,GAAIoD,CAAW,CAACV,QAAZ,CAAqB,SAArB,CAAJ,CAAqC,CACjC,MAAO3D,CAAAA,CAAC,CAACmC,QAAF,GAAaE,OAAb,EACV,CAb8B,GAe3BiC,CAAAA,CAAI,CAAGD,CAAW,CAACR,IAAZ,GAAmBC,IAAnB,EAfoB,CAgB3BE,CAAW,CAAGR,CAAgB,CAACa,CAAD,CAhBH,CAoB/B,GAAIL,CAAW,EAAIM,CAAnB,CAAyB,CACrB,MAAOtE,CAAAA,CAAC,CAACmC,QAAF,GAAaE,OAAb,EACV,CAEDgC,CAAW,CAACtC,QAAZ,CAAqB,SAArB,EACA,GAAIG,CAAAA,CAAO,CAAG7B,CAAQ,CAAC4D,MAAT,CAAgB,CAACC,EAAE,CAAE1B,CAAL,CAAa8B,IAAI,CAAEA,CAAnB,CAAhB,CAAd,CAEApC,CAAO,CAACU,IAAR,CAAa,SAASuB,CAAT,CAAe,CACxBE,CAAW,CAACxC,WAAZ,CAAwB,SAAxB,EAGAwC,CAAW,CAACR,IAAZ,CAAiBM,CAAI,CAACG,IAAtB,CACH,CALD,EASApC,CAAO,CAACe,IAAR,CAAa,UAAW,CACtBoB,CAAW,CAACxC,WAAZ,CAAwB,SAAxB,CACD,CAFD,EAIA,MAAOK,CAAAA,CACV,CAta4F,CAkbzFsC,CAAe,CAAG,SAASvD,CAAT,CAAkB,CACpC,GAAIiD,CAAAA,CAAE,CAAGxC,CAAS,CAACT,CAAD,CAAlB,CAGA,GAAW,EAAP,GAAAiD,CAAJ,CAAe,CACX,MAAOlE,CAAAA,CAAC,CAACmC,QAAF,GAAaE,OAAb,EACV,CAEDP,CAAY,CAACb,CAAD,CAAZ,CAEA,GAAIiB,CAAAA,CAAO,CAAG7B,CAAQ,CAAC4D,MAAT,CAAgB,CAC1BC,EAAE,CAAEA,CADsB,CAE1BO,KAAK,CAAEpE,CAAQ,CAACqE,SAAT,CAAmBD,KAAnB,CAAyBE,UAFN,CAAhB,CAAd,CAKAzC,CAAO,CAAC0C,IAAR,CAAa,SAASC,CAAT,CAAuB,CAChC7C,CAAW,CAACf,CAAD,CAAX,CACAgB,CAAe,CAAChB,CAAD,CAAf,CACA,MAAO4D,CAAAA,CACV,CAJD,EAIGD,IAJH,CAIQ,SAASC,CAAT,CAAuB,CAC3B,MAAO1E,CAAAA,CAAS,CAAC2E,MAAV,CAAiB,mBAAjB,CAAsCD,CAAtC,CACV,CAND,EAMGD,IANH,CAMQ,SAASG,CAAT,CAAeC,CAAf,CAAmB,CACvB7E,CAAS,CAAC8E,WAAV,CAAsBhE,CAAtB,CAA+B8D,CAA/B,CAAqCC,CAArC,CAEH,CATD,EASGE,KATH,CASS,UAAW,CAChBlD,CAAW,CAACf,CAAD,CAAX,CACAqB,CAAe,CAACrB,CAAD,CAClB,CAZD,EAcA,MAAOiB,CAAAA,CACV,CAhd4F,CA0dzFiD,CAA2B,CAAG,SAASlE,CAAT,CAAkB,CAChDA,CAAO,CAACc,QAAR,CAAiB,2BAAjB,CACH,CA5d4F,CAqezFqD,CAAwB,CAAG,SAASnE,CAAT,CAAkB,CAC7CA,CAAO,CAACY,WAAR,CAAoB,2BAApB,CACH,CAve4F,CAkfzFwD,CAAgB,CAAG,SAASpE,CAAT,CAAkB,CACrC,GAAIQ,CAAwB,CAACR,CAAD,CAA5B,CAAuC,CACnCkE,CAA2B,CAAClE,CAAD,CAC9B,CAFD,IAEO,CACHuD,CAAe,CAACvD,CAAD,CAClB,CACJ,CAxf4F,CAigBzFqE,CAAsB,CAAG,SAASrE,CAAT,CAAkB,CAC3C,GAAIsE,CAAAA,CAAY,CAAGvE,CAAe,CAACC,CAAD,CAAlC,CACAsE,CAAY,CAACC,KAAb,CAAmB,SAASC,CAAT,CAAY,CAC3BA,CAAC,CAACC,cAAF,GACAnD,CAAU,CAACtB,CAAD,CACb,CAHD,EAIAsE,CAAY,CAACI,QAAb,CAAsB,SAASF,CAAT,CAAY,CAC9B,GAAI,CAACA,CAAC,CAACG,OAAH,EAAc,CAACH,CAAC,CAACI,QAAjB,EAA6B,CAACJ,CAAC,CAACK,MAAhC,EAA0C,CAACL,CAAC,CAACM,OAAjD,CAA0D,CACtD,GAAIN,CAAC,CAACO,OAAF,EAAazF,CAAI,CAAC0F,KAAlB,EAA2BR,CAAC,CAACO,OAAF,EAAazF,CAAI,CAAC2F,KAAjD,CAAwD,CACpDT,CAAC,CAACC,cAAF,GACAH,CAAY,CAACC,KAAb,EACH,CACJ,CACJ,CAPD,EASA,GAAI9B,CAAAA,CAAkB,CAAGtC,CAAqB,CAACH,CAAD,CAA9C,CACAyC,CAAkB,CAACyC,KAAnB,CAAyB,SAASV,CAAT,CAAY,CACjCA,CAAC,CAACC,cAAF,GAIAjC,CAAmB,CAACxC,CAAD,CACtB,CAND,EAOAyC,CAAkB,CAAC0C,IAAnB,CAAwB,SAASX,CAAT,CAAY,CAChCA,CAAC,CAACC,cAAF,GACA3B,CAAiB,CAAC9C,CAAD,CACpB,CAHD,EAIAyC,CAAkB,CAACiC,QAAnB,CAA4B,SAASF,CAAT,CAAY,CACpC,GAAI,CAACA,CAAC,CAACG,OAAH,EAAc,CAACH,CAAC,CAACI,QAAjB,EAA6B,CAACJ,CAAC,CAACK,MAAhC,EAA0C,CAACL,CAAC,CAACM,OAAjD,CAA0D,CACtD,GAAIN,CAAC,CAACO,OAAF,EAAazF,CAAI,CAAC0F,KAAtB,CAA6B,CACzBR,CAAC,CAACC,cAAF,GACAhC,CAAkB,CAAC0C,IAAnB,EACH,CACJ,CACJ,CAPD,EASA,GAAI/B,CAAAA,CAAW,CAAGlD,CAAc,CAACF,CAAD,CAAhC,CACAoD,CAAW,CAAC8B,KAAZ,CAAkB,SAASV,CAAT,CAAY,CAC1BA,CAAC,CAACC,cAAF,GAIAtB,CAAY,CAACnD,CAAD,CACf,CAND,EAOAoD,CAAW,CAAC+B,IAAZ,CAAiB,SAASX,CAAT,CAAY,CACzBA,CAAC,CAACC,cAAF,GACAnB,CAAU,CAACtD,CAAD,CACb,CAHD,EAIAoD,CAAW,CAACsB,QAAZ,CAAqB,SAASF,CAAT,CAAY,CAC7B,GAAI,CAACA,CAAC,CAACG,OAAH,EAAc,CAACH,CAAC,CAACI,QAAjB,EAA6B,CAACJ,CAAC,CAACK,MAAhC,EAA0C,CAACL,CAAC,CAACM,OAAjD,CAA0D,CACtD,GAAIN,CAAC,CAACO,OAAF,EAAazF,CAAI,CAAC0F,KAAtB,CAA6B,CACzBR,CAAC,CAACC,cAAF,GACArB,CAAW,CAAC+B,IAAZ,EACH,CACJ,CACJ,CAPD,EAUA,GAAI9E,CAAiB,CAACL,CAAD,CAArB,CAAgC,CAC5B,GAAIoF,CAAAA,CAAc,CAAGhF,CAAiB,CAACJ,CAAD,CAAtC,CACAoF,CAAc,CAACb,KAAf,CAAqB,SAASC,CAAT,CAAY,CAC7BA,CAAC,CAACC,cAAF,GACAL,CAAgB,CAACpE,CAAD,CACnB,CAHD,EAIAoF,CAAc,CAACV,QAAf,CAAwB,SAASF,CAAT,CAAY,CAChC,GAAI,CAACA,CAAC,CAACG,OAAH,EAAc,CAACH,CAAC,CAACI,QAAjB,EAA6B,CAACJ,CAAC,CAACK,MAAhC,EAA0C,CAACL,CAAC,CAACM,OAAjD,CAA0D,CACtD,GAAIN,CAAC,CAACO,OAAF,EAAazF,CAAI,CAAC0F,KAAlB,EAA2BR,CAAC,CAACO,OAAF,EAAazF,CAAI,CAAC2F,KAAjD,CAAwD,CACpDT,CAAC,CAACC,cAAF,GACAW,CAAc,CAACb,KAAf,EACH,CACJ,CACJ,CAPD,CAQH,CAED,GAAI/D,CAAwB,CAACR,CAAD,CAA5B,CAAuC,CACnC,GAAIqF,CAAAA,CAAqB,CAAG9E,CAAwB,CAACP,CAAD,CAApD,CAEAqF,CAAqB,CAACC,EAAtB,CAAyBjG,CAAS,CAACkG,kBAAnC,CAAuD,UAAW,CAC9DhC,CAAe,CAACvD,CAAD,CAClB,CAFD,EAIAqF,CAAqB,CAACC,EAAtB,CAAyBjG,CAAS,CAACmG,oBAAnC,CAAyD,UAAW,CAChErB,CAAwB,CAACnE,CAAD,CAC3B,CAFD,CAGH,CACJ,CAtlB4F,CA+lBzFyF,CAAa,CAAG,SAASzF,CAAT,CAAkB,IAC9B0F,CAAAA,CAAO,CAAG3G,CAAC,CAAC,IAAMiB,CAAO,CAAC2F,IAAR,CAAa,QAAb,CAAN,CAA+B,GAA/B,CAAqC3F,CAAO,CAAC2F,IAAR,CAAa,cAAb,CAAtC,CADmB,CAE9BC,CAAO,CAAG,CACV,OAAU5F,CAAO,CAAC2F,IAAR,CAAa,QAAb,CADA,CAEV,WAAc3F,CAAO,CAAC2F,IAAR,CAAa,YAAb,CAFJ,CAGV,SAAY3F,CAAO,CAAC2F,IAAR,CAAa,UAAb,CAHF,CAIV,aAAgB3F,CAAO,CAAC2F,IAAR,CAAa,cAAb,CAJN,CAKV,KAAQ,CACJ,aAAgB3F,CAAO,CAAC2F,IAAR,CAAa,iBAAb,CADZ,CAEJ,YAAe3F,CAAO,CAAC2F,IAAR,CAAa,gBAAb,CAFX,CAGJ,YAAe3F,CAAO,CAAC2F,IAAR,CAAa,gBAAb,CAHX,CALE,CAFoB,CAa9BE,CAAW,CAAG3G,CAAS,CAAC2E,MAAV,CAAiB,gCAAjB,CAAmD+B,CAAnD,CAbgB,CAc9BE,CAAM,CAAG,mBAAqBC,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,eAAb,CAAD,CAAvC,CACT,QADS,CACEI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,eAAb,CAAD,CADpB,CACsD,MADtD,CAETI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,YAAb,CAAD,CAFT,CAEwC,QAFxC,CAGTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,aAAb,CAAD,CAHT,CAGyC,MAHzC,CAITI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,UAAb,CAAD,CAJT,CAIsC,QAJtC,CAKTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,iBAAb,CAAD,CALT,CAK6C,MAL7C,CAMTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,cAAb,CAAD,CANT,CAM0C,QAN1C,CAOTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,oBAAb,CAAD,CAPT,CAOgD,MAPhD,CAQTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,iBAAb,CAAD,CART,CAQ6C,QAR7C,CASTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,mBAAb,CAAD,CATT,CAS+C,MAT/C,CAUTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,gBAAb,CAAD,CAVT,CAU4C,QAV5C,CAWTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,mBAAb,CAAD,CAXT,CAW+C,MAX/C,CAYTI,kBAAkB,CAAC/F,CAAO,CAAC2F,IAAR,CAAa,gBAAb,CAAD,CAZT,CAY4C,QA1BvB,CA2BlCC,CAAO,CAAG,CACN,OAAUE,CADJ,CAAV,CAGA,GAAIE,CAAAA,CAAa,CAAG9G,CAAS,CAAC2E,MAAV,CAAiB,kCAAjB,CAAqD+B,CAArD,CAApB,CACAzG,CAAY,CAAC8G,MAAb,CAAoB,CAClBC,KAAK,GADa,CAElBC,KAAK,CAAEnG,CAAO,CAAC2F,IAAR,CAAa,YAAb,CAFW,CAGlBS,IAAI,CAAEP,CAHY,CAIlBQ,MAAM,CAAEL,CAJU,CAApB,CAKGN,CALH,CAMH,CApoB4F,CAsoB7F,MAAyD,CAOrDY,IAAI,CAAE,cAAStG,CAAT,CAAkB,CACpBqE,CAAsB,CAACrE,CAAD,CAAtB,CACAyF,CAAa,CAACzF,CAAD,CAChB,CAVoD,CAY5D,CArpBM,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Controls all of the behaviour and interaction with a tool type card. These are\n * listed on the LTI tool type management page.\n *\n * See template: mod_lti/tool_card\n *\n * @module mod_lti/tool_card_controller\n * @class tool_card_controller\n * @package mod_lti\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\n define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'core/modal_factory',\n 'mod_lti/tool_type', 'mod_lti/events', 'mod_lti/keys',\n 'core/str'],\n function($, ajax, notification, templates, modalFactory, toolType, ltiEvents, KEYS, str) {\n\n var SELECTORS = {\n DELETE_BUTTON: '.delete',\n NAME_ELEMENT: '.name',\n DESCRIPTION_ELEMENT: '.description',\n CAPABILITIES_CONTAINER: '.capabilities-container',\n ACTIVATE_BUTTON: '.tool-card-footer a.activate',\n };\n\n // Timeout in seconds.\n var ANNOUNCEMENT_TIMEOUT = 2000;\n\n /**\n * Return the delete button element.\n *\n * @method getDeleteButton\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getDeleteButton = function(element) {\n return element.find(SELECTORS.DELETE_BUTTON);\n };\n\n /**\n * Return the element representing the tool type name.\n *\n * @method getNameElement\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getNameElement = function(element) {\n return element.find(SELECTORS.NAME_ELEMENT);\n };\n\n /**\n * Return the element representing the tool type description.\n *\n * @method getDescriptionElement\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {JQuery} jQuery object\n */\n var getDescriptionElement = function(element) {\n return element.find(SELECTORS.DESCRIPTION_ELEMENT);\n };\n\n /**\n * Return the activate button for the type.\n *\n * @method getActivateButton\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {Object} jQuery object\n */\n var getActivateButton = function(element) {\n return element.find(SELECTORS.ACTIVATE_BUTTON);\n };\n\n /**\n * Checks if the type card has an activate button.\n *\n * @method hasActivateButton\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Boolean} true if has active buton\n */\n var hasActivateButton = function(element) {\n return getActivateButton(element).length ? true : false;\n };\n\n /**\n * Return the element that contains the capabilities approval for\n * the user.\n *\n * @method getCapabilitiesContainer\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {Object} The element\n */\n var getCapabilitiesContainer = function(element) {\n return element.find(SELECTORS.CAPABILITIES_CONTAINER);\n };\n\n /**\n * Checks if the tool type has capabilities that need approval. If it\n * does then the container will be present.\n *\n * @method hasCapabilitiesContainer\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Boolean} true if has capbilities.\n */\n var hasCapabilitiesContainer = function(element) {\n return getCapabilitiesContainer(element).length ? true : false;\n };\n\n /**\n * Get the type id.\n *\n * @method getTypeId\n * @private\n * @param {Object} element jQuery object representing the tool card.\n * @return {String} Type ID\n */\n var getTypeId = function(element) {\n return element.attr('data-type-id');\n };\n\n /**\n * Stop any announcement currently visible on the card.\n *\n * @method clearAllAnnouncements\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var clearAllAnnouncements = function(element) {\n element.removeClass('announcement loading success fail capabilities');\n };\n\n /**\n * Show the loading announcement.\n *\n * @method startLoading\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var startLoading = function(element) {\n clearAllAnnouncements(element);\n element.addClass('announcement loading');\n };\n\n /**\n * Hide the loading announcement.\n *\n * @method stopLoading\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var stopLoading = function(element) {\n element.removeClass('announcement loading');\n };\n\n /**\n * Show the success announcement. The announcement is only\n * visible for 2 seconds.\n *\n * @method announceSuccess\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var announceSuccess = function(element) {\n var promise = $.Deferred();\n\n clearAllAnnouncements(element);\n element.addClass('announcement success');\n setTimeout(function() {\n element.removeClass('announcement success');\n promise.resolve();\n }, ANNOUNCEMENT_TIMEOUT);\n\n return promise;\n };\n\n /**\n * Show the failure announcement. The announcement is only\n * visible for 2 seconds.\n *\n * @method announceFailure\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var announceFailure = function(element) {\n var promise = $.Deferred();\n\n clearAllAnnouncements(element);\n element.addClass('announcement fail');\n setTimeout(function() {\n element.removeClass('announcement fail');\n promise.resolve();\n }, ANNOUNCEMENT_TIMEOUT);\n\n return promise;\n };\n\n /**\n * Delete the tool type from the Moodle server. Triggers a success\n * or failure announcement depending on the result.\n *\n * @method deleteType\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var deleteType = function(element) {\n var promise = $.Deferred();\n var typeId = getTypeId(element);\n startLoading(element);\n\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n str.get_strings([\n {\n key: 'delete',\n component: 'mod_lti'\n },\n {\n key: 'delete_confirmation',\n component: 'mod_lti'\n },\n {\n key: 'delete',\n component: 'mod_lti'\n },\n {\n key: 'cancel',\n component: 'core'\n },\n ])\n .done(function(strs) {\n notification.confirm(strs[0], strs[1], strs[2], strs[3], function() {\n toolType.delete(typeId)\n .done(function() {\n stopLoading(element);\n announceSuccess(element)\n .done(function() {\n element.remove();\n })\n .fail(notification.exception)\n .always(function() {\n // Always resolve because even if the announcement fails the type was deleted.\n promise.resolve();\n });\n })\n .fail(function(error) {\n announceFailure(element);\n promise.reject(error);\n });\n }, function() {\n stopLoading(element);\n promise.resolve();\n });\n })\n .fail(function(error) {\n stopLoading(element);\n notification.exception(error);\n promise.reject(error);\n });\n\n return promise;\n };\n\n /**\n * Save a given value in a data attribute on the element.\n *\n * @method setValueSnapshot\n * @private\n * @param {JQuery} element jQuery object representing the element.\n * @param {String} value to be saved.\n */\n var setValueSnapshot = function(element, value) {\n element.attr('data-val-snapshot', value);\n };\n\n /**\n * Return the saved value from the element.\n *\n * @method getValueSnapshot\n * @private\n * @param {JQuery} element jQuery object representing the element.\n * @return {String} the saved value.\n */\n var getValueSnapshot = function(element) {\n return element.attr('data-val-snapshot');\n };\n\n /**\n * Save the current value of the tool description.\n *\n * @method snapshotDescription\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var snapshotDescription = function(element) {\n var descriptionElement = getDescriptionElement(element);\n\n if (descriptionElement.hasClass('loading')) {\n return;\n }\n\n var description = descriptionElement.text().trim();\n setValueSnapshot(descriptionElement, description);\n };\n\n /**\n * Send a request to update the description value for this tool\n * in the Moodle server.\n *\n * @method updateDescription\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var updateDescription = function(element) {\n var typeId = getTypeId(element);\n\n // Return early if we don't have an id because it's\n // required to save the changes.\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n var descriptionElement = getDescriptionElement(element);\n\n // Return early if we're already saving a value.\n if (descriptionElement.hasClass('loading')) {\n return $.Deferred().resolve();\n }\n\n var description = descriptionElement.text().trim();\n var snapshotVal = getValueSnapshot(descriptionElement);\n\n // If the value hasn't change then don't bother sending the\n // update request.\n if (snapshotVal == description) {\n return $.Deferred().resolve();\n }\n\n descriptionElement.addClass('loading');\n\n var promise = toolType.update({id: typeId, description: description});\n\n promise.done(function(type) {\n descriptionElement.removeClass('loading');\n // Make sure the text is updated with the description from the\n // server, just in case the update didn't work.\n descriptionElement.text(type.description);\n }).fail(notification.exception);\n\n // Probably need to handle failures better so that we can revert\n // the value in the input for the user.\n promise.fail(function() {\n descriptionElement.removeClass('loading');\n });\n\n return promise;\n };\n\n /**\n * Save the current value of the tool name.\n *\n * @method snapshotName\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var snapshotName = function(element) {\n var nameElement = getNameElement(element);\n\n if (nameElement.hasClass('loading')) {\n return;\n }\n\n var name = nameElement.text().trim();\n setValueSnapshot(nameElement, name);\n };\n\n /**\n * Send a request to update the name value for this tool\n * in the Moodle server.\n *\n * @method updateName\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var updateName = function(element) {\n var typeId = getTypeId(element);\n\n // Return if we don't have an id.\n if (typeId === \"\") {\n return $.Deferred().resolve();\n }\n\n var nameElement = getNameElement(element);\n\n // Return if we're already saving.\n if (nameElement.hasClass('loading')) {\n return $.Deferred().resolve();\n }\n\n var name = nameElement.text().trim();\n var snapshotVal = getValueSnapshot(nameElement);\n\n // If the value hasn't change then don't bother sending the\n // update request.\n if (snapshotVal == name) {\n return $.Deferred().resolve();\n }\n\n nameElement.addClass('loading');\n var promise = toolType.update({id: typeId, name: name});\n\n promise.done(function(type) {\n nameElement.removeClass('loading');\n // Make sure the text is updated with the name from the\n // server, just in case the update didn't work.\n nameElement.text(type.name);\n });\n\n // Probably need to handle failures better so that we can revert\n // the value in the input for the user.\n promise.fail(function() {\n nameElement.removeClass('loading');\n });\n\n return promise;\n };\n\n /**\n * Send a request to update the state for this tool to be configured (active)\n * in the Moodle server. A success or failure announcement is triggered depending\n * on the result.\n *\n * @method setStatusActive\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n * @return {Promise} jQuery Deferred object\n */\n var setStatusActive = function(element) {\n var id = getTypeId(element);\n\n // Return if we don't have an id.\n if (id === \"\") {\n return $.Deferred().resolve();\n }\n\n startLoading(element);\n\n var promise = toolType.update({\n id: id,\n state: toolType.constants.state.configured\n });\n\n promise.then(function(toolTypeData) {\n stopLoading(element);\n announceSuccess(element);\n return toolTypeData;\n }).then(function(toolTypeData) {\n return templates.render('mod_lti/tool_card', toolTypeData);\n }).then(function(html, js) {\n templates.replaceNode(element, html, js);\n return;\n }).catch(function() {\n stopLoading(element);\n announceFailure(element);\n });\n\n return promise;\n };\n\n /**\n * Show the capabilities approval screen to show which groups of data this\n * type requires access to in Moodle (if any).\n *\n * @method displayCapabilitiesApproval\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var displayCapabilitiesApproval = function(element) {\n element.addClass('announcement capabilities');\n };\n\n /**\n * Hide the capabilities approval screen.\n *\n * @method hideCapabilitiesApproval\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var hideCapabilitiesApproval = function(element) {\n element.removeClass('announcement capabilities');\n };\n\n /**\n * The user wishes to activate this tool so show them the capabilities that\n * they need to agree to or if there are none then set the tool type's state\n * to active.\n *\n * @method activateToolType\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var activateToolType = function(element) {\n if (hasCapabilitiesContainer(element)) {\n displayCapabilitiesApproval(element);\n } else {\n setStatusActive(element);\n }\n };\n\n /**\n * Sets up the listeners for user interaction on this tool type card.\n *\n * @method registerEventListeners\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var registerEventListeners = function(element) {\n var deleteButton = getDeleteButton(element);\n deleteButton.click(function(e) {\n e.preventDefault();\n deleteType(element);\n });\n deleteButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n deleteButton.click();\n }\n }\n });\n\n var descriptionElement = getDescriptionElement(element);\n descriptionElement.focus(function(e) {\n e.preventDefault();\n // Save a copy of the current value for the description so that\n // we can check if the user has changed it before sending a request to\n // the server.\n snapshotDescription(element);\n });\n descriptionElement.blur(function(e) {\n e.preventDefault();\n updateDescription(element);\n });\n descriptionElement.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER) {\n e.preventDefault();\n descriptionElement.blur();\n }\n }\n });\n\n var nameElement = getNameElement(element);\n nameElement.focus(function(e) {\n e.preventDefault();\n // Save a copy of the current value for the name so that\n // we can check if the user has changed it before sending a request to\n // the server.\n snapshotName(element);\n });\n nameElement.blur(function(e) {\n e.preventDefault();\n updateName(element);\n });\n nameElement.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER) {\n e.preventDefault();\n nameElement.blur();\n }\n }\n });\n\n // Only pending tool type cards have an activate button.\n if (hasActivateButton(element)) {\n var activateButton = getActivateButton(element);\n activateButton.click(function(e) {\n e.preventDefault();\n activateToolType(element);\n });\n activateButton.keypress(function(e) {\n if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {\n if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {\n e.preventDefault();\n activateButton.click();\n }\n }\n });\n }\n\n if (hasCapabilitiesContainer(element)) {\n var capabilitiesContainer = getCapabilitiesContainer(element);\n\n capabilitiesContainer.on(ltiEvents.CAPABILITIES_AGREE, function() {\n setStatusActive(element);\n });\n\n capabilitiesContainer.on(ltiEvents.CAPABILITIES_DECLINE, function() {\n hideCapabilitiesApproval(element);\n });\n }\n };\n\n /**\n * Sets up the templates for the tool configuration modal on this tool type card.\n *\n * @method registerModal\n * @private\n * @param {JQuery} element jQuery object representing the tool card.\n */\n var registerModal = function(element) {\n var trigger = $('#' + element.data('uniqid') + '-' + element.data('deploymentid'));\n var context = {\n 'uniqid': element.data('uniqid'),\n 'platformid': element.data('platformid'),\n 'clientid': element.data('clientid'),\n 'deploymentid': element.data('deploymentid'),\n 'urls': {\n 'publickeyset': element.data('publickeyseturl'),\n 'accesstoken': element.data('accesstokenurl'),\n 'authrequest': element.data('authrequesturl')\n }\n };\n var bodyPromise = templates.render('mod_lti/tool_config_modal_body', context);\n var mailTo = 'mailto:?subject=' + encodeURIComponent(element.data('mailtosubject')) +\n '&body=' + encodeURIComponent(element.data('platformidstr')) + ':%20' +\n encodeURIComponent(element.data('platformid')) + '%0D%0A' +\n encodeURIComponent(element.data('clientidstr')) + ':%20' +\n encodeURIComponent(element.data('clientid')) + '%0D%0A' +\n encodeURIComponent(element.data('deploymentidstr')) + ':%20' +\n encodeURIComponent(element.data('deploymentid')) + '%0D%0A' +\n encodeURIComponent(element.data('publickeyseturlstr')) + ':%20' +\n encodeURIComponent(element.data('publickeyseturl')) + '%0D%0A' +\n encodeURIComponent(element.data('accesstokenurlstr')) + ':%20' +\n encodeURIComponent(element.data('accesstokenurl')) + '%0D%0A' +\n encodeURIComponent(element.data('authrequesturlstr')) + ':%20' +\n encodeURIComponent(element.data('authrequesturl')) + '%0D%0A';\n context = {\n 'mailto': mailTo\n };\n var footerPromise = templates.render('mod_lti/tool_config_modal_footer', context);\n modalFactory.create({\n large: true,\n title: element.data('modaltitle'),\n body: bodyPromise,\n footer: footerPromise,\n }, trigger);\n };\n\n return /** @alias module:mod_lti/tool_card_controller */ {\n\n /**\n * Initialise this module.\n *\n * @param {JQuery} element jQuery object representing the tool card.\n */\n init: function(element) {\n registerEventListeners(element);\n registerModal(element);\n }\n };\n});\n"],"file":"tool_card_controller.min.js"} \ No newline at end of file diff --git a/mod/lti/amd/build/tool_configure_controller.min.js b/mod/lti/amd/build/tool_configure_controller.min.js index 4fccd1bc1da..94998c34f15 100644 --- a/mod/lti/amd/build/tool_configure_controller.min.js +++ b/mod/lti/amd/build/tool_configure_controller.min.js @@ -1,2 +1,2 @@ -define ("mod_lti/tool_configure_controller",["jquery","core/ajax","core/notification","core/templates","mod_lti/events","mod_lti/keys","mod_lti/tool_type","mod_lti/tool_proxy","core/str"],function(a,b,c,d,e,f,g,h,i){var j={EXTERNAL_REGISTRATION_CONTAINER:"#external-registration-container",EXTERNAL_REGISTRATION_PAGE_CONTAINER:"#external-registration-page-container",CARTRIDGE_REGISTRATION_CONTAINER:"#cartridge-registration-container",CARTRIDGE_REGISTRATION_FORM:"#cartridge-registration-form",ADD_TOOL_FORM:"#add-tool-form",TOOL_LIST_CONTAINER:"#tool-list-container",TOOL_CREATE_BUTTON:"#tool-create-button",REGISTRATION_CHOICE_CONTAINER:"#registration-choice-container",TOOL_URL:"#tool-url"},k=function(){return a(j.TOOL_CREATE_BUTTON)},l=function(){return a(j.TOOL_LIST_CONTAINER)},m=function(){return a(j.EXTERNAL_REGISTRATION_CONTAINER)},n=function(){return a(j.CARTRIDGE_REGISTRATION_CONTAINER)},o=function(){return a(j.REGISTRATION_CHOICE_CONTAINER)},p=function(){return a(j.TOOL_URL).val()},q=function(){m().addClass("hidden")},r=function(){n().addClass("hidden")},s=function(){o().addClass("hidden")},t=function(){r();s();m().removeClass("hidden");w(m())},u=function(a){q();s();var b=n();b.find("input").val("");b.removeClass("hidden");b.find(j.CARTRIDGE_REGISTRATION_FORM).attr("data-cartridge-url",a);w(b)},v=function(){q();r();o().removeClass("hidden");w(o())},w=function(a){var b=a.children().detach();b.appendTo(a)},x=function(){l().addClass("hidden")},y=function(){l().removeClass("hidden")},z=function(a){var b=a.error?"error":"success";c.addNotification({message:a.message,type:b})},A=function(a){a.addClass("loading")},B=function(a){a.removeClass("loading")},C=function(){var b=a.Deferred(),e=l();A(e);a.when(g.query(),h.query({orphanedonly:!0})).done(function(a,c){d.render("mod_lti/tool_list",{tools:a,proxies:c}).done(function(a,c){e.empty();e.append(a);d.runTemplateJS(c);b.resolve()}).fail(b.reject)}).fail(b.reject);b.fail(c.exception).always(function(){B(e)})},D=function(){var b=a.trim(p());if(""===b){return a.Deferred().resolve()}var d=k();A(d);var f=g.isCartridge(b);f.always(function(){B(d)});f.done(function(c){if(c.iscartridge){a(j.TOOL_URL).val("");a(document).trigger(e.START_CARTRIDGE_REGISTRATION,b)}else{a(document).trigger(e.START_EXTERNAL_REGISTRATION,{url:b})}});f.fail(function(){i.get_string("errorbadurl","mod_lti").done(function(b){a(document).trigger(e.REGISTRATION_FEEDBACK,{message:b,error:!0})}).fail(c.exception)});return f},E=function(){a(document).on(e.NEW_TOOL_TYPE,function(){C()});a(document).on(e.START_EXTERNAL_REGISTRATION,function(){t();a(j.TOOL_URL).val("");x()});a(document).on(e.STOP_EXTERNAL_REGISTRATION,function(){y();v()});a(document).on(e.START_CARTRIDGE_REGISTRATION,function(a,b){u(b)});a(document).on(e.STOP_CARTRIDGE_REGISTRATION,function(){n().find(j.CARTRIDGE_REGISTRATION_FORM).removeAttr("data-cartridge-url");v()});a(document).on(e.REGISTRATION_FEEDBACK,function(a,b){z(b)});var b=a(j.ADD_TOOL_FORM);b.submit(function(a){a.preventDefault();D()})};return{init:function init(){E();C()}}}); +define ("mod_lti/tool_configure_controller",["jquery","core/ajax","core/notification","core/templates","mod_lti/events","mod_lti/keys","mod_lti/tool_type","mod_lti/tool_proxy","core/str"],function(a,b,c,d,e,f,g,h,i){var j={EXTERNAL_REGISTRATION_CONTAINER:"#external-registration-container",EXTERNAL_REGISTRATION_PAGE_CONTAINER:"#external-registration-page-container",EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER:"#external-registration-template-container",CARTRIDGE_REGISTRATION_CONTAINER:"#cartridge-registration-container",CARTRIDGE_REGISTRATION_FORM:"#cartridge-registration-form",ADD_TOOL_FORM:"#add-tool-form",TOOL_LIST_CONTAINER:"#tool-list-container",TOOL_CREATE_BUTTON:"#tool-create-button",TOOL_CREATE_LTILEGACY_BUTTON:"#tool-createltilegacy-button",REGISTRATION_CHOICE_CONTAINER:"#registration-choice-container",TOOL_URL:"#tool-url"},k=function(){return a(j.TOOL_LIST_CONTAINER)},l=function(){return a(j.EXTERNAL_REGISTRATION_CONTAINER)},m=function(){return a(j.CARTRIDGE_REGISTRATION_CONTAINER)},n=function(){return a(j.REGISTRATION_CHOICE_CONTAINER)},o=function(b){if(b.data&&"org.imsglobal.lti.close"===b.data.subject){a(j.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER).empty();r();w();z();w();D()}},p=function(b){a(j.EXTERNAL_REGISTRATION_PAGE_CONTAINER).removeClass("hidden");var c=a(j.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER);c.append(a(""));u();window.addEventListener("message",o,!1)},q=function(){return a(j.TOOL_URL).val()},r=function(){l().addClass("hidden")},s=function(){m().addClass("hidden")},t=function(){n().addClass("hidden")},u=function(){s();t();l().removeClass("hidden");x(l())},v=function(a){r();t();var b=m();b.find("input").val("");b.removeClass("hidden");b.find(j.CARTRIDGE_REGISTRATION_FORM).attr("data-cartridge-url",a);x(b)},w=function(){r();s();n().removeClass("hidden");x(n())},x=function(a){var b=a.children().detach();b.appendTo(a)},y=function(){k().addClass("hidden")},z=function(){k().removeClass("hidden")},A=function(a){var b=a.error?"error":"success";c.addNotification({message:a.message,type:b})},B=function(a){a.addClass("loading")},C=function(a){a.removeClass("loading")},D=function(){var b=a.Deferred(),e=k();B(e);a.when(g.query(),h.query({orphanedonly:!0})).done(function(a,c){d.render("mod_lti/tool_list",{tools:a,proxies:c}).done(function(a,c){e.empty();e.append(a);d.runTemplateJS(c);b.resolve()}).fail(b.reject)}).fail(b.reject);b.fail(c.exception).always(function(){C(e)})},E=function(){var b=a.trim(q());if(b){a(j.TOOL_URL).val("");y();p(b)}},F=function(){var b=a.trim(q());if(""===b){return a.Deferred().resolve()}var d=a(j.TOOL_CREATE_LTILEGACY_BUTTON);B(d);var f=g.isCartridge(b);f.always(function(){C(d)});f.done(function(c){if(c.iscartridge){a(j.TOOL_URL).val("");a(document).trigger(e.START_CARTRIDGE_REGISTRATION,b)}else{a(document).trigger(e.START_EXTERNAL_REGISTRATION,{url:b})}});f.fail(function(){i.get_string("errorbadurl","mod_lti").done(function(b){a(document).trigger(e.REGISTRATION_FEEDBACK,{message:b,error:!0})}).fail(c.exception)});return f},G=function(){a(document).on(e.NEW_TOOL_TYPE,function(){D()});a(document).on(e.START_EXTERNAL_REGISTRATION,function(){u();a(j.TOOL_URL).val("");y()});a(document).on(e.STOP_EXTERNAL_REGISTRATION,function(){z();w()});a(document).on(e.START_CARTRIDGE_REGISTRATION,function(a,b){v(b)});a(document).on(e.STOP_CARTRIDGE_REGISTRATION,function(){m().find(j.CARTRIDGE_REGISTRATION_FORM).removeAttr("data-cartridge-url");w()});a(document).on(e.REGISTRATION_FEEDBACK,function(a,b){A(b)});var b=a(j.TOOL_CREATE_LTILEGACY_BUTTON);b.click(function(a){a.preventDefault();F()});var c=a(j.TOOL_CREATE_BUTTON);c.click(function(a){a.preventDefault();E()})};return{init:function init(){G();D()}}}); //# sourceMappingURL=tool_configure_controller.min.js.map diff --git a/mod/lti/amd/build/tool_configure_controller.min.js.map b/mod/lti/amd/build/tool_configure_controller.min.js.map index 5191052bf00..df0e78cb2d8 100644 --- a/mod/lti/amd/build/tool_configure_controller.min.js.map +++ b/mod/lti/amd/build/tool_configure_controller.min.js.map @@ -1 +1 @@ -{"version":3,"sources":["../src/tool_configure_controller.js"],"names":["define","$","ajax","notification","templates","ltiEvents","KEYS","toolType","toolProxy","str","SELECTORS","EXTERNAL_REGISTRATION_CONTAINER","EXTERNAL_REGISTRATION_PAGE_CONTAINER","CARTRIDGE_REGISTRATION_CONTAINER","CARTRIDGE_REGISTRATION_FORM","ADD_TOOL_FORM","TOOL_LIST_CONTAINER","TOOL_CREATE_BUTTON","REGISTRATION_CHOICE_CONTAINER","TOOL_URL","getToolCreateButton","getToolListContainer","getExternalRegistrationContainer","getCartridgeRegistrationContainer","getRegistrationChoiceContainer","getToolURL","val","hideExternalRegistration","addClass","hideCartridgeRegistration","hideRegistrationChoices","showExternalRegistration","removeClass","screenReaderAnnounce","showCartridgeRegistration","url","container","find","attr","showRegistrationChoices","element","children","detach","appendTo","hideToolList","showToolList","showRegistrationFeedback","data","type","error","addNotification","message","startLoading","stopLoading","reloadToolList","promise","Deferred","when","query","done","types","proxies","render","tools","html","js","empty","append","runTemplateJS","resolve","fail","reject","exception","always","addTool","trim","toolButton","isCartridge","result","iscartridge","document","trigger","START_CARTRIDGE_REGISTRATION","START_EXTERNAL_REGISTRATION","get_string","s","REGISTRATION_FEEDBACK","registerEventListeners","on","NEW_TOOL_TYPE","STOP_EXTERNAL_REGISTRATION","event","STOP_CARTRIDGE_REGISTRATION","removeAttr","form","submit","e","preventDefault","init"],"mappings":"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","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Standard Ajax wrapper for Moodle. It calls the central Ajax script,\n * which can call any existing webservice using the current session.\n * In addition, it can batch multiple requests and return multiple responses.\n *\n * @module mod_lti/tool_configure_controller\n * @class tool_configure_controller\n * @package mod_lti\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/events', 'mod_lti/keys', 'mod_lti/tool_type',\n 'mod_lti/tool_proxy', 'core/str'],\n function($, ajax, notification, templates, ltiEvents, KEYS, toolType, toolProxy, str) {\n\n var SELECTORS = {\n EXTERNAL_REGISTRATION_CONTAINER: '#external-registration-container',\n EXTERNAL_REGISTRATION_PAGE_CONTAINER: '#external-registration-page-container',\n CARTRIDGE_REGISTRATION_CONTAINER: '#cartridge-registration-container',\n CARTRIDGE_REGISTRATION_FORM: '#cartridge-registration-form',\n ADD_TOOL_FORM: '#add-tool-form',\n TOOL_LIST_CONTAINER: '#tool-list-container',\n TOOL_CREATE_BUTTON: '#tool-create-button',\n REGISTRATION_CHOICE_CONTAINER: '#registration-choice-container',\n TOOL_URL: '#tool-url'\n };\n\n /**\n * Get the tool create button element.\n *\n * @method getToolCreateButton\n * @private\n * @return {Object} jQuery object\n */\n var getToolCreateButton = function() {\n return $(SELECTORS.TOOL_CREATE_BUTTON);\n };\n\n /**\n * Get the tool list container element.\n *\n * @method getToolListContainer\n * @private\n * @return {Object} jQuery object\n */\n var getToolListContainer = function() {\n return $(SELECTORS.TOOL_LIST_CONTAINER);\n };\n\n /**\n * Get the external registration container element.\n *\n * @method getExternalRegistrationContainer\n * @private\n * @return {Object} jQuery object\n */\n var getExternalRegistrationContainer = function() {\n return $(SELECTORS.EXTERNAL_REGISTRATION_CONTAINER);\n };\n\n /**\n * Get the cartridge registration container element.\n *\n * @method getCartridgeRegistrationContainer\n * @private\n * @return {Object} jQuery object\n */\n var getCartridgeRegistrationContainer = function() {\n return $(SELECTORS.CARTRIDGE_REGISTRATION_CONTAINER);\n };\n\n /**\n * Get the registration choice container element.\n *\n * @method getRegistrationChoiceContainer\n * @private\n * @return {Object} jQuery object\n */\n var getRegistrationChoiceContainer = function() {\n return $(SELECTORS.REGISTRATION_CHOICE_CONTAINER);\n };\n\n /**\n * Get the tool type URL.\n *\n * @method getToolURL\n * @private\n * @return {String} the tool type url\n */\n var getToolURL = function() {\n return $(SELECTORS.TOOL_URL).val();\n };\n\n /**\n * Hide the external registration container.\n *\n * @method hideExternalRegistration\n * @private\n */\n var hideExternalRegistration = function() {\n getExternalRegistrationContainer().addClass('hidden');\n };\n\n /**\n * Hide the cartridge registration container.\n *\n * @method hideCartridgeRegistration\n * @private\n */\n var hideCartridgeRegistration = function() {\n getCartridgeRegistrationContainer().addClass('hidden');\n };\n\n /**\n * Hide the registration choice container.\n *\n * @method hideRegistrationChoices\n * @private\n */\n var hideRegistrationChoices = function() {\n getRegistrationChoiceContainer().addClass('hidden');\n };\n\n /**\n * Display the external registration panel and hides the other\n * panels.\n *\n * @method showExternalRegistration\n * @private\n */\n var showExternalRegistration = function() {\n hideCartridgeRegistration();\n hideRegistrationChoices();\n getExternalRegistrationContainer().removeClass('hidden');\n screenReaderAnnounce(getExternalRegistrationContainer());\n };\n\n /**\n * Display the cartridge registration panel and hides the other\n * panels.\n *\n * @method showCartridgeRegistration\n * @param {String} url\n * @private\n */\n var showCartridgeRegistration = function(url) {\n hideExternalRegistration();\n hideRegistrationChoices();\n // Don't save the key and secret from the last tool.\n var container = getCartridgeRegistrationContainer();\n container.find('input').val('');\n container.removeClass('hidden');\n container.find(SELECTORS.CARTRIDGE_REGISTRATION_FORM).attr('data-cartridge-url', url);\n screenReaderAnnounce(container);\n };\n\n /**\n * Display the registration choices panel and hides the other\n * panels.\n *\n * @method showRegistrationChoices\n * @private\n */\n var showRegistrationChoices = function() {\n hideExternalRegistration();\n hideCartridgeRegistration();\n getRegistrationChoiceContainer().removeClass('hidden');\n screenReaderAnnounce(getRegistrationChoiceContainer());\n };\n\n /**\n * JAWS does not notice visibility changes with aria-live.\n * Remove and add the content back to force it to read it out.\n * This function can be removed once JAWS supports visibility.\n *\n * @method screenReaderAnnounce\n * @param {Object} element\n * @private\n */\n var screenReaderAnnounce = function(element) {\n var children = element.children().detach();\n children.appendTo(element);\n };\n\n /**\n * Hides the list of tool types.\n *\n * @method hideToolList\n * @private\n */\n var hideToolList = function() {\n getToolListContainer().addClass('hidden');\n };\n\n /**\n * Display the list of tool types.\n *\n * @method hideToolList\n * @private\n */\n var showToolList = function() {\n getToolListContainer().removeClass('hidden');\n };\n\n /**\n * Display the registration feedback alert and hide the other panels.\n *\n * @method showRegistrationFeedback\n * @param {Object} data\n * @private\n */\n var showRegistrationFeedback = function(data) {\n var type = data.error ? 'error' : 'success';\n notification.addNotification({\n message: data.message,\n type: type\n });\n };\n\n /**\n * Show the loading animation\n *\n * @method startLoading\n * @private\n * @param {Object} element jQuery object\n */\n var startLoading = function(element) {\n element.addClass(\"loading\");\n };\n\n /**\n * Hide the loading animation\n *\n * @method stopLoading\n * @private\n * @param {Object} element jQuery object\n */\n var stopLoading = function(element) {\n element.removeClass(\"loading\");\n };\n\n /**\n * Refresh the list of tool types and render the new ones.\n *\n * @method reloadToolList\n * @private\n */\n var reloadToolList = function() {\n var promise = $.Deferred();\n var container = getToolListContainer();\n startLoading(container);\n\n $.when(\n toolType.query(),\n toolProxy.query({'orphanedonly': true})\n )\n .done(function(types, proxies) {\n templates.render('mod_lti/tool_list', {tools: types, proxies: proxies})\n .done(function(html, js) {\n container.empty();\n container.append(html);\n templates.runTemplateJS(js);\n promise.resolve();\n }).fail(promise.reject);\n })\n .fail(promise.reject);\n\n promise.fail(notification.exception)\n .always(function() {\n stopLoading(container);\n });\n };\n\n /**\n * Trigger appropriate registration process process for the user input\n * URL. It can either be a cartridge or a registration url.\n *\n * @method addTool\n * @private\n * @return {Promise} jQuery Deferred object\n */\n var addTool = function() {\n var url = $.trim(getToolURL());\n\n if (url === \"\") {\n return $.Deferred().resolve();\n }\n\n var toolButton = getToolCreateButton();\n startLoading(toolButton);\n\n var promise = toolType.isCartridge(url);\n\n promise.always(function() {\n stopLoading(toolButton);\n });\n\n promise.done(function(result) {\n if (result.iscartridge) {\n $(SELECTORS.TOOL_URL).val('');\n $(document).trigger(ltiEvents.START_CARTRIDGE_REGISTRATION, url);\n } else {\n $(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION, {url: url});\n }\n });\n\n promise.fail(function() {\n str.get_string('errorbadurl', 'mod_lti')\n .done(function(s) {\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, {\n message: s,\n error: true\n });\n })\n .fail(notification.exception);\n });\n\n return promise;\n };\n\n /**\n * Sets up the listeners for user interaction on the page.\n *\n * @method registerEventListeners\n * @private\n */\n var registerEventListeners = function() {\n\n // These are events fired by the registration processes. Either\n // the cartridge registration or the external registration url.\n $(document).on(ltiEvents.NEW_TOOL_TYPE, function() {\n reloadToolList();\n });\n\n $(document).on(ltiEvents.START_EXTERNAL_REGISTRATION, function() {\n showExternalRegistration();\n $(SELECTORS.TOOL_URL).val('');\n hideToolList();\n });\n\n $(document).on(ltiEvents.STOP_EXTERNAL_REGISTRATION, function() {\n showToolList();\n showRegistrationChoices();\n });\n\n $(document).on(ltiEvents.START_CARTRIDGE_REGISTRATION, function(event, url) {\n showCartridgeRegistration(url);\n });\n\n $(document).on(ltiEvents.STOP_CARTRIDGE_REGISTRATION, function() {\n getCartridgeRegistrationContainer().find(SELECTORS.CARTRIDGE_REGISTRATION_FORM).removeAttr('data-cartridge-url');\n showRegistrationChoices();\n });\n\n $(document).on(ltiEvents.REGISTRATION_FEEDBACK, function(event, data) {\n showRegistrationFeedback(data);\n });\n\n var form = $(SELECTORS.ADD_TOOL_FORM);\n form.submit(function(e) {\n e.preventDefault();\n addTool();\n });\n\n };\n\n return /** @alias module:mod_lti/cartridge_registration_form */ {\n\n /**\n * Initialise this module.\n */\n init: function() {\n registerEventListeners();\n reloadToolList();\n }\n };\n});\n"],"file":"tool_configure_controller.min.js"} \ No newline at end of file +{"version":3,"sources":["../src/tool_configure_controller.js"],"names":["define","$","ajax","notification","templates","ltiEvents","KEYS","toolType","toolProxy","str","SELECTORS","EXTERNAL_REGISTRATION_CONTAINER","EXTERNAL_REGISTRATION_PAGE_CONTAINER","EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER","CARTRIDGE_REGISTRATION_CONTAINER","CARTRIDGE_REGISTRATION_FORM","ADD_TOOL_FORM","TOOL_LIST_CONTAINER","TOOL_CREATE_BUTTON","TOOL_CREATE_LTILEGACY_BUTTON","REGISTRATION_CHOICE_CONTAINER","TOOL_URL","getToolListContainer","getExternalRegistrationContainer","getCartridgeRegistrationContainer","getRegistrationChoiceContainer","closeLTIAdvRegistration","e","data","subject","empty","hideExternalRegistration","showRegistrationChoices","showToolList","reloadToolList","initiateRegistration","url","removeClass","container","append","encodeURIComponent","showExternalRegistration","window","addEventListener","getToolURL","val","addClass","hideCartridgeRegistration","hideRegistrationChoices","screenReaderAnnounce","showCartridgeRegistration","find","attr","element","children","detach","appendTo","hideToolList","showRegistrationFeedback","type","error","addNotification","message","startLoading","stopLoading","promise","Deferred","when","query","done","types","proxies","render","tools","html","js","runTemplateJS","resolve","fail","reject","exception","always","addLTIAdvTool","trim","addLTILegacyTool","toolButton","isCartridge","result","iscartridge","document","trigger","START_CARTRIDGE_REGISTRATION","START_EXTERNAL_REGISTRATION","get_string","s","REGISTRATION_FEEDBACK","registerEventListeners","on","NEW_TOOL_TYPE","STOP_EXTERNAL_REGISTRATION","event","STOP_CARTRIDGE_REGISTRATION","removeAttr","addLegacyButton","click","preventDefault","addLTIButton","init"],"mappings":"AA2BAA,OAAM,qCAAC,CAAC,QAAD,CAAW,WAAX,CAAwB,mBAAxB,CAA6C,gBAA7C,CAA+D,gBAA/D,CAAiF,cAAjF,CAAiG,mBAAjG,CACC,oBADD,CACuB,UADvB,CAAD,CAEE,SAASC,CAAT,CAAYC,CAAZ,CAAkBC,CAAlB,CAAgCC,CAAhC,CAA2CC,CAA3C,CAAsDC,CAAtD,CAA4DC,CAA5D,CAAsEC,CAAtE,CAAiFC,CAAjF,CAAsF,IAEtFC,CAAAA,CAAS,CAAG,CACZC,+BAA+B,CAAE,kCADrB,CAEZC,oCAAoC,CAAE,uCAF1B,CAGZC,wCAAwC,CAAE,2CAH9B,CAIZC,gCAAgC,CAAE,mCAJtB,CAKZC,2BAA2B,CAAE,8BALjB,CAMZC,aAAa,CAAE,gBANH,CAOZC,mBAAmB,CAAE,sBAPT,CAQZC,kBAAkB,CAAE,qBARR,CASZC,4BAA4B,CAAE,8BATlB,CAUZC,6BAA6B,CAAE,gCAVnB,CAWZC,QAAQ,CAAE,WAXE,CAF0E,CAuBtFC,CAAoB,CAAG,UAAW,CAClC,MAAOrB,CAAAA,CAAC,CAACS,CAAS,CAACO,mBAAX,CACX,CAzByF,CAkCtFM,CAAgC,CAAG,UAAW,CAC9C,MAAOtB,CAAAA,CAAC,CAACS,CAAS,CAACC,+BAAX,CACX,CApCyF,CA6CtFa,CAAiC,CAAG,UAAW,CAC/C,MAAOvB,CAAAA,CAAC,CAACS,CAAS,CAACI,gCAAX,CACX,CA/CyF,CAwDtFW,CAA8B,CAAG,UAAW,CAC5C,MAAOxB,CAAAA,CAAC,CAACS,CAAS,CAACU,6BAAX,CACX,CA1DyF,CAkEtFM,CAAuB,CAAG,SAASC,CAAT,CAAY,CACtC,GAAIA,CAAC,CAACC,IAAF,EAAU,4BAA8BD,CAAC,CAACC,IAAF,CAAOC,OAAnD,CAA4D,CACxD5B,CAAC,CAACS,CAAS,CAACG,wCAAX,CAAD,CAAsDiB,KAAtD,GACAC,CAAwB,GACxBC,CAAuB,GACvBC,CAAY,GACZD,CAAuB,GACvBE,CAAc,EACjB,CACJ,CA3EyF,CAoFtFC,CAAoB,CAAG,SAASC,CAAT,CAAc,CAErCnC,CAAC,CAACS,CAAS,CAACE,oCAAX,CAAD,CAAkDyB,WAAlD,CAA8D,QAA9D,EACA,GAAIC,CAAAA,CAAS,CAAGrC,CAAC,CAACS,CAAS,CAACG,wCAAX,CAAjB,CACAyB,CAAS,CAACC,MAAV,CAAiBtC,CAAC,CAAC,gDACAuC,kBAAkB,CAACJ,CAAD,CADlB,CAC0B,aAD3B,CAAlB,EAEAK,CAAwB,GACxBC,MAAM,CAACC,gBAAP,CAAwB,SAAxB,CAAmCjB,CAAnC,IACH,CA5FyF,CAqGtFkB,CAAU,CAAG,UAAW,CACxB,MAAO3C,CAAAA,CAAC,CAACS,CAAS,CAACW,QAAX,CAAD,CAAsBwB,GAAtB,EACV,CAvGyF,CA+GtFd,CAAwB,CAAG,UAAW,CACtCR,CAAgC,GAAGuB,QAAnC,CAA4C,QAA5C,CACH,CAjHyF,CAyHtFC,CAAyB,CAAG,UAAW,CACvCvB,CAAiC,GAAGsB,QAApC,CAA6C,QAA7C,CACH,CA3HyF,CAmItFE,CAAuB,CAAG,UAAW,CACrCvB,CAA8B,GAAGqB,QAAjC,CAA0C,QAA1C,CACH,CArIyF,CA8ItFL,CAAwB,CAAG,UAAW,CACtCM,CAAyB,GACzBC,CAAuB,GACvBzB,CAAgC,GAAGc,WAAnC,CAA+C,QAA/C,EACAY,CAAoB,CAAC1B,CAAgC,EAAjC,CACvB,CAnJyF,CA6JtF2B,CAAyB,CAAG,SAASd,CAAT,CAAc,CAC1CL,CAAwB,GACxBiB,CAAuB,GAEvB,GAAIV,CAAAA,CAAS,CAAGd,CAAiC,EAAjD,CACAc,CAAS,CAACa,IAAV,CAAe,OAAf,EAAwBN,GAAxB,CAA4B,EAA5B,EACAP,CAAS,CAACD,WAAV,CAAsB,QAAtB,EACAC,CAAS,CAACa,IAAV,CAAezC,CAAS,CAACK,2BAAzB,EAAsDqC,IAAtD,CAA2D,oBAA3D,CAAiFhB,CAAjF,EACAa,CAAoB,CAACX,CAAD,CACvB,CAtKyF,CA+KtFN,CAAuB,CAAG,UAAW,CACrCD,CAAwB,GACxBgB,CAAyB,GACzBtB,CAA8B,GAAGY,WAAjC,CAA6C,QAA7C,EACAY,CAAoB,CAACxB,CAA8B,EAA/B,CACvB,CApLyF,CA+LtFwB,CAAoB,CAAG,SAASI,CAAT,CAAkB,CACzC,GAAIC,CAAAA,CAAQ,CAAGD,CAAO,CAACC,QAAR,GAAmBC,MAAnB,EAAf,CACAD,CAAQ,CAACE,QAAT,CAAkBH,CAAlB,CACH,CAlMyF,CA0MtFI,CAAY,CAAG,UAAW,CAC1BnC,CAAoB,GAAGwB,QAAvB,CAAgC,QAAhC,CACH,CA5MyF,CAoNtFb,CAAY,CAAG,UAAW,CAC1BX,CAAoB,GAAGe,WAAvB,CAAmC,QAAnC,CACH,CAtNyF,CA+NtFqB,CAAwB,CAAG,SAAS9B,CAAT,CAAe,CAC1C,GAAI+B,CAAAA,CAAI,CAAG/B,CAAI,CAACgC,KAAL,CAAa,OAAb,CAAuB,SAAlC,CACAzD,CAAY,CAAC0D,eAAb,CAA6B,CACzBC,OAAO,CAAElC,CAAI,CAACkC,OADW,CAEzBH,IAAI,CAAEA,CAFmB,CAA7B,CAIH,CArOyF,CA8OtFI,CAAY,CAAG,SAASV,CAAT,CAAkB,CACjCA,CAAO,CAACP,QAAR,CAAiB,SAAjB,CACH,CAhPyF,CAyPtFkB,CAAW,CAAG,SAASX,CAAT,CAAkB,CAChCA,CAAO,CAAChB,WAAR,CAAoB,SAApB,CACH,CA3PyF,CAmQtFH,CAAc,CAAG,UAAW,IACxB+B,CAAAA,CAAO,CAAGhE,CAAC,CAACiE,QAAF,EADc,CAExB5B,CAAS,CAAGhB,CAAoB,EAFR,CAG5ByC,CAAY,CAACzB,CAAD,CAAZ,CAEArC,CAAC,CAACkE,IAAF,CACQ5D,CAAQ,CAAC6D,KAAT,EADR,CAEQ5D,CAAS,CAAC4D,KAAV,CAAgB,CAAC,eAAD,CAAhB,CAFR,EAIKC,IAJL,CAIU,SAASC,CAAT,CAAgBC,CAAhB,CAAyB,CACvBnE,CAAS,CAACoE,MAAV,CAAiB,mBAAjB,CAAsC,CAACC,KAAK,CAAEH,CAAR,CAAeC,OAAO,CAAEA,CAAxB,CAAtC,EACKF,IADL,CACU,SAASK,CAAT,CAAeC,CAAf,CAAmB,CACjBrC,CAAS,CAACR,KAAV,GACAQ,CAAS,CAACC,MAAV,CAAiBmC,CAAjB,EACAtE,CAAS,CAACwE,aAAV,CAAwBD,CAAxB,EACAV,CAAO,CAACY,OAAR,EACH,CANT,EAMWC,IANX,CAMgBb,CAAO,CAACc,MANxB,CAOH,CAZT,EAaKD,IAbL,CAaUb,CAAO,CAACc,MAblB,EAeAd,CAAO,CAACa,IAAR,CAAa3E,CAAY,CAAC6E,SAA1B,EACKC,MADL,CACY,UAAW,CACXjB,CAAW,CAAC1B,CAAD,CACd,CAHT,CAIH,CA3RyF,CAmStF4C,CAAa,CAAG,UAAW,CAC3B,GAAI9C,CAAAA,CAAG,CAAGnC,CAAC,CAACkF,IAAF,CAAOvC,CAAU,EAAjB,CAAV,CAEA,GAAIR,CAAJ,CAAS,CACLnC,CAAC,CAACS,CAAS,CAACW,QAAX,CAAD,CAAsBwB,GAAtB,CAA0B,EAA1B,EACAY,CAAY,GACZtB,CAAoB,CAACC,CAAD,CACvB,CAEJ,CA5SyF,CAsTtFgD,CAAgB,CAAG,UAAW,CAC9B,GAAIhD,CAAAA,CAAG,CAAGnC,CAAC,CAACkF,IAAF,CAAOvC,CAAU,EAAjB,CAAV,CAEA,GAAY,EAAR,GAAAR,CAAJ,CAAgB,CACZ,MAAOnC,CAAAA,CAAC,CAACiE,QAAF,GAAaW,OAAb,EACV,CACD,GAAIQ,CAAAA,CAAU,CAAGpF,CAAC,CAACS,CAAS,CAACS,4BAAX,CAAlB,CACA4C,CAAY,CAACsB,CAAD,CAAZ,CAEA,GAAIpB,CAAAA,CAAO,CAAG1D,CAAQ,CAAC+E,WAAT,CAAqBlD,CAArB,CAAd,CAEA6B,CAAO,CAACgB,MAAR,CAAe,UAAW,CACxBjB,CAAW,CAACqB,CAAD,CACZ,CAFD,EAIApB,CAAO,CAACI,IAAR,CAAa,SAASkB,CAAT,CAAiB,CAC1B,GAAIA,CAAM,CAACC,WAAX,CAAwB,CACpBvF,CAAC,CAACS,CAAS,CAACW,QAAX,CAAD,CAAsBwB,GAAtB,CAA0B,EAA1B,EACA5C,CAAC,CAACwF,QAAD,CAAD,CAAYC,OAAZ,CAAoBrF,CAAS,CAACsF,4BAA9B,CAA4DvD,CAA5D,CACH,CAHD,IAGO,CACHnC,CAAC,CAACwF,QAAD,CAAD,CAAYC,OAAZ,CAAoBrF,CAAS,CAACuF,2BAA9B,CAA2D,CAACxD,GAAG,CAAEA,CAAN,CAA3D,CACH,CACJ,CAPD,EASA6B,CAAO,CAACa,IAAR,CAAa,UAAW,CACpBrE,CAAG,CAACoF,UAAJ,CAAe,aAAf,CAA8B,SAA9B,EACKxB,IADL,CACU,SAASyB,CAAT,CAAY,CACV7F,CAAC,CAACwF,QAAD,CAAD,CAAYC,OAAZ,CAAoBrF,CAAS,CAAC0F,qBAA9B,CAAqD,CAC7CjC,OAAO,CAAEgC,CADoC,CAE7ClC,KAAK,GAFwC,CAArD,CAIH,CANT,EAOKkB,IAPL,CAOU3E,CAAY,CAAC6E,SAPvB,CAQH,CATD,EAWA,MAAOf,CAAAA,CACV,CA1VyF,CAkWtF+B,CAAsB,CAAG,UAAW,CAIpC/F,CAAC,CAACwF,QAAD,CAAD,CAAYQ,EAAZ,CAAe5F,CAAS,CAAC6F,aAAzB,CAAwC,UAAW,CAC/ChE,CAAc,EACjB,CAFD,EAIAjC,CAAC,CAACwF,QAAD,CAAD,CAAYQ,EAAZ,CAAe5F,CAAS,CAACuF,2BAAzB,CAAsD,UAAW,CAC7DnD,CAAwB,GACxBxC,CAAC,CAACS,CAAS,CAACW,QAAX,CAAD,CAAsBwB,GAAtB,CAA0B,EAA1B,EACAY,CAAY,EACf,CAJD,EAMAxD,CAAC,CAACwF,QAAD,CAAD,CAAYQ,EAAZ,CAAe5F,CAAS,CAAC8F,0BAAzB,CAAqD,UAAW,CAC5DlE,CAAY,GACZD,CAAuB,EAC1B,CAHD,EAKA/B,CAAC,CAACwF,QAAD,CAAD,CAAYQ,EAAZ,CAAe5F,CAAS,CAACsF,4BAAzB,CAAuD,SAASS,CAAT,CAAgBhE,CAAhB,CAAqB,CACxEc,CAAyB,CAACd,CAAD,CAC5B,CAFD,EAIAnC,CAAC,CAACwF,QAAD,CAAD,CAAYQ,EAAZ,CAAe5F,CAAS,CAACgG,2BAAzB,CAAsD,UAAW,CAC7D7E,CAAiC,GAAG2B,IAApC,CAAyCzC,CAAS,CAACK,2BAAnD,EAAgFuF,UAAhF,CAA2F,oBAA3F,EACAtE,CAAuB,EAC1B,CAHD,EAKA/B,CAAC,CAACwF,QAAD,CAAD,CAAYQ,EAAZ,CAAe5F,CAAS,CAAC0F,qBAAzB,CAAgD,SAASK,CAAT,CAAgBxE,CAAhB,CAAsB,CAClE8B,CAAwB,CAAC9B,CAAD,CAC3B,CAFD,EAIA,GAAI2E,CAAAA,CAAe,CAAGtG,CAAC,CAACS,CAAS,CAACS,4BAAX,CAAvB,CACAoF,CAAe,CAACC,KAAhB,CAAsB,SAAS7E,CAAT,CAAY,CAC9BA,CAAC,CAAC8E,cAAF,GACArB,CAAgB,EACnB,CAHD,EAKA,GAAIsB,CAAAA,CAAY,CAAGzG,CAAC,CAACS,CAAS,CAACQ,kBAAX,CAApB,CACAwF,CAAY,CAACF,KAAb,CAAmB,SAAS7E,CAAT,CAAY,CAC3BA,CAAC,CAAC8E,cAAF,GACAvB,CAAa,EAChB,CAHD,CAKH,CA9YyF,CAgZ1F,MAAgE,CAK5DyB,IAAI,CAAE,eAAW,CACbX,CAAsB,GACtB9D,CAAc,EACjB,CAR2D,CAUnE,CA5ZK,CAAN","sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Standard Ajax wrapper for Moodle. It calls the central Ajax script,\n * which can call any existing webservice using the current session.\n * In addition, it can batch multiple requests and return multiple responses.\n *\n * @module mod_lti/tool_configure_controller\n * @class tool_configure_controller\n * @package mod_lti\n * @copyright 2015 Ryan Wyllie \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n * @since 3.1\n */\ndefine(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/events', 'mod_lti/keys', 'mod_lti/tool_type',\n 'mod_lti/tool_proxy', 'core/str'],\n function($, ajax, notification, templates, ltiEvents, KEYS, toolType, toolProxy, str) {\n\n var SELECTORS = {\n EXTERNAL_REGISTRATION_CONTAINER: '#external-registration-container',\n EXTERNAL_REGISTRATION_PAGE_CONTAINER: '#external-registration-page-container',\n EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER: '#external-registration-template-container',\n CARTRIDGE_REGISTRATION_CONTAINER: '#cartridge-registration-container',\n CARTRIDGE_REGISTRATION_FORM: '#cartridge-registration-form',\n ADD_TOOL_FORM: '#add-tool-form',\n TOOL_LIST_CONTAINER: '#tool-list-container',\n TOOL_CREATE_BUTTON: '#tool-create-button',\n TOOL_CREATE_LTILEGACY_BUTTON: '#tool-createltilegacy-button',\n REGISTRATION_CHOICE_CONTAINER: '#registration-choice-container',\n TOOL_URL: '#tool-url'\n };\n\n /**\n * Get the tool list container element.\n *\n * @method getToolListContainer\n * @private\n * @return {Object} jQuery object\n */\n var getToolListContainer = function() {\n return $(SELECTORS.TOOL_LIST_CONTAINER);\n };\n\n /**\n * Get the external registration container element.\n *\n * @method getExternalRegistrationContainer\n * @private\n * @return {Object} jQuery object\n */\n var getExternalRegistrationContainer = function() {\n return $(SELECTORS.EXTERNAL_REGISTRATION_CONTAINER);\n };\n\n /**\n * Get the cartridge registration container element.\n *\n * @method getCartridgeRegistrationContainer\n * @private\n * @return {Object} jQuery object\n */\n var getCartridgeRegistrationContainer = function() {\n return $(SELECTORS.CARTRIDGE_REGISTRATION_CONTAINER);\n };\n\n /**\n * Get the registration choice container element.\n *\n * @method getRegistrationChoiceContainer\n * @private\n * @return {Object} jQuery object\n */\n var getRegistrationChoiceContainer = function() {\n return $(SELECTORS.REGISTRATION_CHOICE_CONTAINER);\n };\n\n /**\n * Close the LTI Advantage Registration IFrame.\n *\n * @private\n * @param {Object} e post message event sent from the registration frame.\n */\n var closeLTIAdvRegistration = function(e) {\n if (e.data && 'org.imsglobal.lti.close' === e.data.subject) {\n $(SELECTORS.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER).empty();\n hideExternalRegistration();\n showRegistrationChoices();\n showToolList();\n showRegistrationChoices();\n reloadToolList();\n }\n };\n\n /**\n * Load the external registration template and render it in the DOM and display it.\n *\n * @method initiateRegistration\n * @private\n * @param {String} url where to send the registration request\n */\n var initiateRegistration = function(url) {\n // Show the external registration page in an iframe.\n $(SELECTORS.EXTERNAL_REGISTRATION_PAGE_CONTAINER).removeClass('hidden');\n var container = $(SELECTORS.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER);\n container.append($(\"\"));\n showExternalRegistration();\n window.addEventListener(\"message\", closeLTIAdvRegistration, false);\n };\n\n /**\n * Get the tool type URL.\n *\n * @method getToolURL\n * @private\n * @return {String} the tool type url\n */\n var getToolURL = function() {\n return $(SELECTORS.TOOL_URL).val();\n };\n\n /**\n * Hide the external registration container.\n *\n * @method hideExternalRegistration\n * @private\n */\n var hideExternalRegistration = function() {\n getExternalRegistrationContainer().addClass('hidden');\n };\n\n /**\n * Hide the cartridge registration container.\n *\n * @method hideCartridgeRegistration\n * @private\n */\n var hideCartridgeRegistration = function() {\n getCartridgeRegistrationContainer().addClass('hidden');\n };\n\n /**\n * Hide the registration choice container.\n *\n * @method hideRegistrationChoices\n * @private\n */\n var hideRegistrationChoices = function() {\n getRegistrationChoiceContainer().addClass('hidden');\n };\n\n /**\n * Display the external registration panel and hides the other\n * panels.\n *\n * @method showExternalRegistration\n * @private\n */\n var showExternalRegistration = function() {\n hideCartridgeRegistration();\n hideRegistrationChoices();\n getExternalRegistrationContainer().removeClass('hidden');\n screenReaderAnnounce(getExternalRegistrationContainer());\n };\n\n /**\n * Display the cartridge registration panel and hides the other\n * panels.\n *\n * @method showCartridgeRegistration\n * @param {String} url\n * @private\n */\n var showCartridgeRegistration = function(url) {\n hideExternalRegistration();\n hideRegistrationChoices();\n // Don't save the key and secret from the last tool.\n var container = getCartridgeRegistrationContainer();\n container.find('input').val('');\n container.removeClass('hidden');\n container.find(SELECTORS.CARTRIDGE_REGISTRATION_FORM).attr('data-cartridge-url', url);\n screenReaderAnnounce(container);\n };\n\n /**\n * Display the registration choices panel and hides the other\n * panels.\n *\n * @method showRegistrationChoices\n * @private\n */\n var showRegistrationChoices = function() {\n hideExternalRegistration();\n hideCartridgeRegistration();\n getRegistrationChoiceContainer().removeClass('hidden');\n screenReaderAnnounce(getRegistrationChoiceContainer());\n };\n\n /**\n * JAWS does not notice visibility changes with aria-live.\n * Remove and add the content back to force it to read it out.\n * This function can be removed once JAWS supports visibility.\n *\n * @method screenReaderAnnounce\n * @param {Object} element\n * @private\n */\n var screenReaderAnnounce = function(element) {\n var children = element.children().detach();\n children.appendTo(element);\n };\n\n /**\n * Hides the list of tool types.\n *\n * @method hideToolList\n * @private\n */\n var hideToolList = function() {\n getToolListContainer().addClass('hidden');\n };\n\n /**\n * Display the list of tool types.\n *\n * @method hideToolList\n * @private\n */\n var showToolList = function() {\n getToolListContainer().removeClass('hidden');\n };\n\n /**\n * Display the registration feedback alert and hide the other panels.\n *\n * @method showRegistrationFeedback\n * @param {Object} data\n * @private\n */\n var showRegistrationFeedback = function(data) {\n var type = data.error ? 'error' : 'success';\n notification.addNotification({\n message: data.message,\n type: type\n });\n };\n\n /**\n * Show the loading animation\n *\n * @method startLoading\n * @private\n * @param {Object} element jQuery object\n */\n var startLoading = function(element) {\n element.addClass(\"loading\");\n };\n\n /**\n * Hide the loading animation\n *\n * @method stopLoading\n * @private\n * @param {Object} element jQuery object\n */\n var stopLoading = function(element) {\n element.removeClass(\"loading\");\n };\n\n /**\n * Refresh the list of tool types and render the new ones.\n *\n * @method reloadToolList\n * @private\n */\n var reloadToolList = function() {\n var promise = $.Deferred();\n var container = getToolListContainer();\n startLoading(container);\n\n $.when(\n toolType.query(),\n toolProxy.query({'orphanedonly': true})\n )\n .done(function(types, proxies) {\n templates.render('mod_lti/tool_list', {tools: types, proxies: proxies})\n .done(function(html, js) {\n container.empty();\n container.append(html);\n templates.runTemplateJS(js);\n promise.resolve();\n }).fail(promise.reject);\n })\n .fail(promise.reject);\n\n promise.fail(notification.exception)\n .always(function() {\n stopLoading(container);\n });\n };\n\n /**\n * Start the LTI Advantage registration.\n *\n * @method addLTIAdvTool\n * @private\n */\n var addLTIAdvTool = function() {\n var url = $.trim(getToolURL());\n\n if (url) {\n $(SELECTORS.TOOL_URL).val('');\n hideToolList();\n initiateRegistration(url);\n }\n\n };\n\n /**\n * Trigger appropriate registration process process for the user input\n * URL. It can either be a cartridge or a registration url.\n *\n * @method addLTILegacyTool\n * @private\n * @return {Promise} jQuery Deferred object\n */\n var addLTILegacyTool = function() {\n var url = $.trim(getToolURL());\n\n if (url === \"\") {\n return $.Deferred().resolve();\n }\n var toolButton = $(SELECTORS.TOOL_CREATE_LTILEGACY_BUTTON);\n startLoading(toolButton);\n\n var promise = toolType.isCartridge(url);\n\n promise.always(function() {\n stopLoading(toolButton);\n });\n\n promise.done(function(result) {\n if (result.iscartridge) {\n $(SELECTORS.TOOL_URL).val('');\n $(document).trigger(ltiEvents.START_CARTRIDGE_REGISTRATION, url);\n } else {\n $(document).trigger(ltiEvents.START_EXTERNAL_REGISTRATION, {url: url});\n }\n });\n\n promise.fail(function() {\n str.get_string('errorbadurl', 'mod_lti')\n .done(function(s) {\n $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, {\n message: s,\n error: true\n });\n })\n .fail(notification.exception);\n });\n\n return promise;\n };\n\n /**\n * Sets up the listeners for user interaction on the page.\n *\n * @method registerEventListeners\n * @private\n */\n var registerEventListeners = function() {\n\n // These are events fired by the registration processes. Either\n // the cartridge registration or the external registration url.\n $(document).on(ltiEvents.NEW_TOOL_TYPE, function() {\n reloadToolList();\n });\n\n $(document).on(ltiEvents.START_EXTERNAL_REGISTRATION, function() {\n showExternalRegistration();\n $(SELECTORS.TOOL_URL).val('');\n hideToolList();\n });\n\n $(document).on(ltiEvents.STOP_EXTERNAL_REGISTRATION, function() {\n showToolList();\n showRegistrationChoices();\n });\n\n $(document).on(ltiEvents.START_CARTRIDGE_REGISTRATION, function(event, url) {\n showCartridgeRegistration(url);\n });\n\n $(document).on(ltiEvents.STOP_CARTRIDGE_REGISTRATION, function() {\n getCartridgeRegistrationContainer().find(SELECTORS.CARTRIDGE_REGISTRATION_FORM).removeAttr('data-cartridge-url');\n showRegistrationChoices();\n });\n\n $(document).on(ltiEvents.REGISTRATION_FEEDBACK, function(event, data) {\n showRegistrationFeedback(data);\n });\n\n var addLegacyButton = $(SELECTORS.TOOL_CREATE_LTILEGACY_BUTTON);\n addLegacyButton.click(function(e) {\n e.preventDefault();\n addLTILegacyTool();\n });\n\n var addLTIButton = $(SELECTORS.TOOL_CREATE_BUTTON);\n addLTIButton.click(function(e) {\n e.preventDefault();\n addLTIAdvTool();\n });\n\n };\n\n return /** @alias module:mod_lti/cartridge_registration_form */ {\n\n /**\n * Initialise this module.\n */\n init: function() {\n registerEventListeners();\n reloadToolList();\n }\n };\n});\n"],"file":"tool_configure_controller.min.js"} \ No newline at end of file diff --git a/mod/lti/amd/src/tool_card_controller.js b/mod/lti/amd/src/tool_card_controller.js index 763037f045a..4bd865c73f8 100644 --- a/mod/lti/amd/src/tool_card_controller.js +++ b/mod/lti/amd/src/tool_card_controller.js @@ -484,10 +484,7 @@ return toolTypeData; }).then(function(toolTypeData) { return templates.render('mod_lti/tool_card', toolTypeData); - }).then(function(renderResult) { - var html = renderResult[0]; - var js = renderResult[1]; - + }).then(function(html, js) { templates.replaceNode(element, html, js); return; }).catch(function() { diff --git a/mod/lti/amd/src/tool_configure_controller.js b/mod/lti/amd/src/tool_configure_controller.js index 40dc88589eb..6a38f598e87 100644 --- a/mod/lti/amd/src/tool_configure_controller.js +++ b/mod/lti/amd/src/tool_configure_controller.js @@ -32,26 +32,17 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e var SELECTORS = { EXTERNAL_REGISTRATION_CONTAINER: '#external-registration-container', EXTERNAL_REGISTRATION_PAGE_CONTAINER: '#external-registration-page-container', + EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER: '#external-registration-template-container', CARTRIDGE_REGISTRATION_CONTAINER: '#cartridge-registration-container', CARTRIDGE_REGISTRATION_FORM: '#cartridge-registration-form', ADD_TOOL_FORM: '#add-tool-form', TOOL_LIST_CONTAINER: '#tool-list-container', TOOL_CREATE_BUTTON: '#tool-create-button', + TOOL_CREATE_LTILEGACY_BUTTON: '#tool-createltilegacy-button', REGISTRATION_CHOICE_CONTAINER: '#registration-choice-container', TOOL_URL: '#tool-url' }; - /** - * Get the tool create button element. - * - * @method getToolCreateButton - * @private - * @return {Object} jQuery object - */ - var getToolCreateButton = function() { - return $(SELECTORS.TOOL_CREATE_BUTTON); - }; - /** * Get the tool list container element. * @@ -96,6 +87,40 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e return $(SELECTORS.REGISTRATION_CHOICE_CONTAINER); }; + /** + * Close the LTI Advantage Registration IFrame. + * + * @private + * @param {Object} e post message event sent from the registration frame. + */ + var closeLTIAdvRegistration = function(e) { + if (e.data && 'org.imsglobal.lti.close' === e.data.subject) { + $(SELECTORS.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER).empty(); + hideExternalRegistration(); + showRegistrationChoices(); + showToolList(); + showRegistrationChoices(); + reloadToolList(); + } + }; + + /** + * Load the external registration template and render it in the DOM and display it. + * + * @method initiateRegistration + * @private + * @param {String} url where to send the registration request + */ + var initiateRegistration = function(url) { + // Show the external registration page in an iframe. + $(SELECTORS.EXTERNAL_REGISTRATION_PAGE_CONTAINER).removeClass('hidden'); + var container = $(SELECTORS.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER); + container.append($("")); + showExternalRegistration(); + window.addEventListener("message", closeLTIAdvRegistration, false); + }; + /** * Get the tool type URL. * @@ -287,22 +312,38 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e }); }; + /** + * Start the LTI Advantage registration. + * + * @method addLTIAdvTool + * @private + */ + var addLTIAdvTool = function() { + var url = $.trim(getToolURL()); + + if (url) { + $(SELECTORS.TOOL_URL).val(''); + hideToolList(); + initiateRegistration(url); + } + + }; + /** * Trigger appropriate registration process process for the user input * URL. It can either be a cartridge or a registration url. * - * @method addTool + * @method addLTILegacyTool * @private * @return {Promise} jQuery Deferred object */ - var addTool = function() { + var addLTILegacyTool = function() { var url = $.trim(getToolURL()); if (url === "") { return $.Deferred().resolve(); } - - var toolButton = getToolCreateButton(); + var toolButton = $(SELECTORS.TOOL_CREATE_LTILEGACY_BUTTON); startLoading(toolButton); var promise = toolType.isCartridge(url); @@ -372,10 +413,16 @@ define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/e showRegistrationFeedback(data); }); - var form = $(SELECTORS.ADD_TOOL_FORM); - form.submit(function(e) { + var addLegacyButton = $(SELECTORS.TOOL_CREATE_LTILEGACY_BUTTON); + addLegacyButton.click(function(e) { e.preventDefault(); - addTool(); + addLTILegacyTool(); + }); + + var addLTIButton = $(SELECTORS.TOOL_CREATE_BUTTON); + addLTIButton.click(function(e) { + e.preventDefault(); + addLTIAdvTool(); }); }; diff --git a/mod/lti/certs.php b/mod/lti/certs.php index 7afbbe68440..419482fa37a 100644 --- a/mod/lti/certs.php +++ b/mod/lti/certs.php @@ -21,28 +21,13 @@ * @copyright 2019 Stephen Vickers * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_lti\local\ltiopenid\jwks_helper; define('NO_DEBUG_DISPLAY', true); define('NO_MOODLE_COOKIES', true); require_once(__DIR__ . '/../../config.php'); -$jwks = array('keys' => array()); - -$privatekey = get_config('mod_lti', 'privatekey'); -$res = openssl_pkey_get_private($privatekey); -$details = openssl_pkey_get_details($res); - -$jwk = array(); -$jwk['kty'] = 'RSA'; -$jwk['alg'] = 'RS256'; -$jwk['kid'] = get_config('mod_lti', 'kid'); -$jwk['e'] = rtrim(strtr(base64_encode($details['rsa']['e']), '+/', '-_'), '='); -$jwk['n'] = rtrim(strtr(base64_encode($details['rsa']['n']), '+/', '-_'), '='); -$jwk['use'] = 'sig'; - -$jwks['keys'][] = $jwk; - @header('Content-Type: application/json; charset=utf-8'); -echo json_encode($jwks, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); +echo json_encode(jwks_helper::get_jwks(), JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); diff --git a/mod/lti/classes/local/ltiopenid/jwks_helper.php b/mod/lti/classes/local/ltiopenid/jwks_helper.php new file mode 100644 index 00000000000..20cc95608a8 --- /dev/null +++ b/mod/lti/classes/local/ltiopenid/jwks_helper.php @@ -0,0 +1,72 @@ +. + +/** + * This files exposes functions for LTI 1.3 Key Management. + * + * @package mod_lti + * @copyright 2020 Claude Vervoort (Cengage) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace mod_lti\local\ltiopenid; + +/** + * This class exposes functions for LTI 1.3 Key Management. + * + * @package mod_lti + * @copyright 2020 Claude Vervoort (Cengage) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class jwks_helper { + + /** + * Returns the private key to use to sign outgoing JWT. + * + * @return array keys are kid and key in PEM format. + */ + public static function get_private_key() { + $privatekey = get_config('mod_lti', 'privatekey'); + $kid = get_config('mod_lti', 'kid'); + return [ + "key" => $privatekey, + "kid" => $kid + ]; + } + + /** + * Returns the JWK Key Set for this site. + * @return array keyset exposting the site public key. + */ + public static function get_jwks() { + $jwks = array('keys' => array()); + + $privatekey = self::get_private_key(); + $res = openssl_pkey_get_private($privatekey['key']); + $details = openssl_pkey_get_details($res); + + $jwk = array(); + $jwk['kty'] = 'RSA'; + $jwk['alg'] = 'RS256'; + $jwk['kid'] = $privatekey['kid']; + $jwk['e'] = rtrim(strtr(base64_encode($details['rsa']['e']), '+/', '-_'), '='); + $jwk['n'] = rtrim(strtr(base64_encode($details['rsa']['n']), '+/', '-_'), '='); + $jwk['use'] = 'sig'; + + $jwks['keys'][] = $jwk; + return $jwks; + } + +} diff --git a/mod/lti/classes/local/ltiopenid/registration_exception.php b/mod/lti/classes/local/ltiopenid/registration_exception.php new file mode 100644 index 00000000000..216f8c45a1e --- /dev/null +++ b/mod/lti/classes/local/ltiopenid/registration_exception.php @@ -0,0 +1,32 @@ +. + +/** + * This library exposes functions for LTI Dynamic Registration. + * + * @package mod_lti + * @copyright 2020 Claude Vervoort (Cengage), Carlos Costa, Adrian Hutchinson (Macgraw Hill) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace mod_lti\local\ltiopenid; + +/** + * Exception when transforming the registration to LTI config. + * + * Code is the HTTP Error code. + */ +class registration_exception extends \Exception { +} diff --git a/mod/lti/classes/local/ltiopenid/registration_helper.php b/mod/lti/classes/local/ltiopenid/registration_helper.php new file mode 100644 index 00000000000..448fc01bc2c --- /dev/null +++ b/mod/lti/classes/local/ltiopenid/registration_helper.php @@ -0,0 +1,345 @@ +. + +/** + * A Helper for LTI Dynamic Registration. + * + * @package mod_lti + * @copyright 2020 Claude Vervoort (Cengage), Carlos Costa, Adrian Hutchinson (Macgraw Hill) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +namespace mod_lti\local\ltiopenid; + +defined('MOODLE_INTERNAL') || die; + +require_once($CFG->dirroot . '/mod/lti/locallib.php'); +use Firebase\JWT\JWK; +use Firebase\JWT\JWT; +use stdClass; + +/** + * This class exposes functions for LTI Dynamic Registration. + * + * @package mod_lti + * @copyright 2020 Claude Vervoort (Cengage), Carlos Costa, Adrian Hutchinson (Macgraw Hill) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class registration_helper { + /** score scope */ + const SCOPE_SCORE = 'https://purl.imsglobal.org/spec/lti-ags/scope/score'; + /** result scope */ + const SCOPE_RESULT = 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly'; + /** lineitem read-only scope */ + const SCOPE_LINEITEM_RO = 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly'; + /** lineitem full access scope */ + const SCOPE_LINEITEM = 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem'; + /** Names and Roles (membership) scope */ + const SCOPE_NRPS = 'https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly'; + /** Tool Settings scope */ + const SCOPE_TOOL_SETTING = 'https://purl.imsglobal.org/spec/lti-ts/scope/toolsetting'; + + + /** + * Function used to validate parameters. + * + * This function is needed because the payload contains nested + * objects, and optional_param() does not support arrays of arrays. + * + * @param array $payload that may contain the parameter key + * @param string $key the key of the value to be looked for in the payload + * @param bool $required if required, not finding a value will raise a registration_exception + * + * @return mixed + */ + private static function get_parameter(array $payload, string $key, bool $required) { + if (!isset($payload[$key]) || empty($payload[$key])) { + if ($required) { + throw new registration_exception('missing required attribute '.$key, 400); + } + return null; + } + $parameter = $payload[$key]; + // Cleans parameters to avoid XSS and other issues. + if (is_array($parameter)) { + return clean_param_array($parameter, PARAM_TEXT, true); + } + return clean_param($parameter, PARAM_TEXT); + } + + /** + * Transforms an LTI 1.3 Registration to a Moodle LTI Config. + * + * @param array $registrationpayload the registration data received from the tool. + * @param string $clientid the clientid to be issued for that tool. + * + * @return object the Moodle LTI config. + */ + public static function registration_to_config(array $registrationpayload, string $clientid): object { + $responsetypes = self::get_parameter($registrationpayload, 'response_types', true); + $initiateloginuri = self::get_parameter($registrationpayload, 'initiate_login_uri', true); + $redirecturis = self::get_parameter($registrationpayload, 'redirect_uris', true); + $clientname = self::get_parameter($registrationpayload, 'client_name', true); + $jwksuri = self::get_parameter($registrationpayload, 'jwks_uri', true); + $tokenendpointauthmethod = self::get_parameter($registrationpayload, 'token_endpoint_auth_method', true); + + $applicationtype = self::get_parameter($registrationpayload, 'application_type', false); + $logouri = self::get_parameter($registrationpayload, 'logo_uri', false); + + $ltitoolconfiguration = self::get_parameter($registrationpayload, + 'https://purl.imsglobal.org/spec/lti-tool-configuration', true); + + $domain = self::get_parameter($ltitoolconfiguration, 'domain', true); + $targetlinkuri = self::get_parameter($ltitoolconfiguration, 'target_link_uri', true); + $customparameters = self::get_parameter($ltitoolconfiguration, 'custom_parameters', false); + $scopes = explode(" ", self::get_parameter($registrationpayload, 'scope', false) ?? ''); + $claims = self::get_parameter($ltitoolconfiguration, 'claims', false); + $messages = $ltitoolconfiguration['messages'] ?? []; + $description = self::get_parameter($ltitoolconfiguration, 'description', false); + + // Validate response type. + // According to specification, for this scenario, id_token must be explicitly set. + if (!in_array('id_token', $responsetypes)) { + throw new registration_exception('invalid_response_types', 400); + } + + // According to specification, this parameter needs to be an array. + if (!is_array($redirecturis)) { + throw new registration_exception('invalid_redirect_uris', 400); + } + + // According to specification, for this scenario private_key_jwt must be explicitly set. + if ($tokenendpointauthmethod !== 'private_key_jwt') { + throw new registration_exception('invalid_token_endpoint_auth_method', 400); + } + + if (!empty($applicationtype) && $applicationtype !== 'web') { + throw new registration_exception('invalid_application_type', 400); + } + + $config = new stdClass(); + $config->lti_clientid = $clientid; + $config->lti_toolurl = $targetlinkuri; + $config->lti_tooldomain = $domain; + $config->lti_typename = $clientname; + $config->lti_description = $description; + $config->lti_ltiversion = LTI_VERSION_1P3; + $config->lti_organizationid_default = LTI_DEFAULT_ORGID_SITEID; + $config->lti_icon = $logouri; + $config->lti_coursevisible = LTI_COURSEVISIBLE_PRECONFIGURED; + $config->lti_contentitem = 0; + // Sets Content Item. + if (!empty($messages)) { + $messagesresponse = []; + foreach ($messages as $value) { + if ($value['type'] === 'LtiDeepLinkingRequest') { + $config->lti_contentitem = 1; + $config->lti_toolurl_ContentItemSelectionRequest = $value['target_link_uri'] ?? ''; + array_push($messagesresponse, $value); + } + } + } + + $config->lti_keytype = 'JWK_KEYSET'; + $config->lti_publickeyset = $jwksuri; + $config->lti_initiatelogin = $initiateloginuri; + $config->lti_redirectionuris = implode(PHP_EOL, $redirecturis); + $config->lti_customparameters = ''; + // Sets custom parameters. + if (isset($customparameters)) { + $paramssarray = []; + foreach ($customparameters as $key => $value) { + array_push($paramssarray, $key . '=' . $value); + } + $config->lti_customparameters = implode(PHP_EOL, $paramssarray); + } + // Sets launch container. + $config->lti_launchcontainer = LTI_LAUNCH_CONTAINER_EMBED_NO_BLOCKS; + + // Sets Service info based on scopes. + $config->lti_acceptgrades = LTI_SETTING_NEVER; + $config->ltiservice_gradesynchronization = 0; + $config->ltiservice_memberships = 0; + $config->ltiservice_toolsettings = 0; + if (isset($scopes)) { + // Sets Assignment and Grade Services info. + + if (in_array(self::SCOPE_SCORE, $scopes)) { + $config->lti_acceptgrades = LTI_SETTING_DELEGATE; + $config->ltiservice_gradesynchronization = 1; + } + if (in_array(self::SCOPE_RESULT, $scopes)) { + $config->lti_acceptgrades = LTI_SETTING_DELEGATE; + $config->ltiservice_gradesynchronization = 1; + } + if (in_array(self::SCOPE_LINEITEM_RO, $scopes)) { + $config->lti_acceptgrades = LTI_SETTING_DELEGATE; + $config->ltiservice_gradesynchronization = 1; + } + if (in_array(self::SCOPE_LINEITEM, $scopes)) { + $config->lti_acceptgrades = LTI_SETTING_DELEGATE; + $config->ltiservice_gradesynchronization = 2; + } + + // Sets Names and Role Provisioning info. + if (in_array(self::SCOPE_NRPS, $scopes)) { + $config->ltiservice_memberships = 1; + } + + // Sets Tool Settings info. + if (in_array(self::SCOPE_TOOL_SETTING, $scopes)) { + $config->ltiservice_toolsettings = 1; + } + } + + // Sets privacy settings. + $config->lti_sendname = LTI_SETTING_NEVER; + $config->lti_sendemailaddr = LTI_SETTING_NEVER; + if (isset($claims)) { + // Sets name privacy settings. + + if (in_array('name', $claims)) { + $config->lti_sendname = LTI_SETTING_ALWAYS; + } + if (in_array('given_name', $claims)) { + $config->lti_sendname = LTI_SETTING_ALWAYS; + } + if (in_array('family_name', $claims)) { + $config->lti_sendname = LTI_SETTING_ALWAYS; + } + + // Sets email privacy settings. + if (in_array('email', $claims)) { + $config->lti_sendemailaddr = LTI_SETTING_ALWAYS; + } + } + return $config; + } + + /** + * Transforms a moodle LTI 1.3 Config to an OAuth/LTI Client Registration. + * + * @param object $config Moodle LTI Config. + * @param int $typeid which is the LTI deployment id. + * + * @return array the Client Registration as an associative array. + */ + public static function config_to_registration(object $config, int $typeid): array { + $registrationresponse = []; + $registrationresponse['client_id'] = $config->lti_clientid; + $registrationresponse['token_endpoint_auth_method'] = ['private_key_jwt']; + $registrationresponse['response_types'] = ['id_token']; + $registrationresponse['jwks_uri'] = $config->lti_publickeyset; + $registrationresponse['initiate_login_uri'] = $config->lti_initiatelogin; + $registrationresponse['grant_types'] = ['client_credentials', 'implicit']; + $registrationresponse['redirect_uris'] = explode(PHP_EOL, $config->lti_redirectionuris); + $registrationresponse['application_type'] = ['web']; + $registrationresponse['token_endpoint_auth_method'] = 'private_key_jwt'; + $registrationresponse['client_name'] = $config->lti_typename; + $registrationresponse['logo_uri'] = $config->lti_icon ?? ''; + $lticonfigurationresponse = []; + $lticonfigurationresponse['deployment_id'] = strval($typeid); + $lticonfigurationresponse['target_link_uri'] = $config->lti_toolurl; + $lticonfigurationresponse['domain'] = $config->lti_tooldomain ?? ''; + $lticonfigurationresponse['description'] = $config->lti_description ?? ''; + if ($config->lti_contentitem == 1) { + $contentitemmessage = []; + $contentitemmessage['type'] = 'LtiDeepLinkingRequest'; + if (isset($config->lti_toolurl_ContentItemSelectionRequest)) { + $contentitemmessage['target_link_uri'] = $config->lti_toolurl_ContentItemSelectionRequest; + } + $lticonfigurationresponse['messages'] = [$contentitemmessage]; + } + if (isset($config->lti_customparameters) && !empty($config->lti_customparameters)) { + $params = []; + foreach (explode(PHP_EOL, $config->lti_customparameters) as $param) { + $split = explode('=', $param); + $params[$split[0]] = $split[1]; + } + $lticonfigurationresponse['custom_parameters'] = $params; + } + $scopesresponse = []; + if ($config->ltiservice_gradesynchronization > 0) { + $scopesresponse[] = self::SCOPE_SCORE; + $scopesresponse[] = self::SCOPE_RESULT; + $scopesresponse[] = self::SCOPE_LINEITEM_RO; + } + if ($config->ltiservice_gradesynchronization == 2) { + $scopesresponse[] = self::SCOPE_LINEITEM; + } + if ($config->ltiservice_memberships == 1) { + $scopesresponse[] = self::SCOPE_NRPS; + } + if ($config->ltiservice_toolsettings == 1) { + $scopesresponse[] = self::SCOPE_TOOL_SETTING; + } + $registrationresponse['scope'] = implode(' ', $scopesresponse); + + $claimsresponse = ['sub', 'iss']; + if ($config->lti_sendname = LTI_SETTING_ALWAYS) { + $claimsresponse[] = 'name'; + $claimsresponse[] = 'family_name'; + $claimsresponse[] = 'middle_name'; + } + if ($config->lti_sendemailaddr = LTI_SETTING_ALWAYS) { + $claimsresponse[] = 'email'; + } + $lticonfigurationresponse['claims'] = $claimsresponse; + $registrationresponse['https://purl.imsglobal.org/spec/lti-tool-configuration'] = $lticonfigurationresponse; + return $registrationresponse; + } + + /** + * Validates the registration token is properly signed and not used yet. + * Return the client id to use for this registration. + * + * @param string $registrationtokenjwt registration token + * + * @return string client id for the registration + */ + public static function validate_registration_token(string $registrationtokenjwt): string { + global $DB; + $keys = JWK::parseKeySet(jwks_helper::get_jwks()); + $registrationtoken = JWT::decode($registrationtokenjwt, $keys, ['RS256']); + + // Get clientid from registrationtoken. + $clientid = $registrationtoken->sub; + + // Checks if clientid is already registered. + if (!empty($DB->get_record('lti_types', array('clientid' => $clientid)))) { + throw new registration_exception("token_already_used", 401); + } + return $clientid; + } + + /** + * Initializes an array with the scopes for services supported by the LTI module + * + * @return array List of scopes + */ + public static function lti_get_service_scopes() { + + $services = lti_get_services(); + $scopes = array(); + foreach ($services as $service) { + $servicescopes = $service->get_scopes(); + if (!empty($servicescopes)) { + $scopes = array_merge($scopes, $servicescopes); + } + } + return $scopes; + } + +} diff --git a/mod/lti/classes/local/ltiservice/service_base.php b/mod/lti/classes/local/ltiservice/service_base.php index d95aa53b717..234b558f1da 100644 --- a/mod/lti/classes/local/ltiservice/service_base.php +++ b/mod/lti/classes/local/ltiservice/service_base.php @@ -207,7 +207,7 @@ abstract class service_base { abstract public function get_resources(); /** - * Get the scope(s) permitted for this service. + * Get the scope(s) permitted for this service in the context of a particular tool type. * * A null value indicates that no scopes are required to access the service. * @@ -217,6 +217,17 @@ abstract class service_base { return null; } + /** + * Get the scope(s) permitted for this service. + * + * A null value indicates that no scopes are required to access the service. + * + * @return array|null + */ + public function get_scopes() { + return null; + } + /** * Returns the configuration options for this service. * diff --git a/mod/lti/lang/en/lti.php b/mod/lti/lang/en/lti.php index 51d2593923c..37467602362 100644 --- a/mod/lti/lang/en/lti.php +++ b/mod/lti/lang/en/lti.php @@ -66,6 +66,8 @@ $string['activate'] = 'Activate'; $string['activatetoadddescription'] = 'You will need to activate this tool before you can add a description.'; $string['active'] = 'Active'; $string['activity'] = 'Activity'; +$string['add_ltiadv'] = 'Add LTI Advantage'; +$string['add_ltilegacy'] = 'Add Legacy LTI'; $string['addnewapp'] = 'Enable external application'; $string['addserver'] = 'Add new trusted server'; $string['addtype'] = 'Add preconfigured tool'; diff --git a/mod/lti/locallib.php b/mod/lti/locallib.php index b33e91f5aa5..aacca4f7573 100644 --- a/mod/lti/locallib.php +++ b/mod/lti/locallib.php @@ -54,6 +54,7 @@ defined('MOODLE_INTERNAL') || die; use moodle\mod\lti as lti; use Firebase\JWT\JWT; use Firebase\JWT\JWK; +use mod_lti\local\ltiopenid\jwks_helper; global $CFG; require_once($CFG->dirroot.'/mod/lti/OAuth.php'); @@ -2720,7 +2721,11 @@ function lti_get_type_type_config($id) { function lti_prepare_type_for_save($type, $config) { if (isset($config->lti_toolurl)) { $type->baseurl = $config->lti_toolurl; - $type->tooldomain = lti_get_domain_from_url($config->lti_toolurl); + if (isset($config->lti_tooldomain)) { + $type->tooldomain = $config->lti_tooldomain; + } else { + $type->tooldomain = lti_get_domain_from_url($config->lti_toolurl); + } } if (isset($config->lti_description)) { $type->description = $config->lti_description; @@ -3273,9 +3278,8 @@ function lti_sign_jwt($parms, $endpoint, $oauthconsumerkey, $typeid = 0, $nonce } } - $privatekey = get_config('mod_lti', 'privatekey'); - $kid = get_config('mod_lti', 'kid'); - $jwt = JWT::encode($payload, $privatekey, 'RS256', $kid); + $privatekey = jwks_helper::get_private_key(); + $jwt = JWT::encode($payload, $privatekey['key'], 'RS256', $privatekey['kid']); $newparms = array(); $newparms['id_token'] = $jwt; @@ -3820,6 +3824,7 @@ function lti_get_service_by_resource_id($services, $resourceid) { /** * Initializes an array with the scopes for services supported by the LTI module + * and authorized for this particular tool instance. * * @param object $type LTI tool type * @param array $typeconfig LTI tool type configuration @@ -3840,7 +3845,6 @@ function lti_get_permitted_service_scopes($type, $typeconfig) { } return $scopes; - } /** @@ -4455,3 +4459,4 @@ function lti_new_access_token($typeid, $scopes) { return $newtoken; } + diff --git a/mod/lti/openid-configuration.php b/mod/lti/openid-configuration.php new file mode 100644 index 00000000000..e30b121e32a --- /dev/null +++ b/mod/lti/openid-configuration.php @@ -0,0 +1,61 @@ +. + +/** + * This file returns the OpenId/LTI Configuration for this site. + * + * It is part of the LTI Tool Dynamic Registration, and used by + * tools to get the site configuration and registration end-point. + * + * @package mod_lti + * @copyright 2020 Claude Vervoort (Cengage), Carlos Costa, Adrian Hutchinson (Macgraw Hill) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use mod_lti\local\ltiopenid\registration_helper; + +define('NO_DEBUG_DISPLAY', true); +define('NO_MOODLE_COOKIES', true); +require_once(__DIR__ . '/../../config.php'); +require_once($CFG->dirroot . '/mod/lti/locallib.php'); +require_once($CFG->libdir.'/weblib.php'); + +$scopes = registration_helper::lti_get_service_scopes(); +$scopes[] = 'openid'; +$conf = [ + 'issuer' => $CFG->wwwroot, + 'token_endpoint' => (new moodle_url('/mod/lti/token.php'))->out(false), + 'token_endpoint_auth_methods_supported' => ['private_key_jwt'], + 'token_endpoint_auth_signing_alg_values_supported' => ['RS256'], + 'jwks_uri' => (new moodle_url('/mod/lti/certs.php'))->out(false), + 'registration_endpoint' => (new moodle_url('/mod/lti/openid-registration.php'))->out(false), + 'scopes_supported' => $scopes, + 'response_types_supported' => ['id_token'], + 'subject_types_supported' => ['public', 'pairwise'], + 'id_token_signing_alg_values_supported' => ['RS256'], + 'claims_supported' => ['sub', 'iss', 'name', 'given_name', 'family_name', 'email'], + 'https://purl.imsglobal.org/spec/lti-platform-configuration ' => [ + 'product_family_code' => 'moodle', + 'version' => $CFG->release, + 'messages_supported' => ['LtiResourceLink', 'LtiDeepLinkingRequest'], + 'placements' => ['AddContentMenu'], + 'variables' => array_keys(lti_get_capabilities()) + ] +]; + +@header('Content-Type: application/json; charset=utf-8'); + +echo json_encode($conf, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT); diff --git a/mod/lti/openid-registration.php b/mod/lti/openid-registration.php new file mode 100644 index 00000000000..c06e531dc1b --- /dev/null +++ b/mod/lti/openid-registration.php @@ -0,0 +1,62 @@ +. + +/** + * This file receives a registration request along with the registration token and returns a client_id. + * + * @copyright 2020 Claude Vervoort (Cengage), Carlos Costa, Adrian Hutchinson (Macgraw Hill) + * @package mod_lti + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +define('NO_DEBUG_DISPLAY', true); +define('NO_MOODLE_COOKIES', true); + +use mod_lti\local\ltiopenid\registration_helper; +use mod_lti\local\ltiopenid\registration_exception; + +require_once(__DIR__ . '/../../config.php'); +require_once($CFG->dirroot . '/mod/lti/locallib.php'); + +$code = 200; +$message = ''; +// Retrieve registration token from Bearer Authorization header. +$authheader = moodle\mod\lti\OAuthUtil::get_headers() ['Authorization'] ?? ''; +if (!($authheader && substr($authheader, 0, 7) == 'Bearer ')) { + $message = 'missing_registration_token'; + $code = 401; +} else { + $registrationpayload = json_decode(file_get_contents('php://input'), true); + + // Registers tool. + $type = new stdClass(); + $type->state = LTI_TOOL_STATE_PENDING; + try { + $clientid = registration_helper::validate_registration_token(trim(substr($authheader, 7))); + $config = registration_helper::registration_to_config($registrationpayload, $clientid); + $typeid = lti_add_type($type, clone $config); + $message = json_encode(registration_helper::config_to_registration($config, $typeid)); + header('Content-Type: application/json; charset=utf-8'); + } catch (registration_exception $e) { + $code = $e->getCode(); + $message = $e->getMessage(); + } +} +$response = new \mod_lti\local\ltiservice\response(); +// Set code. +$response->set_code($code); +// Set body. +$response->set_body($message); +$response->send(); diff --git a/mod/lti/service/basicoutcomes/classes/local/service/basicoutcomes.php b/mod/lti/service/basicoutcomes/classes/local/service/basicoutcomes.php index 32c2a474b5c..bfc6420609a 100644 --- a/mod/lti/service/basicoutcomes/classes/local/service/basicoutcomes.php +++ b/mod/lti/service/basicoutcomes/classes/local/service/basicoutcomes.php @@ -81,4 +81,13 @@ class basicoutcomes extends \mod_lti\local\ltiservice\service_base { } + /** + * Get the scope(s) permitted for the tool relevant to this service. + * + * @return array + */ + public function get_scopes() { + return [self::SCOPE_BASIC_OUTCOMES]; + } + } diff --git a/mod/lti/service/gradebookservices/classes/local/service/gradebookservices.php b/mod/lti/service/gradebookservices/classes/local/service/gradebookservices.php index 9d35849f78f..caa437aca35 100644 --- a/mod/lti/service/gradebookservices/classes/local/service/gradebookservices.php +++ b/mod/lti/service/gradebookservices/classes/local/service/gradebookservices.php @@ -112,6 +112,16 @@ class gradebookservices extends service_base { } + /** + * Get the scopes defined by this service. + * + * @return array + */ + public function get_scopes() { + return [self::SCOPE_GRADEBOOKSERVICES_LINEITEM_READ, self::SCOPE_GRADEBOOKSERVICES_RESULT_READ, + self::SCOPE_GRADEBOOKSERVICES_SCORE, self::SCOPE_GRADEBOOKSERVICES_LINEITEM]; + } + /** * Adds form elements for gradebook sync add/edit page. * diff --git a/mod/lti/service/memberships/classes/local/service/memberships.php b/mod/lti/service/memberships/classes/local/service/memberships.php index 231e3cf1b26..2572799f1a5 100644 --- a/mod/lti/service/memberships/classes/local/service/memberships.php +++ b/mod/lti/service/memberships/classes/local/service/memberships.php @@ -102,6 +102,15 @@ class memberships extends \mod_lti\local\ltiservice\service_base { } + /** + * Get the scope(s) defined by this service. + * + * @return array + */ + public function get_scopes() { + return [self::SCOPE_MEMBERSHIPS_READ]; + } + /** * Get the JSON for members. * diff --git a/mod/lti/service/toolsettings/classes/local/service/toolsettings.php b/mod/lti/service/toolsettings/classes/local/service/toolsettings.php index 31ca1d6539b..91a851f42e8 100644 --- a/mod/lti/service/toolsettings/classes/local/service/toolsettings.php +++ b/mod/lti/service/toolsettings/classes/local/service/toolsettings.php @@ -88,6 +88,15 @@ class toolsettings extends \mod_lti\local\ltiservice\service_base { } + /** + * Get the scope(s) defined this service. + * + * @return array + */ + public function get_scopes() { + return [self::SCOPE_TOOL_SETTINGS]; + } + /** * Get the distinct settings from each level by removing any duplicates from higher levels. * diff --git a/mod/lti/startltiadvregistration.php b/mod/lti/startltiadvregistration.php new file mode 100644 index 00000000000..9644a9479ff --- /dev/null +++ b/mod/lti/startltiadvregistration.php @@ -0,0 +1,51 @@ +. + +/** + * Redirect the user to registration with token and openid config url as query params. + * + * @package mod_lti + * @copyright 2020 Cengage + * @author Claude Vervoort + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use Firebase\JWT\JWT; + +use mod_lti\local\ltiopenid\jwks_helper; + +require_once(__DIR__ . '/../../config.php'); +require_once($CFG->libdir.'/weblib.php'); + +require_login(); +$context = context_system::instance(); +require_capability('moodle/site:config', $context); + +$starturl = required_param('url', PARAM_URL); +$now = time(); +$token = [ + "sub" => random_string(15), + "scope" => "reg", + "iat" => $now, + "exp" => $now + HOURSECS +]; +$privatekey = jwks_helper::get_private_key(); +$regtoken = JWT::encode($token, $privatekey['key'], 'RS256', $privatekey['kid']); +$confurl = new moodle_url('/mod/lti/openid-configuration.php'); +$url = new moodle_url($starturl); +$url->param('openid_configuration', $confurl->out(false)); +$url->param('registration_token', $regtoken); +header("Location: ".$url->out(false)); diff --git a/mod/lti/templates/cartridge_registration_form.mustache b/mod/lti/templates/cartridge_registration_form.mustache index 5e415230cb7..4821f79a464 100644 --- a/mod/lti/templates/cartridge_registration_form.mustache +++ b/mod/lti/templates/cartridge_registration_form.mustache @@ -75,15 +75,15 @@
diff --git a/mod/lti/templates/external_registration.mustache b/mod/lti/templates/external_registration.mustache index 6be87467849..9c5a601c433 100644 --- a/mod/lti/templates/external_registration.mustache +++ b/mod/lti/templates/external_registration.mustache @@ -29,13 +29,17 @@ Context variables required for this template: * + Example context (json): + { + } + }}
diff --git a/mod/lti/templates/loader.mustache b/mod/lti/templates/loader.mustache index 0f21877cb2d..6a4d6b951b0 100644 --- a/mod/lti/templates/loader.mustache +++ b/mod/lti/templates/loader.mustache @@ -34,6 +34,6 @@ } }} -
+ {{#pix}} i/loading, core, {{#str}} loadinghelp, moodle {{/str}} {{/pix}} -
+ diff --git a/mod/lti/templates/tool_configure.mustache b/mod/lti/templates/tool_configure.mustache index 4060152b8b7..099e3c52dc7 100644 --- a/mod/lti/templates/tool_configure.mustache +++ b/mod/lti/templates/tool_configure.mustache @@ -29,6 +29,13 @@ Context variables required for this template: * + Example context (json): + { + "configuremanualurl":"https://some.tool.example/mod/lti/typessettings.php?sesskey=OKl37bHflL&returnto=toolconfigure", + "managetoolsurl":"https://some.tool.example/admin/settings.php?section=modsettinglti", + "managetoolproxiesurl":"https://some.tool.example/mod/lti/toolproxies.php" + } + }}

{{#str}} manage_external_tools, mod_lti {{/str}}

@@ -46,10 +53,16 @@ placeholder="{{#str}} toolurlplaceholder, mod_lti {{/str}}" required> +
diff --git a/mod/lti/tests/behat/toolconfigure.feature b/mod/lti/tests/behat/toolconfigure.feature index 171ebee8e10..3622bf80962 100644 --- a/mod/lti/tests/behat/toolconfigure.feature +++ b/mod/lti/tests/behat/toolconfigure.feature @@ -22,7 +22,7 @@ Feature: Configure tool types @javascript Scenario: Add a tool type from a cartridge URL When I set the field "url" to local url "/mod/lti/tests/fixtures/ims_cartridge_basic_lti_link.xml" - And I press "Add" + And I press "Add Legacy LTI" Then I should see "Enter your consumer key and shared secret" And I press "Save changes" And I should see "Example tool" @@ -30,7 +30,7 @@ Feature: Configure tool types @javascript Scenario: Try to add a non-existant cartridge When I set the field "url" to local url "/mod/lti/tests/fixtures/nonexistant.xml" - And I press "Add" + And I press "Add Legacy LTI" Then I should see "Enter your consumer key and shared secret" And I press "Save changes" And I should see "Failed to create new tool. Please check the URL and try again." @@ -38,6 +38,6 @@ Feature: Configure tool types @javascript Scenario: Attempt to add a tool type from a configuration URL, then cancel When I set the field "url" to local url "/mod/lti/tests/fixtures/tool_provider.php" - And I press "Add" + And I press "Add Legacy LTI" Then I should see "Cancel" And I press "cancel-external-registration" diff --git a/mod/lti/tests/openidregistration_test.php b/mod/lti/tests/openidregistration_test.php new file mode 100644 index 00000000000..af678295506 --- /dev/null +++ b/mod/lti/tests/openidregistration_test.php @@ -0,0 +1,269 @@ +. +// +// This file is part of BasicLTI4Moodle +// +// BasicLTI4Moodle is an IMS BasicLTI (Basic Learning Tools for Interoperability) +// consumer for Moodle 1.9 and Moodle 2.0. BasicLTI is a IMS Standard that allows web +// based learning tools to be easily integrated in LMS as native ones. The IMS BasicLTI +// specification is part of the IMS standard Common Cartridge 1.1 Sakai and other main LMS +// are already supporting or going to support BasicLTI. This project Implements the consumer +// for Moodle. Moodle is a Free Open source Learning Management System by Martin Dougiamas. +// BasicLTI4Moodle is a project iniciated and leaded by Ludo(Marc Alier) and Jordi Piguillem +// at the GESSI research group at UPC. +// SimpleLTI consumer for Moodle is an implementation of the early specification of LTI +// by Charles Severance (Dr Chuck) htp://dr-chuck.com , developed by Jordi Piguillem in a +// Google Summer of Code 2008 project co-mentored by Charles Severance and Marc Alier. +// +// BasicLTI4Moodle is copyright 2009 by Marc Alier Forment, Jordi Piguillem and Nikolas Galanis +// of the Universitat Politecnica de Catalunya http://www.upc.edu +// Contact info: Marc Alier Forment granludo @ gmail.com or marc.alier @ upc.edu. + +/** + * This file contains unit tests for lti/openidregistrationlib.php + * + * @package mod_lti + * @copyright 2020 Claude Vervoort, Cengage + * @author Claude Vervoort + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +use mod_lti\local\ltiopenid\registration_exception; +use mod_lti\local\ltiopenid\registration_helper; + +/** + * OpenId LTI Registration library tests + */ +class mod_lti_openidregistrationlib_testcase extends advanced_testcase { + + /** + * @var string A has-it-all client registration. + */ + private $registrationfulljson = <<registrationfulljson, true); + $registration['scope'] .= ' https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly'; + $config = registration_helper::registration_to_config($registration, 'TheClientId'); + $this->assertEquals('JWK_KEYSET', $config->lti_keytype); + $this->assertEquals(LTI_VERSION_1P3, $config->lti_ltiversion); + $this->assertEquals('TheClientId', $config->lti_clientid); + $this->assertEquals('Virtual Garden', $config->lti_typename); + $this->assertEquals('Learn Botany by tending to your little (virtual) garden.', $config->lti_description); + $this->assertEquals('https://client.example.org/lti/init', $config->lti_initiatelogin); + $this->assertEquals(implode(PHP_EOL, ["https://client.example.org/callback", + "https://client.example.org/callback2"]), $config->lti_redirectionuris); + $this->assertEquals("context_history=\$Context.id.history", $config->lti_customparameters); + $this->assertEquals("https://client.example.org/.well-known/jwks.json", $config->lti_publickeyset); + $this->assertEquals("https://client.example.org/logo.png", $config->lti_icon); + $this->assertEquals(2, $config->ltiservice_gradesynchronization); + $this->assertEquals(LTI_SETTING_DELEGATE, $config->lti_acceptgrades); + $this->assertEquals(1, $config->ltiservice_memberships); + $this->assertEquals(0, $config->ltiservice_toolsettings); + $this->assertEquals(LTI_SETTING_ALWAYS, $config->lti_sendname); + $this->assertEquals(LTI_SETTING_ALWAYS, $config->lti_sendemailaddr); + $this->assertEquals(1, $config->lti_contentitem); + $this->assertEquals('https://client.example.org/lti/dl', $config->lti_toolurl_ContentItemSelectionRequest); + } + + /** + * Test the mapping from Registration JSON to LTI Config for a minimal tool registration. + */ + public function test_to_config_minimal() { + $registration = json_decode($this->registrationminimaljson, true); + $config = registration_helper::registration_to_config($registration, 'TheClientId'); + $this->assertEquals('JWK_KEYSET', $config->lti_keytype); + $this->assertEquals(LTI_VERSION_1P3, $config->lti_ltiversion); + $this->assertEquals('TheClientId', $config->lti_clientid); + $this->assertEquals('Virtual Garden', $config->lti_typename); + $this->assertEmpty($config->lti_description); + $this->assertEquals('https://client.example.org/lti/init', $config->lti_initiatelogin); + $this->assertEquals('https://client.example.org/callback', $config->lti_redirectionuris); + $this->assertEmpty($config->lti_customparameters); + $this->assertEquals("https://client.example.org/.well-known/jwks.json", $config->lti_publickeyset); + $this->assertEmpty($config->lti_icon); + $this->assertEquals(0, $config->ltiservice_gradesynchronization); + $this->assertEquals(LTI_SETTING_NEVER, $config->lti_acceptgrades); + $this->assertEquals(0, $config->ltiservice_memberships); + $this->assertEquals(LTI_SETTING_NEVER, $config->lti_sendname); + $this->assertEquals(LTI_SETTING_NEVER, $config->lti_sendemailaddr); + $this->assertEquals(0, $config->lti_contentitem); + } + + /** + * Test the mapping from Registration JSON to LTI Config for a minimal tool with + * deep linking support registration. + */ + public function test_to_config_minimal_with_deeplinking() { + $registration = json_decode($this->registrationminimaldljson, true); + $config = registration_helper::registration_to_config($registration, 'TheClientId'); + $this->assertEquals(1, $config->lti_contentitem); + $this->assertEmpty($config->lti_toolurl_ContentItemSelectionRequest); + } + + /** + * Validation Test: initiation login. + */ + public function test_validation_initlogin() { + $registration = json_decode($this->registrationfulljson, true); + $this->expectException(registration_exception::class); + $this->expectExceptionCode(400); + unset($registration['initiate_login_uri']); + registration_helper::registration_to_config($registration, 'TheClientId'); + } + + /** + * Validation Test: redirect uris. + */ + public function test_validation_redirecturis() { + $registration = json_decode($this->registrationfulljson, true); + $this->expectException(registration_exception::class); + $this->expectExceptionCode(400); + unset($registration['redirect_uris']); + registration_helper::registration_to_config($registration, 'TheClientId'); + } + + /** + * Validation Test: jwks uri empty. + */ + public function test_validation_jwks() { + $registration = json_decode($this->registrationfulljson, true); + $this->expectException(registration_exception::class); + $this->expectExceptionCode(400); + $registration['jwks_uri'] = ''; + registration_helper::registration_to_config($registration, 'TheClientId'); + } + + /** + * Test the transformation from lti config to OpenId LTI Client Registration response. + */ + public function test_config_to_registration() { + $orig = json_decode($this->registrationfulljson, true); + $orig['scope'] .= ' https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly'; + $reg = registration_helper::config_to_registration(registration_helper::registration_to_config($orig, 'clid'), 12); + $this->assertEquals('clid', $reg['client_id']); + $this->assertEquals($orig['response_types'], $reg['response_types']); + $this->assertEquals($orig['initiate_login_uri'], $reg['initiate_login_uri']); + $this->assertEquals($orig['redirect_uris'], $reg['redirect_uris']); + $this->assertEquals($orig['jwks_uri'], $reg['jwks_uri']); + $this->assertEquals($orig['logo_uri'], $reg['logo_uri']); + $this->assertEquals('https://purl.imsglobal.org/spec/lti-ags/scope/score '. + 'https://purl.imsglobal.org/spec/lti-ags/scope/result.readonly '. + 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem.readonly '. + 'https://purl.imsglobal.org/spec/lti-ags/scope/lineitem '. + 'https://purl.imsglobal.org/spec/lti-nrps/scope/contextmembership.readonly', $reg['scope']); + $ltiorig = $orig['https://purl.imsglobal.org/spec/lti-tool-configuration']; + $lti = $reg['https://purl.imsglobal.org/spec/lti-tool-configuration']; + $this->assertEquals("12", $lti['deployment_id']); + $this->assertEquals($ltiorig['target_link_uri'], $lti['target_link_uri']); + $this->assertEquals($ltiorig['domain'], $lti['domain']); + $this->assertEquals($ltiorig['custom_parameters'], $lti['custom_parameters']); + $this->assertEquals($ltiorig['description'], $lti['description']); + $dlmsgorig = $ltiorig['messages'][0]; + $dlmsg = $lti['messages'][0]; + $this->assertEquals($dlmsgorig['type'], $dlmsg['type']); + $this->assertEquals($dlmsgorig['target_link_uri'], $dlmsg['target_link_uri']); + } +}