MDL-54551 core: touch-up after review
[moodle.git] / lib / amd / src / ajax.js
CommitLineData
72f8324e
DW
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
9bdcf579 22 * @class ajax
72f8324e
DW
23 * @package core
24 * @copyright 2015 Damyon Wiese <damyon@moodle.com>
25 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9bdcf579 26 * @since 2.9
72f8324e 27 */
feb6a520 28define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Log, URL) {
671be2be
JO
29
30 // Keeps track of when the user leaves the page so we know not to show an error.
31 var unloading = false;
72f8324e
DW
32
33 /**
34 * Success handler. Called when the ajax call succeeds. Checks each response and
35 * resolves or rejects the deferred from that request.
36 *
9bdcf579
DW
37 * @method requestSuccess
38 * @private
72f8324e
DW
39 * @param {Object[]} responses Array of responses containing error, exception and data attributes.
40 */
41 var requestSuccess = function(responses) {
42 // Call each of the success handlers.
43 var requests = this;
44 var exception = null;
45 var i = 0;
46 var request;
47 var response;
48
fa038116
AN
49 if (responses.error) {
50 // There was an error with the request as a whole.
51 // We need to reject each promise.
52 // Unfortunately this may lead to duplicate dialogues, but each Promise must be rejected.
53 for (; i < requests.length; i++) {
54 request = requests[i];
55 request.deferred.reject(responses);
56 }
57
58 return;
59 }
60
72f8324e
DW
61 for (i = 0; i < requests.length; i++) {
62 request = requests[i];
63
64 response = responses[i];
65 // We may not have responses for all the requests.
66 if (typeof response !== "undefined") {
67 if (response.error === false) {
68 // Call the done handler if it was provided.
69 request.deferred.resolve(response.data);
70 } else {
71 exception = response.exception;
72 break;
73 }
74 } else {
75 // This is not an expected case.
76 exception = new Error('missing response');
77 break;
78 }
79 }
80 // Something failed, reject the remaining promises.
81 if (exception !== null) {
acf94de6
TR
82 // If the user isn't doing anything too important, redirect to the login page.
83 if (exception.errorcode === "servicerequireslogin") {
feb6a520 84 window.location = URL.relativeUrl("/login/index.php");
acf94de6
TR
85 } else {
86 for (; i < requests.length; i++) {
87 request = requests[i];
88 request.deferred.reject(exception);
89 }
72f8324e
DW
90 }
91 }
92 };
93
94 /**
95 * Fail handler. Called when the ajax call fails. Rejects all deferreds.
96 *
9bdcf579
DW
97 * @method requestFail
98 * @private
72f8324e
DW
99 * @param {jqXHR} jqXHR The ajax object.
100 * @param {string} textStatus The status string.
fa038116 101 * @param {Error|Object} exception The error thrown.
72f8324e 102 */
fa038116 103 var requestFail = function(jqXHR, textStatus, exception) {
72f8324e
DW
104 // Reject all the promises.
105 var requests = this;
106
107 var i = 0;
108 for (i = 0; i < requests.length; i++) {
109 var request = requests[i];
110
671be2be
JO
111 if (unloading) {
112 // No need to trigger an error because we are already navigating.
fa038116
AN
113 Log.error("Page unloaded.");
114 Log.error(exception);
671be2be 115 } else {
fa038116 116 request.deferred.reject(exception);
671be2be 117 }
72f8324e
DW
118 }
119 };
120
121 return /** @alias module:core/ajax */ {
122 // Public variables and functions.
123 /**
124 * Make a series of ajax requests and return all the responses.
9bdcf579
DW
125 *
126 * @method call
c96f55e6 127 * @param {Object[]} requests Array of requests with each containing methodname and args properties.
72f8324e
DW
128 * done and fail callbacks can be set for each element in the array, or the
129 * can be attached to the promises returned by this function.
9bdcf579
DW
130 * @param {Boolean} async Optional, defaults to true.
131 * If false - this function will not return until the promises are resolved.
ba224fb4
DW
132 * @param {Boolean} loginrequired Optional, defaults to true.
133 * If false - this function will call the faster nologin ajax script - but
134 * will fail unless all functions have been marked as 'loginrequired' => false
135 * in services.php
9bdcf579 136 * @return {Promise[]} Array of promises that will be resolved when the ajax call returns.
72f8324e 137 */
ba224fb4 138 call: function(requests, async, loginrequired) {
671be2be
JO
139 $(window).bind('beforeunload', function() {
140 unloading = true;
141 });
72f8324e
DW
142 var ajaxRequestData = [],
143 i,
c1ac8e7f
TH
144 promises = [],
145 methodInfo = [],
146 requestInfo = '';
9bdcf579 147
4093d643
DW
148 if (typeof loginrequired === "undefined") {
149 loginrequired = true;
150 }
9bdcf579
DW
151 if (typeof async === "undefined") {
152 async = true;
153 }
72f8324e
DW
154 for (i = 0; i < requests.length; i++) {
155 var request = requests[i];
156 ajaxRequestData.push({
157 index: i,
158 methodname: request.methodname,
159 args: request.args
160 });
161 request.deferred = $.Deferred();
162 promises.push(request.deferred.promise());
163 // Allow setting done and fail handlers as arguments.
164 // This is just a shortcut for the calling code.
165 if (typeof request.done !== "undefined") {
166 request.deferred.done(request.done);
167 }
168 if (typeof request.fail !== "undefined") {
169 request.deferred.fail(request.fail);
170 }
171 request.index = i;
c1ac8e7f
TH
172 methodInfo.push(request.methodname);
173 }
174
175 if (methodInfo.length <= 5) {
176 requestInfo = methodInfo.sort().join();
177 } else {
178 requestInfo = methodInfo.length + '-method-calls';
72f8324e
DW
179 }
180
181 ajaxRequestData = JSON.stringify(ajaxRequestData);
182 var settings = {
183 type: 'POST',
184 data: ajaxRequestData,
185 context: requests,
186 dataType: 'json',
9bdcf579 187 processData: false,
023d6a67
BK
188 async: async,
189 contentType: "application/json"
72f8324e
DW
190 };
191
c1ac8e7f 192 var script = 'service.php';
ba224fb4 193 if (!loginrequired) {
c1ac8e7f 194 script = 'service-nologin.php';
ba224fb4 195 }
c1ac8e7f
TH
196 var url = config.wwwroot + '/lib/ajax/' + script +
197 '?sesskey=' + config.sesskey + '&info=' + requestInfo;
ba224fb4 198
9bdcf579
DW
199 // Jquery deprecated done and fail with async=false so we need to do this 2 ways.
200 if (async) {
c1ac8e7f 201 $.ajax(url, settings)
9bdcf579
DW
202 .done(requestSuccess)
203 .fail(requestFail);
204 } else {
205 settings.success = requestSuccess;
206 settings.error = requestFail;
c1ac8e7f 207 $.ajax(url, settings);
9bdcf579 208 }
72f8324e
DW
209
210 return promises;
211 }
212 };
213});