[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/mod/lti/amd/src/ -> external_registration.js (source)

   1  // This file is part of Moodle - http://moodle.org/
   2  //
   3  // Moodle is free software: you can redistribute it and/or modify
   4  // it under the terms of the GNU General Public License as published by
   5  // the Free Software Foundation, either version 3 of the License, or
   6  // (at your option) any later version.
   7  //
   8  // Moodle is distributed in the hope that it will be useful,
   9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  11  // GNU General Public License for more details.
  12  //
  13  // You should have received a copy of the GNU General Public License
  14  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  15  
  16  /**
  17   * Encapsules the behavior for creating a tool type and tool proxy from a
  18   * registration url in Moodle.
  19   *
  20   * Manages the UI while operations are occuring, including rendering external
  21   * registration page within the iframe.
  22   *
  23   * See template: mod_lti/external_registration
  24   *
  25   * @module     mod_lti/external_registration
  26   * @class      external_registration
  27   * @package    mod_lti
  28   * @copyright  2015 Ryan Wyllie <ryan@moodle.com>
  29   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  30   * @since      3.1
  31   */
  32  define(['jquery', 'core/ajax', 'core/notification', 'core/templates', 'mod_lti/events',
  33          'mod_lti/tool_proxy', 'mod_lti/tool_type', 'mod_lti/keys', 'core/str'],
  34          function($, ajax, notification, templates, ltiEvents, toolProxy, toolType, KEYS, str) {
  35  
  36      var SELECTORS = {
  37          EXTERNAL_REGISTRATION_CONTAINER: '#external-registration-page-container',
  38          EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER: '#external-registration-template-container',
  39          EXTERNAL_REGISTRATION_CANCEL_BUTTON: '#cancel-external-registration',
  40          TOOL_TYPE_CAPABILITIES_CONTAINER: '#tool-type-capabilities-container',
  41          TOOL_TYPE_CAPABILITIES_TEMPLATE_CONTAINER: '#tool-type-capabilities-template-container',
  42          CAPABILITIES_AGREE_CONTAINER: '.capabilities-container',
  43      };
  44  
  45      /**
  46       * Return the external registration cancel button element. This button is
  47       * the cancel button that appears while the iframe is rendered.
  48       *
  49       * @method getExternalRegistrationCancelButton
  50       * @private
  51       * @return {JQuery} jQuery object
  52       */
  53      var getExternalRegistrationCancelButton = function() {
  54          return $(SELECTORS.EXTERNAL_REGISTRATION_CANCEL_BUTTON);
  55      };
  56  
  57      /**
  58       * Return the container that holds all elements for the external registration, including
  59       * the cancel button and the iframe.
  60       *
  61       * @method getExternalRegistrationContainer
  62       * @private
  63       * @return {JQuery} jQuery object
  64       */
  65      var getExternalRegistrationContainer = function() {
  66          return $(SELECTORS.EXTERNAL_REGISTRATION_CONTAINER);
  67      };
  68  
  69      /**
  70       * Return the container that holds the external registration page template. It should
  71       * be the iframe.
  72       *
  73       * @method getExternalRegistrationTemplateContainer
  74       * @private
  75       * @return {JQuery} jQuery object
  76       */
  77      var getExternalRegistrationTemplateContainer = function() {
  78          return $(SELECTORS.EXTERNAL_REGISTRATION_TEMPLATE_CONTAINER);
  79      };
  80  
  81      /**
  82       * Return the container that holds the elements for displaying the list of capabilities
  83       * that this tool type requires. This container wraps the loading indicator and the template
  84       * container.
  85       *
  86       * @method getToolTypeCapabilitiesContainer
  87       * @private
  88       * @return {JQuery} jQuery object
  89       */
  90      var getToolTypeCapabilitiesContainer = function() {
  91          return $(SELECTORS.TOOL_TYPE_CAPABILITIES_CONTAINER);
  92      };
  93  
  94      /**
  95       * Return the container that holds the template that lists the capabilities that the
  96       * tool type will require.
  97       *
  98       * @method getToolTypeCapabilitiesTemplateContainer
  99       * @private
 100       * @return {JQuery} jQuery object
 101       */
 102      var getToolTypeCapabilitiesTemplateContainer = function() {
 103          return $(SELECTORS.TOOL_TYPE_CAPABILITIES_TEMPLATE_CONTAINER);
 104      };
 105  
 106      /**
 107       * Triggers a visual indicator to show that the capabilities section is loading.
 108       *
 109       * @method startLoadingCapabilitiesContainer
 110       * @private
 111       */
 112      var startLoadingCapabilitiesContainer = function() {
 113          getToolTypeCapabilitiesContainer().addClass('loading');
 114      };
 115  
 116      /**
 117       * Removes the visual indicator that shows the capabilities section is loading.
 118       *
 119       * @method stopLoadingCapabilitiesContainer
 120       * @private
 121       */
 122      var stopLoadingCapabilitiesContainer = function() {
 123          getToolTypeCapabilitiesContainer().removeClass('loading');
 124      };
 125  
 126      /**
 127       * Adds a visual indicator that shows the cancel button is loading.
 128       *
 129       * @method startLoadingCancel
 130       * @private
 131       */
 132      var startLoadingCancel = function() {
 133          getExternalRegistrationCancelButton().addClass('loading');
 134      };
 135  
 136      /**
 137       * Adds a visual indicator that shows the cancel button is loading.
 138       *
 139       * @method startLoadingCancel
 140       * @private
 141       */
 142      var stopLoadingCancel = function() {
 143          getExternalRegistrationCancelButton().removeClass('loading');
 144      };
 145  
 146      /**
 147       * Stops displaying the tool type capabilities container.
 148       *
 149       * @method hideToolTypeCapabilitiesContainer
 150       * @private
 151       */
 152      var hideToolTypeCapabilitiesContainer = function() {
 153          getToolTypeCapabilitiesContainer().addClass('hidden');
 154      };
 155  
 156      /**
 157       * Displays the tool type capabilities container.
 158       *
 159       * @method showToolTypeCapabilitiesContainer
 160       * @private
 161       */
 162      var showToolTypeCapabilitiesContainer = function() {
 163          getToolTypeCapabilitiesContainer().removeClass('hidden');
 164      };
 165  
 166      /**
 167       * Stops displaying the external registration content.
 168       *
 169       * @method hideExternalRegistrationContent
 170       * @private
 171       */
 172      var hideExternalRegistrationContent = function() {
 173          getExternalRegistrationContainer().addClass('hidden');
 174      };
 175  
 176      /**
 177       * Displays the external registration content.
 178       *
 179       * @method showExternalRegistrationContent
 180       * @private
 181       */
 182      var showExternalRegistrationContent = function() {
 183          getExternalRegistrationContainer().removeClass('hidden');
 184      };
 185  
 186      /**
 187       * Save the given tool proxy id on the DOM.
 188       *
 189       * @method setToolProxyId
 190       * @private
 191       * @param {Integer} id Tool proxy ID
 192       */
 193      var setToolProxyId = function(id) {
 194          var button = getExternalRegistrationCancelButton();
 195          button.attr('data-tool-proxy-id', id);
 196      };
 197  
 198      /**
 199       * Return the saved tool proxy id.
 200       *
 201       * @method getToolProxyId
 202       * @private
 203       * @return {String} Tool proxy ID
 204       */
 205      var getToolProxyId = function() {
 206          var button = getExternalRegistrationCancelButton();
 207          return button.attr('data-tool-proxy-id');
 208      };
 209  
 210      /**
 211       * Remove the saved tool proxy id.
 212       *
 213       * @method clearToolProxyId
 214       * @private
 215       */
 216      var clearToolProxyId = function() {
 217          var button = getExternalRegistrationCancelButton();
 218          button.removeAttr('data-tool-proxy-id');
 219      };
 220  
 221      /**
 222       * Returns true if a tool proxy id has been recorded.
 223       *
 224       * @method hasToolProxyId
 225       * @private
 226       * @return {Boolean}
 227       */
 228      var hasToolProxyId = function() {
 229          return getToolProxyId() ? true : false;
 230      };
 231  
 232      /**
 233       * Checks if this process has created a tool proxy within
 234       * Moodle yet.
 235       *
 236       * @method hasCreatedToolProxy
 237       * @private
 238       * @return {Boolean}
 239       */
 240      var hasCreatedToolProxy = function() {
 241          var button = getExternalRegistrationCancelButton();
 242          return button.attr('data-tool-proxy-new') && hasToolProxyId();
 243      };
 244  
 245      /**
 246       * Records that this process has created a tool proxy.
 247       *
 248       * @method setProxyAsNew
 249       * @private
 250       * @return {Boolean}
 251       */
 252      var setProxyAsNew = function() {
 253          var button = getExternalRegistrationCancelButton();
 254          return button.attr('data-tool-proxy-new', "new");
 255      };
 256  
 257      /**
 258       * Records that this process has not created a tool proxy.
 259       *
 260       * @method setProxyAsOld
 261       * @private
 262       * @return {Boolean}
 263       */
 264      var setProxyAsOld = function() {
 265          var button = getExternalRegistrationCancelButton();
 266          return button.removeAttr('data-tool-proxy-new');
 267      };
 268  
 269      /**
 270       * Gets the external registration request required to be sent to the external
 271       * registration page using a form.
 272       *
 273       * See mod_lti/tool_proxy_registration_form template.
 274       *
 275       * @method getRegistrationRequest
 276       * @private
 277       * @param {Integer} id Tool Proxy ID
 278       * @return {Promise} jQuery Deferred object
 279       */
 280      var getRegistrationRequest = function(id) {
 281          var request = {
 282              methodname: 'mod_lti_get_tool_proxy_registration_request',
 283              args: {
 284                  id: id
 285              }
 286          };
 287  
 288          return ajax.call([request])[0];
 289      };
 290  
 291      /**
 292       * Cancel an in progress external registration. This will perform any necessary
 293       * clean up of tool proxies and return the page section back to the home section.
 294       *
 295       * @method cancelRegistration
 296       * @private
 297       * @return {Promise} jQuery Deferred object
 298       */
 299      var cancelRegistration = function() {
 300          startLoadingCancel();
 301          var promise = $.Deferred();
 302  
 303          // If we've created a proxy as part of this process then
 304          // we need to delete it to clean up the data in the back end.
 305          if (hasCreatedToolProxy()) {
 306              var id = getToolProxyId();
 307              toolProxy.delete(id).done(function() {
 308                  promise.resolve();
 309              }).fail(function(failure) {
 310                  promise.reject(failure);
 311              });
 312          } else {
 313              promise.resolve();
 314          }
 315  
 316          promise.done(function() {
 317              // Return to the original page.
 318              finishExternalRegistration();
 319              stopLoadingCancel();
 320          }).fail(function(failure) {
 321              notification.exception(failure);
 322              finishExternalRegistration();
 323              stopLoadingCancel();
 324              str.get_string('failedtodeletetoolproxy', 'mod_lti').done(function(s) {
 325                  var feedback = {
 326                      message: s,
 327                      error: true
 328                  };
 329                  $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);
 330              }).fail(notification.exception);
 331          });
 332  
 333          return promise;
 334      };
 335  
 336      /**
 337       * Load the external registration template and render it in the DOM and display it.
 338       *
 339       * @method renderExternalRegistrationWindow
 340       * @private
 341       * @param {Object} registrationRequest
 342       * @return {Promise} jQuery Deferred object
 343       */
 344      var renderExternalRegistrationWindow = function(registrationRequest) {
 345          var promise = templates.render('mod_lti/tool_proxy_registration_form', registrationRequest);
 346  
 347          promise.done(function(html, js) {
 348              // Show the external registration page in an iframe.
 349              var container = getExternalRegistrationTemplateContainer();
 350              container.append(html);
 351              templates.runTemplateJS(js);
 352  
 353              container.find('form').submit();
 354              showExternalRegistrationContent();
 355          }).fail(notification.exception);
 356  
 357          return promise;
 358      };
 359  
 360      /**
 361       * Send a request to Moodle server to set the state of the tool type to configured (active).
 362       *
 363       * @method setTypeStatusActive
 364       * @private
 365       * @param {Object} typeData A set of data representing a type, as returned by a request to get a type
 366       *               from the Moodle server.
 367       * @return {Promise} jQuery Deferred object
 368       */
 369      var setTypeStatusActive = function(typeData) {
 370          return toolType.update({
 371              id: typeData.id,
 372              state: toolType.constants.state.configured
 373          });
 374      };
 375  
 376      /**
 377       * Render and display an agreement page for the user to acknowledge the list of capabilities
 378       * (groups of data) that the external tool requires in order to work. If the user agrees then
 379       * we will activate the tool so that it is immediately available. If they don't agree then
 380       * the tool remains in a pending state within Moodle until agreement is given.
 381       *
 382       * @method promptForToolTypeCapabilitiesAgreement
 383       * @private
 384       * @param {Object} typeData A set of data representing a type, as returned by a request to get a type
 385       *               from the Moodle server.
 386       * @return {Promise} jQuery Deferred object
 387       */
 388      var promptForToolTypeCapabilitiesAgreement = function(typeData) {
 389          var promise = $.Deferred();
 390  
 391          templates.render('mod_lti/tool_type_capabilities_agree', typeData).done(function(html, js) {
 392              var container = getToolTypeCapabilitiesTemplateContainer();
 393  
 394              hideExternalRegistrationContent();
 395              showToolTypeCapabilitiesContainer();
 396  
 397              templates.replaceNodeContents(container, html, js);
 398  
 399              var choiceContainer = container.find(SELECTORS.CAPABILITIES_AGREE_CONTAINER);
 400  
 401              // The user agrees to allow the tool to use the groups of data so we can go
 402              // ahead and activate it for them so that it can be used straight away.
 403              choiceContainer.on(ltiEvents.CAPABILITIES_AGREE, function() {
 404                  startLoadingCapabilitiesContainer();
 405                  setTypeStatusActive(typeData).always(function() {
 406                      stopLoadingCapabilitiesContainer();
 407                      container.empty();
 408                      promise.resolve();
 409                  });
 410              });
 411  
 412              // The user declines to let the tool use the data. In this case we leave
 413              // the tool as pending and they can delete it using the main screen if they
 414              // wish.
 415              choiceContainer.on(ltiEvents.CAPABILITIES_DECLINE, function() {
 416                  container.empty();
 417                  promise.resolve();
 418              });
 419          }).fail(promise.reject);
 420  
 421          promise.done(function() {
 422              hideToolTypeCapabilitiesContainer();
 423          }).fail(notification.exception);
 424  
 425          return promise;
 426      };
 427  
 428      /**
 429       * Send a request to the Moodle server to create a tool proxy using the registration URL the user
 430       * has provided. The proxy is required for the external registration page to work correctly.
 431       *
 432       * After the proxy is created the external registration page is rendered within an iframe for the user
 433       * to complete the registration in the external page.
 434       *
 435       * If the tool proxy creation fails then we redirect the page section back to the home section and
 436       * display the error, rather than rendering the external registration page.
 437       *
 438       * @method createAndRegisterToolProxy
 439       * @private
 440       * @param {String} url Tool registration URL to register
 441       * @return {Promise} jQuery Deferred object
 442       */
 443      var createAndRegisterToolProxy = function(url) {
 444          var promise = $.Deferred();
 445  
 446          if (!url || url === "") {
 447              // No URL has been input so do nothing.
 448              promise.resolve();
 449          } else {
 450              // A tool proxy needs to exist before the external page is rendered because
 451              // the external page sends requests back to Moodle for information that is stored
 452              // in the proxy.
 453              toolProxy.create({regurl: url})
 454                  .done(function(result) {
 455                          // Note that it's a new proxy so we will always clean it up.
 456                          setProxyAsNew();
 457                          promise = registerProxy(result.id);
 458                      })
 459                  .fail(function(exception) {
 460                          // Clean up.
 461                          cancelRegistration();
 462                          // Let the user know what the error is.
 463                          var feedback = {
 464                              message: exception.message,
 465                              error: true
 466                          };
 467                          $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);
 468                          promise.reject(exception);
 469                      });
 470          }
 471  
 472          return promise;
 473      };
 474  
 475      /**
 476       * Loads the window to register a proxy, given an ID.
 477       *
 478       * @method registerProxy
 479       * @private
 480       * @param {Integer} id Proxy id to register
 481       * @return {Promise} jQuery Deferred object to fail or resolve
 482       */
 483      var registerProxy = function(id) {
 484          var promise = $.Deferred();
 485          // Save the id on the DOM to cleanup later.
 486          setToolProxyId(id);
 487  
 488          // There is a specific set of data needed to send to the external registration page
 489          // in a form, so let's get it from our server.
 490          getRegistrationRequest(id)
 491              .done(function(registrationRequest) {
 492                      renderExternalRegistrationWindow(registrationRequest)
 493                          .done(function() {
 494                                  promise.resolve();
 495                              })
 496                          .fail(promise.fail);
 497                  })
 498              .fail(promise.fail);
 499  
 500          return promise;
 501      };
 502  
 503      /**
 504       * Complete the registration process, clean up any left over data and
 505       * trigger the appropriate events.
 506       *
 507       * @method finishExternalRegistration
 508       * @private
 509       */
 510      var finishExternalRegistration = function() {
 511          if (hasToolProxyId()) {
 512              clearToolProxyId();
 513          }
 514          setProxyAsOld(false);
 515  
 516          hideExternalRegistrationContent();
 517          var container = getExternalRegistrationTemplateContainer();
 518          container.empty();
 519  
 520          $(document).trigger(ltiEvents.STOP_EXTERNAL_REGISTRATION);
 521      };
 522  
 523      /**
 524       * Sets up the listeners for user interaction on the page.
 525       *
 526       * @method registerEventListeners
 527       * @private
 528       */
 529      var registerEventListeners = function() {
 530  
 531          $(document).on(ltiEvents.START_EXTERNAL_REGISTRATION, function(event, data) {
 532                  if (!data) {
 533                      return;
 534                  }
 535                  if (data.url) {
 536                      createAndRegisterToolProxy(data.url);
 537                  }
 538                  if (data.proxyid) {
 539                      registerProxy(data.proxyid);
 540                  }
 541              });
 542  
 543          var cancelExternalRegistrationButton = getExternalRegistrationCancelButton();
 544          cancelExternalRegistrationButton.click(function(e) {
 545              e.preventDefault();
 546              cancelRegistration();
 547          });
 548          cancelExternalRegistrationButton.keypress(function(e) {
 549              if (!e.metaKey && !e.shiftKey && !e.altKey && !e.ctrlKey) {
 550                  if (e.keyCode == KEYS.ENTER || e.keyCode == KEYS.SPACE) {
 551                      e.preventDefault();
 552                      cancelRegistration();
 553                  }
 554              }
 555          });
 556  
 557          // This is gross but necessary due to isolated jQuery scopes between
 558          // child iframe and parent windows. There is no other way to communicate.
 559          //
 560          // This function gets called by the moodle page that received the redirect
 561          // from the external registration page and handles the external page's returned
 562          // parameters.
 563          //
 564          // See AMD module mod_lti/external_registration_return.
 565          window.triggerExternalRegistrationComplete = function(data) {
 566              var promise = $.Deferred();
 567              var feedback = {
 568                  message: "",
 569                  error: false
 570              };
 571  
 572              if (data.status == "success") {
 573                  str.get_string('successfullycreatedtooltype', 'mod_lti').done(function(s) {
 574                      feedback.message = s;
 575                  }).fail(notification.exception);
 576  
 577                  // Trigger appropriate events when we've completed the necessary requests.
 578                  promise.done(function() {
 579                      finishExternalRegistration();
 580                      $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);
 581                      $(document).trigger(ltiEvents.NEW_TOOL_TYPE);
 582                  }).fail(notification.exception);
 583  
 584                  // We should have created a tool proxy by this point.
 585                  if (hasCreatedToolProxy()) {
 586                      var proxyId = getToolProxyId();
 587  
 588                      // We need the list of types that are linked to this proxy. We're assuming it'll
 589                      // only be one because this process creates a one-to-one type->proxy.
 590                      toolType.getFromToolProxyId(proxyId).done(function(types) {
 591                          if (types && types.length) {
 592                              // There should only be one result.
 593                              var typeData = types[0];
 594  
 595                              // Check if the external tool required access to any Moodle data (users, courses etc).
 596                              if (typeData.hascapabilitygroups) {
 597                                  // If it did then we ask the user to agree to those groups before the type is
 598                                  // activated (i.e. can be used in Moodle).
 599                                  promptForToolTypeCapabilitiesAgreement(typeData).always(function() {
 600                                      promise.resolve();
 601                                  });
 602                              } else {
 603                                  promise.resolve();
 604                              }
 605                          } else {
 606                              promise.resolve();
 607                          }
 608                      }).fail(function() {
 609                          promise.resolve();
 610                      });
 611                  }
 612              } else {
 613                  // Anything other than success is failure.
 614                  feedback.message = data.error;
 615                  feedback.error = true;
 616  
 617                  // Cancel registration to clean up any proxies and tools that were
 618                  // created.
 619                  promise.done(function() {
 620                      cancelRegistration().always(function() {
 621                          $(document).trigger(ltiEvents.REGISTRATION_FEEDBACK, feedback);
 622                      });
 623                  }).fail(notification.exception);
 624  
 625                  promise.resolve();
 626              }
 627  
 628              return promise;
 629          };
 630      };
 631  
 632      return /** @alias module:mod_lti/external_registration */ {
 633  
 634          /**
 635           * Initialise this module.
 636           */
 637          init: function() {
 638              registerEventListeners();
 639          }
 640      };
 641  });


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1