| [ Index ] |
PHP Cross Reference of Moodle 310 |
[Summary view] [Print] [Text view]
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 });
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 22 11:59:49 2025 | Cross-referenced by PHPXref 0.7.1 |