[ Index ]

PHP Cross Reference of Moodle 310

title

Body

[close]

/lib/amd/src/ -> ajax.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   * Standard Ajax wrapper for Moodle. It calls the central Ajax script,
  18   * which can call any existing webservice using the current session.
  19   * In addition, it can batch multiple requests and return multiple responses.
  20   *
  21   * @module     core/ajax
  22   * @copyright  2015 Damyon Wiese <damyon@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   * @since      2.9
  25   */
  26  define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Log, URL) {
  27  
  28  /**
  29   * A request to be performed.
  30   *
  31   * @typedef {object} request
  32   * @property {string} methodname The remote method to be called
  33   * @property {object} args The arguments to pass when fetching the remote content
  34   */
  35  
  36      // Keeps track of when the user leaves the page so we know not to show an error.
  37      var unloading = false;
  38  
  39      /**
  40       * Success handler. Called when the ajax call succeeds. Checks each response and
  41       * resolves or rejects the deferred from that request.
  42       *
  43       * @method requestSuccess
  44       * @private
  45       * @param {Object[]} responses Array of responses containing error, exception and data attributes.
  46       */
  47      var requestSuccess = function(responses) {
  48          // Call each of the success handlers.
  49          var requests = this,
  50              exception = null,
  51              i = 0,
  52              request,
  53              response,
  54              nosessionupdate;
  55  
  56          if (responses.error) {
  57              // There was an error with the request as a whole.
  58              // We need to reject each promise.
  59              // Unfortunately this may lead to duplicate dialogues, but each Promise must be rejected.
  60              for (; i < requests.length; i++) {
  61                  request = requests[i];
  62                  request.deferred.reject(responses);
  63              }
  64  
  65              return;
  66          }
  67  
  68          for (i = 0; i < requests.length; i++) {
  69              request = requests[i];
  70  
  71              response = responses[i];
  72              // We may not have responses for all the requests.
  73              if (typeof response !== "undefined") {
  74                  if (response.error === false) {
  75                      // Call the done handler if it was provided.
  76                      request.deferred.resolve(response.data);
  77                  } else {
  78                      exception = response.exception;
  79                      nosessionupdate = requests[i].nosessionupdate;
  80                      break;
  81                  }
  82              } else {
  83                  // This is not an expected case.
  84                  exception = new Error('missing response');
  85                  break;
  86              }
  87          }
  88          // Something failed, reject the remaining promises.
  89          if (exception !== null) {
  90              // Redirect to the login page.
  91              if (exception.errorcode === "servicerequireslogin" && !nosessionupdate) {
  92                  window.location = URL.relativeUrl("/login/index.php");
  93              } else {
  94                  requests.forEach(function(request) {
  95                      request.deferred.reject(exception);
  96                  });
  97              }
  98          }
  99      };
 100  
 101      /**
 102       * Fail handler. Called when the ajax call fails. Rejects all deferreds.
 103       *
 104       * @method requestFail
 105       * @private
 106       * @param {jqXHR} jqXHR The ajax object.
 107       * @param {string} textStatus The status string.
 108       * @param {Error|Object} exception The error thrown.
 109       */
 110      var requestFail = function(jqXHR, textStatus, exception) {
 111          // Reject all the promises.
 112          var requests = this;
 113  
 114          var i = 0;
 115          for (i = 0; i < requests.length; i++) {
 116              var request = requests[i];
 117  
 118              if (unloading) {
 119                  // No need to trigger an error because we are already navigating.
 120                  Log.error("Page unloaded.");
 121                  Log.error(exception);
 122              } else {
 123                  request.deferred.reject(exception);
 124              }
 125          }
 126      };
 127  
 128      return /** @alias module:core/ajax */ {
 129          // Public variables and functions.
 130          /**
 131           * Make a series of ajax requests and return all the responses.
 132           *
 133           * @method call
 134           * @param {request[]} requests Array of requests with each containing methodname and args properties.
 135           *                   done and fail callbacks can be set for each element in the array, or the
 136           *                   can be attached to the promises returned by this function.
 137           * @param {Boolean} [async=true] If false this function will not return until the promises are resolved.
 138           * @param {Boolean} [loginrequired=true] When false this function calls an endpoint which does not use the
 139           *                  session.
 140           *                  Note: This may only be used with external functions which have been marked as
 141           *                  `'loginrequired' => false`
 142           * @param {Boolean} [nosessionupdate=false] If true, the timemodified for the session will not be updated.
 143           * @param {Number}  [timeout] number of milliseconds to wait for a response. Defaults to no limit.
 144           * @param {Number}  [cachekey] A cache key used to improve browser-side caching.
 145           *                  Typically the same `cachekey` is used for all function calls.
 146           *                  When the key changes, this causes the URL used to perform the fetch to change, which
 147           *                  prevents the existing browser cache from being used.
 148           *                  Note: This option is only availbale when `loginrequired` is `false`.
 149           *                  See {@link https://tracker.moodle.org/browser/MDL-65794} for more information.
 150           * @return {Promise[]} The Promises for each of the supplied requests.
 151           *                  The order of the Promise matches the order of requests exactly.
 152           *
 153           * @example <caption>A simple example that you might find in a repository module</caption>
 154           *
 155           * import {call as fetchMany} from 'core/ajax';
 156           *
 157           * export const fetchMessages = timeSince => fetchMany([{methodname: 'core_message_get_messages', args: {timeSince}}])[0];
 158           *
 159           * export const fetchNotifications = timeSince => fetchMany([{
 160           *     methodname: 'core_message_get_notifications',
 161           *     args: {
 162           *         timeSince,
 163           *     }
 164           * }])[0];
 165           *
 166           * export const fetchSomethingElse = (some, params, here) => fetchMany([{
 167           *     methodname: 'core_get_something_else',
 168           *     args: {
 169           *         some,
 170           *         params,
 171           *         gohere: here,
 172           *     },
 173           * }])[0];
 174           *
 175           * @example <caption>An example of fetching a string using the cachekey parameter</caption>
 176           * import {call as fetchMany} from 'core/ajax';
 177           * import * as Notification from 'core/notification';
 178           *
 179           * export const performAction = (some, args) => {
 180           *     Promises.all(fetchMany([{methodname: 'core_get_string', args: {
 181           *         stringid: 'do_not_copy',
 182           *         component: 'core',
 183           *         lang: 'en',
 184           *         stringparams: [],
 185           *     }}], true, false, false, undefined, M.cfg.langrev))
 186           *     .then(([doNotCopyString]) => {
 187           *         window.console.log(doNotCopyString);
 188           *     })
 189           *     .catch(Notification.exception);
 190           * };
 191           *
 192           */
 193          call: function(requests, async, loginrequired, nosessionupdate, timeout, cachekey) {
 194              $(window).bind('beforeunload', function() {
 195                  unloading = true;
 196              });
 197              var ajaxRequestData = [],
 198                  i,
 199                  promises = [],
 200                  methodInfo = [],
 201                  requestInfo = '';
 202  
 203              var maxUrlLength = 2000;
 204  
 205              if (typeof loginrequired === "undefined") {
 206                  loginrequired = true;
 207              }
 208              if (typeof async === "undefined") {
 209                  async = true;
 210              }
 211              if (typeof timeout === 'undefined') {
 212                  timeout = 0;
 213              }
 214              if (typeof cachekey === 'undefined') {
 215                  cachekey = null;
 216              } else {
 217                  cachekey = parseInt(cachekey);
 218                  if (cachekey <= 0) {
 219                      cachekey = null;
 220                  } else if (!cachekey) {
 221                      cachekey = null;
 222                  }
 223              }
 224  
 225              if (typeof nosessionupdate === "undefined") {
 226                  nosessionupdate = false;
 227              }
 228              for (i = 0; i < requests.length; i++) {
 229                  var request = requests[i];
 230                  ajaxRequestData.push({
 231                      index: i,
 232                      methodname: request.methodname,
 233                      args: request.args
 234                  });
 235                  request.nosessionupdate = nosessionupdate;
 236                  request.deferred = $.Deferred();
 237                  promises.push(request.deferred.promise());
 238                  // Allow setting done and fail handlers as arguments.
 239                  // This is just a shortcut for the calling code.
 240                  if (typeof request.done !== "undefined") {
 241                      request.deferred.done(request.done);
 242                  }
 243                  if (typeof request.fail !== "undefined") {
 244                      request.deferred.fail(request.fail);
 245                  }
 246                  request.index = i;
 247                  methodInfo.push(request.methodname);
 248              }
 249  
 250              if (methodInfo.length <= 5) {
 251                  requestInfo = methodInfo.sort().join();
 252              } else {
 253                  requestInfo = methodInfo.length + '-method-calls';
 254              }
 255  
 256              ajaxRequestData = JSON.stringify(ajaxRequestData);
 257              var settings = {
 258                  type: 'POST',
 259                  context: requests,
 260                  dataType: 'json',
 261                  processData: false,
 262                  async: async,
 263                  contentType: "application/json",
 264                  timeout: timeout
 265              };
 266  
 267              var script = 'service.php';
 268              var url = config.wwwroot + '/lib/ajax/';
 269              if (!loginrequired) {
 270                  script = 'service-nologin.php';
 271                  url += script + '?info=' + requestInfo;
 272                  if (cachekey) {
 273                      url += '&cachekey=' + cachekey;
 274                      settings.type = 'GET';
 275                  }
 276              } else {
 277                  url += script + '?sesskey=' + config.sesskey + '&info=' + requestInfo;
 278              }
 279  
 280              if (nosessionupdate) {
 281                  url += '&nosessionupdate=true';
 282              }
 283  
 284              if (settings.type === 'POST') {
 285                  settings.data = ajaxRequestData;
 286              } else {
 287                  var urlUseGet = url + '&args=' + encodeURIComponent(ajaxRequestData);
 288  
 289                  if (urlUseGet.length > maxUrlLength) {
 290                      settings.type = 'POST';
 291                      settings.data = ajaxRequestData;
 292                  } else {
 293                      url = urlUseGet;
 294                  }
 295              }
 296  
 297              // Jquery deprecated done and fail with async=false so we need to do this 2 ways.
 298              if (async) {
 299                  $.ajax(url, settings)
 300                      .done(requestSuccess)
 301                      .fail(requestFail);
 302              } else {
 303                  settings.success = requestSuccess;
 304                  settings.error = requestFail;
 305                  $.ajax(url, settings);
 306              }
 307  
 308              return promises;
 309          }
 310      };
 311  });


Generated: Wed Jan 22 11:59:49 2025 Cross-referenced by PHPXref 0.7.1