diff --git a/lib/ajax/service-nologin.php b/lib/ajax/service-nologin.php index 7120adec7f2..e8f82947f93 100644 --- a/lib/ajax/service-nologin.php +++ b/lib/ajax/service-nologin.php @@ -28,4 +28,6 @@ */ define('NO_MOODLE_COOKIES', true); +define('ALLOW_GET_PARAMETERS', true); + require_once('service.php'); diff --git a/lib/ajax/service.php b/lib/ajax/service.php index caaf86c66f9..3bcb8e9621e 100644 --- a/lib/ajax/service.php +++ b/lib/ajax/service.php @@ -38,9 +38,24 @@ require_once($CFG->libdir . '/externallib.php'); define('PREFERRED_RENDERER_TARGET', RENDERER_TARGET_GENERAL); -$rawjson = file_get_contents('php://input'); +$arguments = ''; +$cacherequest = false; +if (defined('ALLOW_GET_PARAMETERS')) { + $arguments = optional_param('args', '', PARAM_RAW); + $cachekey = optional_param('cachekey', '', PARAM_INT); + if ($cachekey && $cachekey > 0 && $cachekey <= time()) { + $cacherequest = true; + } +} + +// Either we are not allowing GET parameters or we didn't use GET because +// we did not pass a cache key or the URL was too long. +if (empty($arguments)) { + $arguments = file_get_contents('php://input'); +} + +$requests = json_decode($arguments, true); -$requests = json_decode($rawjson, true); if ($requests === null) { $lasterror = json_last_error_msg(); throw new coding_exception('Invalid json in request: ' . $lasterror); @@ -54,6 +69,7 @@ $settings->set_fileurl(true); $settings->set_filter(true); $settings->set_raw(false); +$haserror = false; foreach ($requests as $request) { $response = array(); $methodname = clean_param($request['methodname'], PARAM_ALPHANUMEXT); @@ -64,7 +80,19 @@ foreach ($requests as $request) { $responses[$index] = $response; if ($response['error']) { // Do not process the remaining requests. + $haserror = true; break; } } + +if ($cacherequest && !$haserror) { + // 90 days only - based on Moodle point release cadence being every 3 months. + $lifetime = 60 * 60 * 24 * 90; + + header('Expires: '. gmdate('D, d M Y H:i:s', time() + $lifetime) .' GMT'); + header('Pragma: '); + header('Cache-Control: public, max-age=' . $lifetime . ', immutable'); + header('Accept-Ranges: none'); +} + echo json_encode($responses); diff --git a/lib/amd/build/ajax.min.js b/lib/amd/build/ajax.min.js index 2877ff78ac8..e8ad0a854b2 100644 --- a/lib/amd/build/ajax.min.js +++ b/lib/amd/build/ajax.min.js @@ -1 +1 @@ -define(["jquery","core/config","core/log","core/url"],function(a,b,c,d){var e=!1,f=function(a){var b,c,e,f=this,g=null,h=0;if(a.error)for(;hq?(s.type="POST",s.data=m):u=v}return d?a.ajax(u,s).done(f).fail(g):(s.success=f,s.error=g,a.ajax(u,s)),n}}}); \ No newline at end of file diff --git a/lib/amd/src/ajax.js b/lib/amd/src/ajax.js index ea1669a95e1..389680f49ff 100644 --- a/lib/amd/src/ajax.js +++ b/lib/amd/src/ajax.js @@ -137,9 +137,13 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo * @param {Boolean} nosessionupdate Optional, defaults to false. * If true, the timemodified for the session will not be updated. * @param {Integer} timeout number of milliseconds to wait for a response. Defaults to no limit. + * @param {Integer} cachekey This is used in order to identify the request. If this id changes then we + * will be sending a different URL and any caching (eg. browser, proxy) knows that it + * should perform another request and not use the cache. Note - this variable is only + * used when we are calling 'service-nologin.php'. See MDL-65794. * @return {Promise[]} Array of promises that will be resolved when the ajax call returns. */ - call: function(requests, async, loginrequired, nosessionupdate, timeout) { + call: function(requests, async, loginrequired, nosessionupdate, timeout, cachekey) { $(window).bind('beforeunload', function() { unloading = true; }); @@ -149,6 +153,8 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo methodInfo = [], requestInfo = ''; + var maxUrlLength = 2000; + if (typeof loginrequired === "undefined") { loginrequired = true; } @@ -158,6 +164,16 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo if (typeof timeout === 'undefined') { timeout = 0; } + if (typeof cachekey === 'undefined') { + cachekey = null; + } else { + cachekey = parseInt(cachekey); + if (cachekey <= 0) { + cachekey = null; + } else if (!cachekey) { + cachekey = null; + } + } if (typeof nosessionupdate === "undefined") { nosessionupdate = false; @@ -193,7 +209,6 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo ajaxRequestData = JSON.stringify(ajaxRequestData); var settings = { type: 'POST', - data: ajaxRequestData, context: requests, dataType: 'json', processData: false, @@ -207,6 +222,10 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo if (!loginrequired) { script = 'service-nologin.php'; url += script + '?info=' + requestInfo; + if (cachekey) { + url += '&cachekey=' + cachekey; + settings.type = 'GET'; + } } else { url += script + '?sesskey=' + config.sesskey + '&info=' + requestInfo; } @@ -215,6 +234,19 @@ define(['jquery', 'core/config', 'core/log', 'core/url'], function($, config, Lo url += '&nosessionupdate=true'; } + if (settings.type === 'POST') { + settings.data = ajaxRequestData; + } else { + var urlUseGet = url + '&args=' + encodeURIComponent(ajaxRequestData); + + if (urlUseGet.length > maxUrlLength) { + settings.type = 'POST'; + settings.data = ajaxRequestData; + } else { + url = urlUseGet; + } + } + // Jquery deprecated done and fail with async=false so we need to do this 2 ways. if (async) { $.ajax(url, settings) diff --git a/webservice/upgrade.txt b/webservice/upgrade.txt index d5b959d5c5a..407c12a1f23 100644 --- a/webservice/upgrade.txt +++ b/webservice/upgrade.txt @@ -3,6 +3,13 @@ information provided here is intended especially for developers. This information is intended for authors of webservices, not people writing webservice clients. +=== 3.8 === + +* Ajax calls can now specify a cache key. This allows for better caching capabilities on servers. If a cache key + is passed and the web service call does not require the user to be logged in we will attempt to use GET for the + request. This allows for things like proxy caching on URLs. The cache key must be changed if we do not want to + retrieve what has been cached and want to perform the request again. + === 3.7 === * External function core_webservice_external::get_site_info() now returns the current site theme (for the user).