From ba224fb42c41a5b14309adb6c609715c467116a3 Mon Sep 17 00:00:00 2001 From: Damyon Wiese Date: Thu, 16 Jul 2015 14:14:33 +0800 Subject: [PATCH] MDL-50783 Ajax: Configure how to call a webservice through db/service.php Now the db/service.php array can contain these extra keys to provide information on how a webservice may be called: 'ajax' => true (Default is false) Replaces the xx_is_allowed_from_ajax callback. 'loginrequired' => false (Default is true) Means that this webservice can be called through lib/ajax/service-nosession.php which sets NO_MOODLE_COOKIES to true (faster). This is only safe for webservices returning static public data (e.g. get_string). --- .../templatelibrary/amd/build/display.min.js | 2 +- .../templatelibrary/amd/build/search.min.js | 2 +- admin/tool/templatelibrary/amd/src/display.js | 2 +- admin/tool/templatelibrary/amd/src/search.js | 2 +- .../tool/templatelibrary/classes/external.php | 18 ----------- admin/tool/templatelibrary/db/services.php | 6 +++- lib/ajax/service-nologin.php | 31 +++++++++++++++++++ lib/ajax/service.php | 5 +-- lib/amd/build/ajax.min.js | 2 +- lib/amd/build/str.min.js | 2 +- lib/amd/build/templates.min.js | 2 +- lib/amd/src/ajax.js | 15 +++++++-- lib/amd/src/str.js | 2 +- lib/amd/src/templates.js | 2 +- lib/classes/output/external.php | 10 ------ lib/db/services.php | 14 ++++++--- lib/external/externallib.php | 31 ------------------- lib/externallib.php | 3 ++ tag/classes/external.php | 20 +----------- 19 files changed, 74 insertions(+), 97 deletions(-) create mode 100644 lib/ajax/service-nologin.php diff --git a/admin/tool/templatelibrary/amd/build/display.min.js b/admin/tool/templatelibrary/amd/build/display.min.js index a5bf8ab0ec3..49c0afe59a3 100644 --- a/admin/tool/templatelibrary/amd/build/display.min.js +++ b/admin/tool/templatelibrary/amd/build/display.min.js @@ -1 +1 @@ -define(["jquery","core/ajax","core/log","core/notification","core/templates","core/config","core/str"],function(a,b,c,d,e,f,g){var h=function(a,b){var c="@template "+b,d=0,e=[];if(e=a.match(/{{!([\s\S]*?)}}/g),null!==e)for(d=0;d 'List/search templates by component.', 'type' => 'read', 'capabilities'=> '', + 'ajax' => true, + 'loginrequired' => false, ), 'tool_templatelibrary_load_canonical_template' => array( 'classname' => 'tool_templatelibrary\external', 'methodname' => 'load_canonical_template', 'description' => 'Load a canonical template by name (not the theme overidden one).', - 'type' => 'read' + 'type' => 'read', + 'ajax' => true, + 'loginrequired' => false, ), ); diff --git a/lib/ajax/service-nologin.php b/lib/ajax/service-nologin.php new file mode 100644 index 00000000000..7120adec7f2 --- /dev/null +++ b/lib/ajax/service-nologin.php @@ -0,0 +1,31 @@ +. + +/** + * This file is used to call any registered externallib function in Moodle. + * + * It will process more than one request and return more than one response if required. + * It is recommended to add webservice functions and re-use this script instead of + * writing any new custom ajax scripts. + * + * @since Moodle 2.9 + * @package core + * @copyright 2015 Damyon Wiese + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +define('NO_MOODLE_COOKIES', true); +require_once('service.php'); diff --git a/lib/ajax/service.php b/lib/ajax/service.php index 166b8a84e59..366f1ad16ff 100644 --- a/lib/ajax/service.php +++ b/lib/ajax/service.php @@ -32,8 +32,6 @@ define('AJAX_SCRIPT', true); require_once(dirname(__FILE__) . '/../../config.php'); require_once($CFG->libdir . '/externallib.php'); -require_sesskey(); - $rawjson = file_get_contents('php://input'); $requests = json_decode($rawjson, true); @@ -54,6 +52,7 @@ foreach ($requests as $request) { $externalfunctioninfo = external_function_info($methodname); if (!$externalfunctioninfo->allowed_from_ajax) { + error_log('This external function is not available to ajax. Failed to call "' . $methodname . '"'); throw new moodle_exception('servicenotavailable', 'webservice'); } @@ -62,6 +61,8 @@ foreach ($requests as $request) { if (!isloggedin()) { error_log('This external function is not available to public users. Failed to call "' . $methodname . '"'); throw new moodle_exception('servicenotavailable', 'webservice'); + } else { + require_sesskey(); } } diff --git a/lib/amd/build/ajax.min.js b/lib/amd/build/ajax.min.js index 2a80dfe3166..7ed899b1709 100644 --- a/lib/amd/build/ajax.min.js +++ b/lib/amd/build/ajax.min.js @@ -1 +1 @@ -define(["jquery","core/config"],function(a,b){var c=function(a){var b,c,d=this,e=null,f=0;for(f=0;f0&&(g=e.shift().trim()),e.length>0&&(h=e.shift().trim()),e.length>0&&(j=e.join(",").trim());var k=f.imageUrl(g,h),l={attributes:[{name:"src",value:k},{name:"alt",value:c(j)},{name:"class",value:"smallicon"}]},n=i[m+"/core/pix_icon"];return d=a.render(n,l,o),d.trim()},o=function(a){var b="";return u(a,!1).done(function(a){b=a}).fail(e.exception),b},p=function(a,b){return k.push(b(a,this)),""},q=function(a,b){var c=a.split(","),d="",e="",f="";c.length>0&&(d=c.shift().trim()),c.length>0&&(e=c.shift().trim()),c.length>0&&(f=c.join(",").trim()),""!==f&&(f=b(f,this)),0===f.indexOf("{")&&0!==f.indexOf("{{")&&(f=JSON.parse(f));var g=j.length;return j.push({key:d,component:e,param:f}),"{{_s"+g+"}}"},r=function(a,b){m=b,j=[],k=[],a.uniqid=l++,a.str=function(){return q},a.pix=function(){return n},a.js=function(){return p},a.globals={config:g},a.currentTheme=b},s=function(a){var b="";k.length>0&&(b=k.join(";\n"));var c=0;for(c=0;c0?d.get_strings(j).done(function(a){var c;for(c=0;c").attr("type","text/javascript").html(a);b("head").append(c)}}}); \ No newline at end of file +define(["core/mustache","jquery","core/ajax","core/str","core/notification","core/url","core/config","core/localstorage"],function(a,b,c,d,e,f,g,h){var i={},j=[],k=[],l=1,m="",n=function(b,c){var d,e=b.split(","),g="",h="",j="";e.length>0&&(g=e.shift().trim()),e.length>0&&(h=e.shift().trim()),e.length>0&&(j=e.join(",").trim());var k=f.imageUrl(g,h),l={attributes:[{name:"src",value:k},{name:"alt",value:c(j)},{name:"class",value:"smallicon"}]},n=i[m+"/core/pix_icon"];return d=a.render(n,l,o),d.trim()},o=function(a){var b="";return u(a,!1).done(function(a){b=a}).fail(e.exception),b},p=function(a,b){return k.push(b(a,this)),""},q=function(a,b){var c=a.split(","),d="",e="",f="";c.length>0&&(d=c.shift().trim()),c.length>0&&(e=c.shift().trim()),c.length>0&&(f=c.join(",").trim()),""!==f&&(f=b(f,this)),0===f.indexOf("{")&&0!==f.indexOf("{{")&&(f=JSON.parse(f));var g=j.length;return j.push({key:d,component:e,param:f}),"{{_s"+g+"}}"},r=function(a,b){m=b,j=[],k=[],a.uniqid=l++,a.str=function(){return q},a.pix=function(){return n},a.js=function(){return p},a.globals={config:g},a.currentTheme=b},s=function(a){var b="";k.length>0&&(b=k.join(";\n"));var c=0;for(c=0;c0?d.get_strings(j).done(function(a){var c;for(c=0;c").attr("type","text/javascript").html(a);b("head").append(c)}}}); \ No newline at end of file diff --git a/lib/amd/src/ajax.js b/lib/amd/src/ajax.js index 2966cb4d877..6b497c35f44 100644 --- a/lib/amd/src/ajax.js +++ b/lib/amd/src/ajax.js @@ -104,9 +104,13 @@ define(['jquery', 'core/config'], function($, config) { * can be attached to the promises returned by this function. * @param {Boolean} async Optional, defaults to true. * If false - this function will not return until the promises are resolved. + * @param {Boolean} loginrequired Optional, defaults to true. + * If false - this function will call the faster nologin ajax script - but + * will fail unless all functions have been marked as 'loginrequired' => false + * in services.php * @return {Promise[]} Array of promises that will be resolved when the ajax call returns. */ - call: function(requests, async) { + call: function(requests, async, loginrequired) { var ajaxRequestData = [], i, promises = []; @@ -144,15 +148,20 @@ define(['jquery', 'core/config'], function($, config) { async: async }; + var script = config.wwwroot + '/lib/ajax/service.php?sesskey=' + config.sesskey; + if (!loginrequired) { + script = config.wwwroot + '/lib/ajax/service-nologin.php?sesskey=' + config.sesskey; + } + // Jquery deprecated done and fail with async=false so we need to do this 2 ways. if (async) { - $.ajax(config.wwwroot + '/lib/ajax/service.php?sesskey=' + config.sesskey, settings) + $.ajax(script, settings) .done(requestSuccess) .fail(requestFail); } else { settings.success = requestSuccess; settings.error = requestFail; - $.ajax(config.wwwroot + '/lib/ajax/service.php?sesskey=' + config.sesskey, settings); + $.ajax(script, settings); } return promises; diff --git a/lib/amd/src/str.js b/lib/amd/src/str.js index d66c164777f..76830d380fd 100644 --- a/lib/amd/src/str.js +++ b/lib/amd/src/str.js @@ -140,7 +140,7 @@ define(['jquery', 'core/ajax', 'core/localstorage'], function($, ajax, storage) }); } - var deferreds = ajax.call(ajaxrequests); + var deferreds = ajax.call(ajaxrequests, true, false); $.when.apply(null, deferreds).done( function() { // Turn the list of arguments (unknown length) into a real array. diff --git a/lib/amd/src/templates.js b/lib/amd/src/templates.js index 93ed7d111bc..a152e03fd94 100644 --- a/lib/amd/src/templates.js +++ b/lib/amd/src/templates.js @@ -310,7 +310,7 @@ define([ 'core/mustache', template: name, themename: currentThemeName } - }], async); + }], async, false); promises[0].done( function (templateSource) { diff --git a/lib/classes/output/external.php b/lib/classes/output/external.php index 17b1ca74712..a76248edf96 100644 --- a/lib/classes/output/external.php +++ b/lib/classes/output/external.php @@ -55,16 +55,6 @@ class external extends external_api { ); } - /** - * Can this function be called directly from ajax? - * - * @return boolean - * @since Moodle 2.9 - */ - public static function load_template_is_allowed_from_ajax() { - return true; - } - /** * Return a mustache template, and all the strings it requires. * diff --git a/lib/db/services.php b/lib/db/services.php index 415f0fd5d94..5c2ad25e1ce 100644 --- a/lib/db/services.php +++ b/lib/db/services.php @@ -975,7 +975,8 @@ $functions = array( 'classpath' => 'lib/external/externallib.php', 'description' => 'Return a translated string - similar to core get_string() call', 'type' => 'read', - 'loginrequired' => false + 'loginrequired' => false, + 'ajax' => true, ), 'core_get_strings' => array( @@ -984,7 +985,8 @@ $functions = array( 'classpath' => 'lib/external/externallib.php', 'description' => 'Return some translated strings - like several core get_string() calls', 'type' => 'read', - 'loginrequired' => false + 'loginrequired' => false, + 'ajax' => true, ), 'core_get_component_strings' => array( @@ -994,7 +996,8 @@ $functions = array( 'description' => 'Return all raw strings (with {$a->xxx}) for a specific component - similar to core get_component_strings() call', 'type' => 'read', - 'loginrequired' => false + 'loginrequired' => false, + 'ajax' => true, ), @@ -1033,7 +1036,8 @@ $functions = array( 'methodname' => 'load_template', 'description' => 'Load a template for a renderable', 'type' => 'read', - 'loginrequired' => false + 'loginrequired' => false, + 'ajax' => true, ), // Completion related functions. @@ -1075,6 +1079,7 @@ $functions = array( 'methodname' => 'update_tags', 'description' => 'Updates tags', 'type' => 'write', + 'ajax' => true ), 'core_tag_get_tags' => array( @@ -1082,6 +1087,7 @@ $functions = array( 'methodname' => 'get_tags', 'description' => 'Gets tags by their ids', 'type' => 'read', + 'ajax' => true ), ); diff --git a/lib/external/externallib.php b/lib/external/externallib.php index 1909c2f3fc7..a0526c53379 100644 --- a/lib/external/externallib.php +++ b/lib/external/externallib.php @@ -77,16 +77,6 @@ class core_external extends external_api { return $strparams; } - /** - * Can this function be called directly from ajax? - * - * @return boolean - * @since Moodle 2.9 - */ - public static function get_string_is_allowed_from_ajax() { - return true; - } - /** * Returns description of get_string parameters * @@ -163,17 +153,6 @@ class core_external extends external_api { ); } - /** - * Can this function be called directly from ajax? - * - * @return boolean - * @since Moodle 2.9 - */ - public static function get_strings_is_allowed_from_ajax() { - return true; - } - - /** * Return multiple call to core get_string() * @@ -237,16 +216,6 @@ class core_external extends external_api { ); } - /** - * Can this function be called directly from ajax? - * - * @return boolean - * @since Moodle 2.9 - */ - public static function get_component_strings_is_allowed_from_ajax() { - return true; - } - /** * Return all lang strings of a component - call to core get_component_strings(). * diff --git a/lib/externallib.php b/lib/externallib.php index d392cf1cf2b..951f215636c 100644 --- a/lib/externallib.php +++ b/lib/externallib.php @@ -115,6 +115,9 @@ function external_function_info($function, $strictness=MUST_EXIST) { if (isset($functions[$function->name]['type'])) { $function->type = $functions[$function->name]['type']; } + if (isset($functions[$function->name]['ajax'])) { + $function->allowed_from_ajax = $functions[$function->name]['ajax']; + } if (isset($functions[$function->name]['loginrequired'])) { $function->loginrequired = $functions[$function->name]['loginrequired']; } else { diff --git a/tag/classes/external.php b/tag/classes/external.php index ee8f1a0e3b6..2bb564f335b 100644 --- a/tag/classes/external.php +++ b/tag/classes/external.php @@ -59,15 +59,6 @@ class core_tag_external extends external_api { ); } - /** - * Expose to AJAX - * - * @return boolean - */ - public static function update_tags_is_allowed_from_ajax() { - return true; - } - /** * Update tags * @@ -194,15 +185,6 @@ class core_tag_external extends external_api { ); } - /** - * Expose to AJAX - * - * @return boolean - */ - public static function get_tags_is_allowed_from_ajax() { - return true; - } - /** * Get tags by their ids * @@ -281,4 +263,4 @@ class core_tag_external extends external_api { ) ); } -} \ No newline at end of file +}