1 |
efrain |
1 |
<?php
|
|
|
2 |
// This file is part of Moodle - http://moodle.org/
|
|
|
3 |
//
|
|
|
4 |
// Moodle is free software: you can redistribute it and/or modify
|
|
|
5 |
// it under the terms of the GNU General Public License as published by
|
|
|
6 |
// the Free Software Foundation, either version 3 of the License, or
|
|
|
7 |
// (at your option) any later version.
|
|
|
8 |
//
|
|
|
9 |
// Moodle is distributed in the hope that it will be useful,
|
|
|
10 |
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
11 |
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
12 |
// GNU General Public License for more details.
|
|
|
13 |
//
|
|
|
14 |
// You should have received a copy of the GNU General Public License
|
|
|
15 |
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
|
|
16 |
|
|
|
17 |
/**
|
|
|
18 |
* This is the external API for this tool.
|
|
|
19 |
*
|
|
|
20 |
* @package tool_mobile
|
|
|
21 |
* @copyright 2016 Juan Leyva
|
|
|
22 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
23 |
*/
|
|
|
24 |
|
|
|
25 |
namespace tool_mobile;
|
|
|
26 |
|
|
|
27 |
defined('MOODLE_INTERNAL') || die();
|
|
|
28 |
require_once("$CFG->dirroot/webservice/lib.php");
|
|
|
29 |
|
|
|
30 |
use core_external\external_api;
|
|
|
31 |
use core_external\external_files;
|
|
|
32 |
use core_external\external_function_parameters;
|
|
|
33 |
use core_external\external_multiple_structure;
|
|
|
34 |
use core_external\external_single_structure;
|
|
|
35 |
use core_external\external_settings;
|
|
|
36 |
use core_external\external_value;
|
|
|
37 |
use core_external\external_warnings;
|
|
|
38 |
use context_system;
|
|
|
39 |
use moodle_exception;
|
|
|
40 |
use moodle_url;
|
|
|
41 |
use core_user;
|
|
|
42 |
use coding_exception;
|
|
|
43 |
|
|
|
44 |
/**
|
|
|
45 |
* This is the external API for this tool.
|
|
|
46 |
*
|
|
|
47 |
* @copyright 2016 Juan Leyva
|
|
|
48 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
49 |
*/
|
|
|
50 |
class external extends external_api {
|
|
|
51 |
|
|
|
52 |
/**
|
|
|
53 |
* Returns description of get_plugins_supporting_mobile() parameters.
|
|
|
54 |
*
|
|
|
55 |
* @return external_function_parameters
|
|
|
56 |
* @since Moodle 3.1
|
|
|
57 |
*/
|
|
|
58 |
public static function get_plugins_supporting_mobile_parameters() {
|
|
|
59 |
return new external_function_parameters(array());
|
|
|
60 |
}
|
|
|
61 |
|
|
|
62 |
/**
|
|
|
63 |
* Returns a list of Moodle plugins supporting the mobile app.
|
|
|
64 |
*
|
|
|
65 |
* @return array an array of warnings and objects containing the plugin information
|
|
|
66 |
* @since Moodle 3.1
|
|
|
67 |
*/
|
|
|
68 |
public static function get_plugins_supporting_mobile() {
|
|
|
69 |
return array(
|
|
|
70 |
'plugins' => api::get_plugins_supporting_mobile(),
|
|
|
71 |
'warnings' => array(),
|
|
|
72 |
);
|
|
|
73 |
}
|
|
|
74 |
|
|
|
75 |
/**
|
|
|
76 |
* Returns description of get_plugins_supporting_mobile() result value.
|
|
|
77 |
*
|
|
|
78 |
* @return \core_external\external_description
|
|
|
79 |
* @since Moodle 3.1
|
|
|
80 |
*/
|
|
|
81 |
public static function get_plugins_supporting_mobile_returns() {
|
|
|
82 |
return new external_single_structure(
|
|
|
83 |
array(
|
|
|
84 |
'plugins' => new external_multiple_structure(
|
|
|
85 |
new external_single_structure(
|
|
|
86 |
array(
|
|
|
87 |
'component' => new external_value(PARAM_COMPONENT, 'The plugin component name.'),
|
|
|
88 |
'version' => new external_value(PARAM_NOTAGS, 'The plugin version number.'),
|
|
|
89 |
'addon' => new external_value(PARAM_COMPONENT, 'The Mobile addon (package) name.'),
|
|
|
90 |
'dependencies' => new external_multiple_structure(
|
|
|
91 |
new external_value(PARAM_COMPONENT, 'Mobile addon name.'),
|
|
|
92 |
'The list of Mobile addons this addon depends on.'
|
|
|
93 |
),
|
|
|
94 |
'fileurl' => new external_value(PARAM_URL, 'The addon package url for download
|
|
|
95 |
or empty if it doesn\'t exist.'),
|
|
|
96 |
'filehash' => new external_value(PARAM_RAW, 'The addon package hash or empty if it doesn\'t exist.'),
|
|
|
97 |
'filesize' => new external_value(PARAM_INT, 'The addon package size or empty if it doesn\'t exist.'),
|
|
|
98 |
'handlers' => new external_value(PARAM_RAW, 'Handlers definition (JSON)', VALUE_OPTIONAL),
|
|
|
99 |
'lang' => new external_value(PARAM_RAW, 'Language strings used by the handlers (JSON)', VALUE_OPTIONAL),
|
|
|
100 |
)
|
|
|
101 |
)
|
|
|
102 |
),
|
|
|
103 |
'warnings' => new external_warnings(),
|
|
|
104 |
)
|
|
|
105 |
);
|
|
|
106 |
}
|
|
|
107 |
|
|
|
108 |
/**
|
|
|
109 |
* Returns description of get_public_config() parameters.
|
|
|
110 |
*
|
|
|
111 |
* @return external_function_parameters
|
|
|
112 |
* @since Moodle 3.2
|
|
|
113 |
*/
|
|
|
114 |
public static function get_public_config_parameters() {
|
|
|
115 |
return new external_function_parameters(array());
|
|
|
116 |
}
|
|
|
117 |
|
|
|
118 |
/**
|
|
|
119 |
* Returns a list of the site public settings, those not requiring authentication.
|
|
|
120 |
*
|
|
|
121 |
* @return array with the settings and warnings
|
|
|
122 |
* @since Moodle 3.2
|
|
|
123 |
*/
|
|
|
124 |
public static function get_public_config() {
|
|
|
125 |
$result = api::get_public_config();
|
|
|
126 |
$result['warnings'] = array();
|
|
|
127 |
return $result;
|
|
|
128 |
}
|
|
|
129 |
|
|
|
130 |
/**
|
|
|
131 |
* Returns description of get_public_config() result value.
|
|
|
132 |
*
|
|
|
133 |
* @return \core_external\external_description
|
|
|
134 |
* @since Moodle 3.2
|
|
|
135 |
*/
|
|
|
136 |
public static function get_public_config_returns() {
|
|
|
137 |
return new external_single_structure(
|
|
|
138 |
array(
|
|
|
139 |
'wwwroot' => new external_value(PARAM_RAW, 'Site URL.'),
|
|
|
140 |
'httpswwwroot' => new external_value(PARAM_RAW, 'Site https URL (if httpslogin is enabled).'),
|
|
|
141 |
'sitename' => new external_value(PARAM_RAW, 'Site name.'),
|
|
|
142 |
'guestlogin' => new external_value(PARAM_INT, 'Whether guest login is enabled.'),
|
|
|
143 |
'rememberusername' => new external_value(PARAM_INT, 'Values: 0 for No, 1 for Yes, 2 for optional.'),
|
|
|
144 |
'authloginviaemail' => new external_value(PARAM_INT, 'Whether log in via email is enabled.'),
|
|
|
145 |
'registerauth' => new external_value(PARAM_PLUGIN, 'Authentication method for user registration.'),
|
|
|
146 |
'forgottenpasswordurl' => new external_value(PARAM_URL, 'Forgotten password URL.'),
|
|
|
147 |
'authinstructions' => new external_value(PARAM_RAW, 'Authentication instructions.'),
|
|
|
148 |
'authnoneenabled' => new external_value(PARAM_INT, 'Whether auth none is enabled.'),
|
|
|
149 |
'enablewebservices' => new external_value(PARAM_INT, 'Whether Web Services are enabled.'),
|
|
|
150 |
'enablemobilewebservice' => new external_value(PARAM_INT, 'Whether the Mobile service is enabled.'),
|
|
|
151 |
'maintenanceenabled' => new external_value(PARAM_INT, 'Whether site maintenance is enabled.'),
|
|
|
152 |
'maintenancemessage' => new external_value(PARAM_RAW, 'Maintenance message.'),
|
|
|
153 |
'logourl' => new external_value(PARAM_URL, 'The site logo URL', VALUE_OPTIONAL),
|
|
|
154 |
'compactlogourl' => new external_value(PARAM_URL, 'The site compact logo URL', VALUE_OPTIONAL),
|
|
|
155 |
'typeoflogin' => new external_value(PARAM_INT, 'The type of login. 1 for app, 2 for browser, 3 for embedded.'),
|
|
|
156 |
'launchurl' => new external_value(PARAM_URL, 'SSO login launch URL.', VALUE_OPTIONAL),
|
|
|
157 |
'mobilecssurl' => new external_value(PARAM_URL, 'Mobile custom CSS theme', VALUE_OPTIONAL),
|
|
|
158 |
'tool_mobile_disabledfeatures' => new external_value(PARAM_RAW, 'Disabled features in the app', VALUE_OPTIONAL),
|
|
|
159 |
'identityproviders' => new external_multiple_structure(
|
|
|
160 |
new external_single_structure(
|
|
|
161 |
array(
|
|
|
162 |
'name' => new external_value(PARAM_TEXT, 'The identity provider name.'),
|
|
|
163 |
'iconurl' => new external_value(PARAM_URL, 'The icon URL for the provider.'),
|
|
|
164 |
'url' => new external_value(PARAM_URL, 'The URL of the provider.'),
|
|
|
165 |
)
|
|
|
166 |
),
|
|
|
167 |
'Identity providers', VALUE_OPTIONAL
|
|
|
168 |
),
|
|
|
169 |
'country' => new external_value(PARAM_NOTAGS, 'Default site country', VALUE_OPTIONAL),
|
|
|
170 |
'agedigitalconsentverification' => new external_value(PARAM_BOOL, 'Whether age digital consent verification
|
|
|
171 |
is enabled.', VALUE_OPTIONAL),
|
|
|
172 |
'supportname' => new external_value(PARAM_NOTAGS, 'Site support contact name
|
|
|
173 |
(only if age verification is enabled).', VALUE_OPTIONAL),
|
|
|
174 |
'supportemail' => new external_value(PARAM_EMAIL, 'Site support contact email
|
|
|
175 |
(only if age verification is enabled).', VALUE_OPTIONAL),
|
|
|
176 |
'supportpage' => new external_value(PARAM_URL, 'Site support page link.', VALUE_OPTIONAL),
|
|
|
177 |
'supportavailability' => new external_value(PARAM_INT, 'Determines who has access to contact site support.',
|
|
|
178 |
VALUE_OPTIONAL),
|
|
|
179 |
'autolang' => new external_value(PARAM_INT, 'Whether to detect default language
|
|
|
180 |
from browser setting.', VALUE_OPTIONAL),
|
|
|
181 |
'lang' => new external_value(PARAM_LANG, 'Default language for the site.', VALUE_OPTIONAL),
|
|
|
182 |
'langmenu' => new external_value(PARAM_INT, 'Whether the language menu should be displayed.', VALUE_OPTIONAL),
|
|
|
183 |
'langlist' => new external_value(PARAM_RAW, 'Languages on language menu.', VALUE_OPTIONAL),
|
|
|
184 |
'locale' => new external_value(PARAM_RAW, 'Sitewide locale.', VALUE_OPTIONAL),
|
|
|
185 |
'tool_mobile_minimumversion' => new external_value(PARAM_NOTAGS, 'Minimum required version to access.',
|
|
|
186 |
VALUE_OPTIONAL),
|
|
|
187 |
'tool_mobile_iosappid' => new external_value(PARAM_ALPHANUM, 'iOS app\'s unique identifier.',
|
|
|
188 |
VALUE_OPTIONAL),
|
|
|
189 |
'tool_mobile_androidappid' => new external_value(PARAM_NOTAGS, 'Android app\'s unique identifier.',
|
|
|
190 |
VALUE_OPTIONAL),
|
|
|
191 |
'tool_mobile_setuplink' => new external_value(PARAM_URL, 'App download page.', VALUE_OPTIONAL),
|
|
|
192 |
'tool_mobile_qrcodetype' => new external_value(PARAM_INT, 'QR login configuration.', VALUE_OPTIONAL),
|
|
|
193 |
'warnings' => new external_warnings(),
|
|
|
194 |
)
|
|
|
195 |
);
|
|
|
196 |
}
|
|
|
197 |
|
|
|
198 |
/**
|
|
|
199 |
* Returns description of get_config() parameters.
|
|
|
200 |
*
|
|
|
201 |
* @return external_function_parameters
|
|
|
202 |
* @since Moodle 3.2
|
|
|
203 |
*/
|
|
|
204 |
public static function get_config_parameters() {
|
|
|
205 |
return new external_function_parameters(
|
|
|
206 |
array(
|
|
|
207 |
'section' => new external_value(PARAM_ALPHANUMEXT, 'Settings section name.', VALUE_DEFAULT, ''),
|
|
|
208 |
)
|
|
|
209 |
);
|
|
|
210 |
}
|
|
|
211 |
|
|
|
212 |
/**
|
|
|
213 |
* Returns a list of site settings, filtering by section.
|
|
|
214 |
*
|
|
|
215 |
* @param string $section settings section name
|
|
|
216 |
* @return array with the settings and warnings
|
|
|
217 |
* @since Moodle 3.2
|
|
|
218 |
*/
|
|
|
219 |
public static function get_config($section = '') {
|
|
|
220 |
|
|
|
221 |
$params = self::validate_parameters(self::get_config_parameters(), array('section' => $section));
|
|
|
222 |
|
|
|
223 |
$settings = api::get_config($params['section']);
|
|
|
224 |
$result['settings'] = array();
|
|
|
225 |
foreach ($settings as $name => $value) {
|
|
|
226 |
$result['settings'][] = array(
|
|
|
227 |
'name' => $name,
|
|
|
228 |
'value' => $value,
|
|
|
229 |
);
|
|
|
230 |
}
|
|
|
231 |
|
|
|
232 |
$result['warnings'] = array();
|
|
|
233 |
return $result;
|
|
|
234 |
}
|
|
|
235 |
|
|
|
236 |
/**
|
|
|
237 |
* Returns description of get_config() result value.
|
|
|
238 |
*
|
|
|
239 |
* @return \core_external\external_description
|
|
|
240 |
* @since Moodle 3.2
|
|
|
241 |
*/
|
|
|
242 |
public static function get_config_returns() {
|
|
|
243 |
return new external_single_structure(
|
|
|
244 |
array(
|
|
|
245 |
'settings' => new external_multiple_structure(
|
|
|
246 |
new external_single_structure(
|
|
|
247 |
array(
|
|
|
248 |
'name' => new external_value(PARAM_RAW, 'The name of the setting'),
|
|
|
249 |
'value' => new external_value(PARAM_RAW, 'The value of the setting'),
|
|
|
250 |
)
|
|
|
251 |
),
|
|
|
252 |
'Settings'
|
|
|
253 |
),
|
|
|
254 |
'warnings' => new external_warnings(),
|
|
|
255 |
)
|
|
|
256 |
);
|
|
|
257 |
}
|
|
|
258 |
|
|
|
259 |
/**
|
|
|
260 |
* Returns description of get_autologin_key() parameters.
|
|
|
261 |
*
|
|
|
262 |
* @return external_function_parameters
|
|
|
263 |
* @since Moodle 3.2
|
|
|
264 |
*/
|
|
|
265 |
public static function get_autologin_key_parameters() {
|
|
|
266 |
return new external_function_parameters (
|
|
|
267 |
array(
|
|
|
268 |
'privatetoken' => new external_value(PARAM_ALPHANUM, 'Private token, usually generated by login/token.php'),
|
|
|
269 |
)
|
|
|
270 |
);
|
|
|
271 |
}
|
|
|
272 |
|
|
|
273 |
/**
|
|
|
274 |
* Creates an auto-login key for the current user. Is created only in https sites and is restricted by time and ip address.
|
|
|
275 |
*
|
|
|
276 |
* Please note that it only works if the request comes from the Moodle mobile or desktop app.
|
|
|
277 |
*
|
|
|
278 |
* @param string $privatetoken the user private token for validating the request
|
|
|
279 |
* @return array with the settings and warnings
|
|
|
280 |
* @since Moodle 3.2
|
|
|
281 |
*/
|
|
|
282 |
public static function get_autologin_key($privatetoken) {
|
|
|
283 |
global $CFG, $DB, $USER;
|
|
|
284 |
|
|
|
285 |
$params = self::validate_parameters(self::get_autologin_key_parameters(), array('privatetoken' => $privatetoken));
|
|
|
286 |
$privatetoken = $params['privatetoken'];
|
|
|
287 |
|
|
|
288 |
$context = context_system::instance();
|
|
|
289 |
|
|
|
290 |
// We must toletare these two exceptions: forcepasswordchangenotice and usernotfullysetup.
|
|
|
291 |
try {
|
|
|
292 |
self::validate_context($context);
|
|
|
293 |
} catch (moodle_exception $e) {
|
|
|
294 |
if ($e->errorcode != 'usernotfullysetup' && $e->errorcode != 'forcepasswordchangenotice') {
|
|
|
295 |
// In case we receive a different exception, throw it.
|
|
|
296 |
throw $e;
|
|
|
297 |
}
|
|
|
298 |
}
|
|
|
299 |
|
|
|
300 |
// Only requests from the Moodle mobile or desktop app. This enhances security to avoid any type of XSS attack.
|
|
|
301 |
// This code goes intentionally here and not inside the check_autologin_prerequisites() function because it
|
|
|
302 |
// is used by other PHP scripts that can be opened in any browser.
|
|
|
303 |
if (!\core_useragent::is_moodle_app()) {
|
|
|
304 |
throw new moodle_exception('apprequired', 'tool_mobile');
|
|
|
305 |
}
|
|
|
306 |
api::check_autologin_prerequisites($USER->id);
|
|
|
307 |
|
|
|
308 |
if (isset($_GET['privatetoken']) or empty($privatetoken)) {
|
|
|
309 |
throw new moodle_exception('invalidprivatetoken', 'tool_mobile');
|
|
|
310 |
}
|
|
|
311 |
|
|
|
312 |
// Check the request counter, we must limit the number of times the privatetoken is sent.
|
|
|
313 |
// Between each request 6 minutes are required.
|
|
|
314 |
$last = get_user_preferences('tool_mobile_autologin_request_last', 0, $USER);
|
|
|
315 |
// Check if we must reset the count.
|
|
|
316 |
$mintimereq = get_config('tool_mobile', 'autologinmintimebetweenreq');
|
|
|
317 |
$mintimereq = empty($mintimereq) ? 6 * MINSECS : $mintimereq;
|
|
|
318 |
$timenow = time();
|
|
|
319 |
if ($timenow - $last < $mintimereq) {
|
|
|
320 |
$minutes = $mintimereq / MINSECS;
|
|
|
321 |
throw new moodle_exception('autologinkeygenerationlockout', 'tool_mobile', '', $minutes);
|
|
|
322 |
}
|
|
|
323 |
set_user_preference('tool_mobile_autologin_request_last', $timenow, $USER);
|
|
|
324 |
|
|
|
325 |
// We are expecting a privatetoken linked to the current token being used.
|
|
|
326 |
// This WS is only valid when using mobile services via REST (this is intended).
|
|
|
327 |
$currenttoken = required_param('wstoken', PARAM_ALPHANUM);
|
|
|
328 |
$conditions = array(
|
|
|
329 |
'userid' => $USER->id,
|
|
|
330 |
'token' => $currenttoken,
|
|
|
331 |
'privatetoken' => $privatetoken,
|
|
|
332 |
);
|
|
|
333 |
if (!$token = $DB->get_record('external_tokens', $conditions)) {
|
|
|
334 |
throw new moodle_exception('invalidprivatetoken', 'tool_mobile');
|
|
|
335 |
}
|
|
|
336 |
|
|
|
337 |
$result = array();
|
|
|
338 |
$result['key'] = api::get_autologin_key();
|
|
|
339 |
$autologinurl = new moodle_url("/$CFG->admin/tool/mobile/autologin.php");
|
|
|
340 |
$result['autologinurl'] = $autologinurl->out(false);
|
|
|
341 |
$result['warnings'] = array();
|
|
|
342 |
return $result;
|
|
|
343 |
}
|
|
|
344 |
|
|
|
345 |
/**
|
|
|
346 |
* Returns description of get_autologin_key() result value.
|
|
|
347 |
*
|
|
|
348 |
* @return \core_external\external_description
|
|
|
349 |
* @since Moodle 3.2
|
|
|
350 |
*/
|
|
|
351 |
public static function get_autologin_key_returns() {
|
|
|
352 |
return new external_single_structure(
|
|
|
353 |
array(
|
|
|
354 |
'key' => new external_value(PARAM_ALPHANUMEXT, 'Auto-login key for a single usage with time expiration.'),
|
|
|
355 |
'autologinurl' => new external_value(PARAM_URL, 'Auto-login URL.'),
|
|
|
356 |
'warnings' => new external_warnings(),
|
|
|
357 |
)
|
|
|
358 |
);
|
|
|
359 |
}
|
|
|
360 |
|
|
|
361 |
/**
|
|
|
362 |
* Returns description of get_content() parameters
|
|
|
363 |
*
|
|
|
364 |
* @return external_function_parameters
|
|
|
365 |
* @since Moodle 3.5
|
|
|
366 |
*/
|
|
|
367 |
public static function get_content_parameters() {
|
|
|
368 |
return new external_function_parameters(
|
|
|
369 |
array(
|
|
|
370 |
'component' => new external_value(PARAM_COMPONENT, 'Component where the class is e.g. mod_assign.'),
|
|
|
371 |
'method' => new external_value(PARAM_ALPHANUMEXT, 'Method to execute in class \$component\output\mobile.'),
|
|
|
372 |
'args' => new external_multiple_structure(
|
|
|
373 |
new external_single_structure(
|
|
|
374 |
array(
|
|
|
375 |
'name' => new external_value(PARAM_ALPHANUMEXT, 'Param name.'),
|
|
|
376 |
'value' => new external_value(PARAM_RAW, 'Param value.')
|
|
|
377 |
)
|
|
|
378 |
), 'Args for the method are optional.', VALUE_OPTIONAL
|
|
|
379 |
)
|
|
|
380 |
)
|
|
|
381 |
);
|
|
|
382 |
}
|
|
|
383 |
|
|
|
384 |
/**
|
|
|
385 |
* Returns a piece of content to be displayed in the Mobile app, it usually returns a template, javascript and
|
|
|
386 |
* other structured data that will be used to render a view in the Mobile app.
|
|
|
387 |
*
|
|
|
388 |
* Callbacks (placed in \$component\output\mobile) that are called by this web service are responsible for doing the
|
|
|
389 |
* appropriate security checks to access the information to be returned.
|
|
|
390 |
*
|
|
|
391 |
* @param string $component name of the component.
|
|
|
392 |
* @param string $method function method name in class \$component\output\mobile.
|
|
|
393 |
* @param array $args optional arguments for the method.
|
|
|
394 |
* @return array HTML, JavaScript and other required data and information to create a view in the app.
|
|
|
395 |
* @since Moodle 3.5
|
|
|
396 |
* @throws coding_exception
|
|
|
397 |
*/
|
|
|
398 |
public static function get_content($component, $method, $args = array()) {
|
|
|
399 |
global $OUTPUT, $PAGE, $USER;
|
|
|
400 |
|
|
|
401 |
$params = self::validate_parameters(self::get_content_parameters(),
|
|
|
402 |
array(
|
|
|
403 |
'component' => $component,
|
|
|
404 |
'method' => $method,
|
|
|
405 |
'args' => $args
|
|
|
406 |
)
|
|
|
407 |
);
|
|
|
408 |
|
|
|
409 |
// Reformat arguments into something less unwieldy.
|
|
|
410 |
$arguments = array();
|
|
|
411 |
foreach ($params['args'] as $paramargument) {
|
|
|
412 |
$arguments[$paramargument['name']] = $paramargument['value'];
|
|
|
413 |
}
|
|
|
414 |
|
|
|
415 |
// The component was validated via the PARAM_COMPONENT parameter type.
|
|
|
416 |
$classname = '\\' . $params['component'] .'\output\mobile';
|
|
|
417 |
if (!method_exists($classname, $params['method'])) {
|
|
|
418 |
throw new coding_exception("Missing method in $classname");
|
|
|
419 |
}
|
|
|
420 |
$result = call_user_func_array(array($classname, $params['method']), array($arguments));
|
|
|
421 |
|
|
|
422 |
// Populate otherdata.
|
|
|
423 |
$otherdata = array();
|
|
|
424 |
if (!empty($result['otherdata'])) {
|
|
|
425 |
$result['otherdata'] = (array) $result['otherdata'];
|
|
|
426 |
foreach ($result['otherdata'] as $name => $value) {
|
|
|
427 |
$otherdata[] = array(
|
|
|
428 |
'name' => $name,
|
|
|
429 |
'value' => $value
|
|
|
430 |
);
|
|
|
431 |
}
|
|
|
432 |
}
|
|
|
433 |
|
|
|
434 |
return array(
|
|
|
435 |
'templates' => !empty($result['templates']) ? $result['templates'] : array(),
|
|
|
436 |
'javascript' => !empty($result['javascript']) ? $result['javascript'] : '',
|
|
|
437 |
'otherdata' => $otherdata,
|
|
|
438 |
'files' => !empty($result['files']) ? $result['files'] : array(),
|
|
|
439 |
'restrict' => !empty($result['restrict']) ? $result['restrict'] : array(),
|
|
|
440 |
'disabled' => !empty($result['disabled']) ? true : false,
|
|
|
441 |
);
|
|
|
442 |
}
|
|
|
443 |
|
|
|
444 |
/**
|
|
|
445 |
* Returns description of get_content() result value
|
|
|
446 |
*
|
|
|
447 |
* @return array
|
|
|
448 |
* @since Moodle 3.5
|
|
|
449 |
*/
|
|
|
450 |
public static function get_content_returns() {
|
|
|
451 |
return new external_single_structure(
|
|
|
452 |
array(
|
|
|
453 |
'templates' => new external_multiple_structure(
|
|
|
454 |
new external_single_structure(
|
|
|
455 |
array(
|
|
|
456 |
'id' => new external_value(PARAM_TEXT, 'ID of the template.'),
|
|
|
457 |
'html' => new external_value(PARAM_RAW, 'HTML code.'),
|
|
|
458 |
)
|
|
|
459 |
),
|
|
|
460 |
'Templates required by the generated content.'
|
|
|
461 |
),
|
|
|
462 |
'javascript' => new external_value(PARAM_RAW, 'JavaScript code.'),
|
|
|
463 |
'otherdata' => new external_multiple_structure(
|
|
|
464 |
new external_single_structure(
|
|
|
465 |
array(
|
|
|
466 |
'name' => new external_value(PARAM_RAW, 'Field name.'),
|
|
|
467 |
'value' => new external_value(PARAM_RAW, 'Field value.')
|
|
|
468 |
)
|
|
|
469 |
),
|
|
|
470 |
'Other data that can be used or manipulated by the template via 2-way data-binding.'
|
|
|
471 |
),
|
|
|
472 |
'files' => new external_files('Files in the content.'),
|
|
|
473 |
'restrict' => new external_single_structure(
|
|
|
474 |
array(
|
|
|
475 |
'users' => new external_multiple_structure(
|
|
|
476 |
new external_value(PARAM_INT, 'user id'), 'List of allowed users.', VALUE_OPTIONAL
|
|
|
477 |
),
|
|
|
478 |
'courses' => new external_multiple_structure(
|
|
|
479 |
new external_value(PARAM_INT, 'course id'), 'List of allowed courses.', VALUE_OPTIONAL
|
|
|
480 |
),
|
|
|
481 |
),
|
|
|
482 |
'Restrict this content to certain users or courses.'
|
|
|
483 |
),
|
|
|
484 |
'disabled' => new external_value(PARAM_BOOL, 'Whether we consider this disabled or not.', VALUE_OPTIONAL),
|
|
|
485 |
)
|
|
|
486 |
);
|
|
|
487 |
}
|
|
|
488 |
|
|
|
489 |
/**
|
|
|
490 |
* Returns description of method parameters
|
|
|
491 |
*
|
|
|
492 |
* @return external_function_parameters
|
|
|
493 |
* @since Moodle 3.7
|
|
|
494 |
*/
|
|
|
495 |
public static function call_external_functions_parameters() {
|
|
|
496 |
return new external_function_parameters([
|
|
|
497 |
'requests' => new external_multiple_structure(
|
|
|
498 |
new external_single_structure([
|
|
|
499 |
'function' => new external_value(PARAM_ALPHANUMEXT, 'Function name'),
|
|
|
500 |
'arguments' => new external_value(PARAM_RAW, 'JSON-encoded object with named arguments', VALUE_DEFAULT, '{}'),
|
|
|
501 |
'settingraw' => new external_value(PARAM_BOOL, 'Return raw text', VALUE_DEFAULT, false),
|
|
|
502 |
'settingfilter' => new external_value(PARAM_BOOL, 'Filter text', VALUE_DEFAULT, false),
|
|
|
503 |
'settingfileurl' => new external_value(PARAM_BOOL, 'Rewrite plugin file URLs', VALUE_DEFAULT, true),
|
|
|
504 |
'settinglang' => new external_value(PARAM_LANG, 'Session language', VALUE_DEFAULT, ''),
|
|
|
505 |
])
|
|
|
506 |
)
|
|
|
507 |
]);
|
|
|
508 |
}
|
|
|
509 |
|
|
|
510 |
/**
|
|
|
511 |
* Call multiple external functions and return all responses.
|
|
|
512 |
*
|
|
|
513 |
* @param array $requests List of requests.
|
|
|
514 |
* @return array Responses.
|
|
|
515 |
* @since Moodle 3.7
|
|
|
516 |
*/
|
|
|
517 |
public static function call_external_functions($requests) {
|
|
|
518 |
global $SESSION;
|
|
|
519 |
|
|
|
520 |
$params = self::validate_parameters(self::call_external_functions_parameters(), ['requests' => $requests]);
|
|
|
521 |
|
|
|
522 |
// We need to check if the functions being called are included in the service of the current token.
|
|
|
523 |
// This function only works when using mobile services via REST (this is intended).
|
|
|
524 |
$webservicemanager = new \webservice;
|
|
|
525 |
$token = $webservicemanager->get_user_ws_token(required_param('wstoken', PARAM_ALPHANUM));
|
|
|
526 |
|
|
|
527 |
$settings = external_settings::get_instance();
|
|
|
528 |
$defaultlang = current_language();
|
|
|
529 |
$responses = [];
|
|
|
530 |
|
|
|
531 |
foreach ($params['requests'] as $request) {
|
|
|
532 |
// Some external functions modify _GET or $_POST data, we need to restore the original data after each call.
|
|
|
533 |
$originalget = fullclone($_GET);
|
|
|
534 |
$originalpost = fullclone($_POST);
|
|
|
535 |
|
|
|
536 |
// Set external settings and language.
|
|
|
537 |
$settings->set_raw($request['settingraw']);
|
|
|
538 |
$settings->set_filter($request['settingfilter']);
|
|
|
539 |
$settings->set_fileurl($request['settingfileurl']);
|
|
|
540 |
$settings->set_lang($request['settinglang']);
|
|
|
541 |
$SESSION->lang = $request['settinglang'] ?: $defaultlang;
|
|
|
542 |
|
|
|
543 |
// Parse arguments to an array, validation is done in external_api::call_external_function.
|
|
|
544 |
$args = @json_decode($request['arguments'], true);
|
|
|
545 |
if (!is_array($args)) {
|
|
|
546 |
$args = [];
|
|
|
547 |
}
|
|
|
548 |
|
|
|
549 |
if ($webservicemanager->service_function_exists($request['function'], $token->externalserviceid)) {
|
|
|
550 |
$response = external_api::call_external_function($request['function'], $args, false);
|
|
|
551 |
} else {
|
|
|
552 |
// Function not included in the service, return an access exception.
|
|
|
553 |
$response = [
|
|
|
554 |
'error' => true,
|
|
|
555 |
'exception' => [
|
|
|
556 |
'errorcode' => 'accessexception',
|
|
|
557 |
'module' => 'webservice'
|
|
|
558 |
]
|
|
|
559 |
];
|
|
|
560 |
if (debugging('', DEBUG_DEVELOPER)) {
|
|
|
561 |
$response['exception']['debuginfo'] = 'Access to the function is not allowed.';
|
|
|
562 |
}
|
|
|
563 |
}
|
|
|
564 |
|
|
|
565 |
if (isset($response['data'])) {
|
|
|
566 |
$response['data'] = json_encode($response['data']);
|
|
|
567 |
}
|
|
|
568 |
if (isset($response['exception'])) {
|
|
|
569 |
$response['exception'] = json_encode($response['exception']);
|
|
|
570 |
}
|
|
|
571 |
$responses[] = $response;
|
|
|
572 |
|
|
|
573 |
// Restore original $_GET and $_POST.
|
|
|
574 |
$_GET = $originalget;
|
|
|
575 |
$_POST = $originalpost;
|
|
|
576 |
|
|
|
577 |
if ($response['error']) {
|
|
|
578 |
// Do not process the remaining requests.
|
|
|
579 |
break;
|
|
|
580 |
}
|
|
|
581 |
}
|
|
|
582 |
|
|
|
583 |
return ['responses' => $responses];
|
|
|
584 |
}
|
|
|
585 |
|
|
|
586 |
/**
|
|
|
587 |
* Returns description of method result value
|
|
|
588 |
*
|
|
|
589 |
* @return external_single_structure
|
|
|
590 |
* @since Moodle 3.7
|
|
|
591 |
*/
|
|
|
592 |
public static function call_external_functions_returns() {
|
|
|
593 |
return new external_function_parameters([
|
|
|
594 |
'responses' => new external_multiple_structure(
|
|
|
595 |
new external_single_structure([
|
|
|
596 |
'error' => new external_value(PARAM_BOOL, 'Whether an exception was thrown.'),
|
|
|
597 |
'data' => new external_value(PARAM_RAW, 'JSON-encoded response data', VALUE_OPTIONAL),
|
|
|
598 |
'exception' => new external_value(PARAM_RAW, 'JSON-encoed exception info', VALUE_OPTIONAL),
|
|
|
599 |
])
|
|
|
600 |
)
|
|
|
601 |
]);
|
|
|
602 |
}
|
|
|
603 |
|
|
|
604 |
/**
|
|
|
605 |
* Returns description of get_tokens_for_qr_login() parameters.
|
|
|
606 |
*
|
|
|
607 |
* @return external_function_parameters
|
|
|
608 |
* @since Moodle 3.9
|
|
|
609 |
*/
|
|
|
610 |
public static function get_tokens_for_qr_login_parameters() {
|
|
|
611 |
return new external_function_parameters (
|
|
|
612 |
[
|
|
|
613 |
'qrloginkey' => new external_value(PARAM_ALPHANUMEXT, 'The user key for validating the request.'),
|
|
|
614 |
'userid' => new external_value(PARAM_INT, 'The user the key belongs to.'),
|
|
|
615 |
]
|
|
|
616 |
);
|
|
|
617 |
}
|
|
|
618 |
|
|
|
619 |
/**
|
|
|
620 |
* Returns a WebService token (and private token) for QR login
|
|
|
621 |
*
|
|
|
622 |
* @param string $qrloginkey the user key generated and embedded into the QR code for validating the request
|
|
|
623 |
* @param int $userid the user the key belongs to
|
|
|
624 |
* @return array with the tokens and warnings
|
|
|
625 |
* @since Moodle 3.9
|
|
|
626 |
*/
|
|
|
627 |
public static function get_tokens_for_qr_login($qrloginkey, $userid) {
|
|
|
628 |
global $PAGE, $DB;
|
|
|
629 |
|
|
|
630 |
$params = self::validate_parameters(self::get_tokens_for_qr_login_parameters(),
|
|
|
631 |
['qrloginkey' => $qrloginkey, 'userid' => $userid]);
|
|
|
632 |
|
|
|
633 |
$context = context_system::instance();
|
|
|
634 |
// We need this to make work the format text functions.
|
|
|
635 |
$PAGE->set_context($context);
|
|
|
636 |
|
|
|
637 |
$qrcodetype = get_config('tool_mobile', 'qrcodetype');
|
|
|
638 |
if ($qrcodetype != api::QR_CODE_LOGIN) {
|
|
|
639 |
throw new moodle_exception('qrcodedisabled', 'tool_mobile');
|
|
|
640 |
}
|
|
|
641 |
|
|
|
642 |
// Only requests from the Moodle mobile or desktop app. This enhances security to avoid any type of XSS attack.
|
|
|
643 |
// This code goes intentionally here and not inside the check_autologin_prerequisites() function because it
|
|
|
644 |
// is used by other PHP scripts that can be opened in any browser.
|
|
|
645 |
if (!\core_useragent::is_moodle_app()) {
|
|
|
646 |
throw new moodle_exception('apprequired', 'tool_mobile');
|
|
|
647 |
}
|
|
|
648 |
api::check_autologin_prerequisites($params['userid']); // Checks https, avoid site admins using this...
|
|
|
649 |
|
|
|
650 |
// Validate and delete the key.
|
|
|
651 |
$key = validate_user_key($params['qrloginkey'], 'tool_mobile/qrlogin', null);
|
|
|
652 |
delete_user_key('tool_mobile/qrlogin', $params['userid']);
|
|
|
653 |
|
|
|
654 |
// Double check key belong to user.
|
|
|
655 |
if ($key->userid != $params['userid']) {
|
|
|
656 |
throw new moodle_exception('invalidkey');
|
|
|
657 |
}
|
|
|
658 |
|
|
|
659 |
// Key validated, check user.
|
|
|
660 |
$user = core_user::get_user($key->userid, '*', MUST_EXIST);
|
|
|
661 |
core_user::require_active_user($user, true, true);
|
|
|
662 |
|
|
|
663 |
// Generate WS tokens.
|
|
|
664 |
\core\session\manager::set_user($user);
|
|
|
665 |
|
|
|
666 |
// Check if the service exists and is enabled.
|
|
|
667 |
$service = $DB->get_record('external_services', ['shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE, 'enabled' => 1]);
|
|
|
668 |
if (empty($service)) {
|
|
|
669 |
// will throw exception if no token found
|
|
|
670 |
throw new moodle_exception('servicenotavailable', 'webservice');
|
|
|
671 |
}
|
|
|
672 |
|
|
|
673 |
// Get an existing token or create a new one.
|
|
|
674 |
$token = \core_external\util::generate_token_for_current_user($service);
|
|
|
675 |
$privatetoken = $token->privatetoken; // Save it here, the next function removes it.
|
|
|
676 |
\core_external\util::log_token_request($token);
|
|
|
677 |
|
|
|
678 |
$result = [
|
|
|
679 |
'token' => $token->token,
|
|
|
680 |
'privatetoken' => $privatetoken ?: '',
|
|
|
681 |
'warnings' => [],
|
|
|
682 |
];
|
|
|
683 |
return $result;
|
|
|
684 |
}
|
|
|
685 |
|
|
|
686 |
/**
|
|
|
687 |
* Returns description of get_tokens_for_qr_login() result value.
|
|
|
688 |
*
|
|
|
689 |
* @return \core_external\external_description
|
|
|
690 |
* @since Moodle 3.9
|
|
|
691 |
*/
|
|
|
692 |
public static function get_tokens_for_qr_login_returns() {
|
|
|
693 |
return new external_single_structure(
|
|
|
694 |
[
|
|
|
695 |
'token' => new external_value(PARAM_ALPHANUM, 'A valid WebService token for the official mobile app service.'),
|
|
|
696 |
'privatetoken' => new external_value(PARAM_ALPHANUM, 'Private token used for auto-login processes.'),
|
|
|
697 |
'warnings' => new external_warnings(),
|
|
|
698 |
]
|
|
|
699 |
);
|
|
|
700 |
}
|
|
|
701 |
|
|
|
702 |
/**
|
|
|
703 |
* Returns description of validate_subscription_key() parameters.
|
|
|
704 |
*
|
|
|
705 |
* @return external_function_parameters
|
|
|
706 |
* @since Moodle 3.9
|
|
|
707 |
*/
|
|
|
708 |
public static function validate_subscription_key_parameters() {
|
|
|
709 |
return new external_function_parameters(
|
|
|
710 |
[
|
|
|
711 |
'key' => new external_value(PARAM_RAW, 'Site subscription temporary key.'),
|
|
|
712 |
]
|
|
|
713 |
);
|
|
|
714 |
}
|
|
|
715 |
|
|
|
716 |
/**
|
|
|
717 |
* Check if the given site subscription key is valid
|
|
|
718 |
*
|
|
|
719 |
* @param string $key subscriptiion temporary key
|
|
|
720 |
* @return array with the settings and warnings
|
|
|
721 |
* @since Moodle 3.9
|
|
|
722 |
*/
|
|
|
723 |
public static function validate_subscription_key(string $key): array {
|
|
|
724 |
global $CFG, $PAGE;
|
|
|
725 |
|
|
|
726 |
$params = self::validate_parameters(self::validate_subscription_key_parameters(), ['key' => $key]);
|
|
|
727 |
|
|
|
728 |
$context = context_system::instance();
|
|
|
729 |
$PAGE->set_context($context);
|
|
|
730 |
|
|
|
731 |
$validated = false;
|
|
|
732 |
$sitesubscriptionkey = get_config('tool_mobile', 'sitesubscriptionkey');
|
|
|
733 |
if (!empty($sitesubscriptionkey) && $CFG->enablemobilewebservice && empty($CFG->disablemobileappsubscription)) {
|
|
|
734 |
$sitesubscriptionkey = json_decode($sitesubscriptionkey);
|
|
|
735 |
$validated = time() < $sitesubscriptionkey->validuntil && $params['key'] === $sitesubscriptionkey->key;
|
|
|
736 |
// Delete existing, even if not validated to enforce security and attacks prevention.
|
|
|
737 |
unset_config('sitesubscriptionkey', 'tool_mobile');
|
|
|
738 |
}
|
|
|
739 |
|
|
|
740 |
return [
|
|
|
741 |
'validated' => $validated,
|
|
|
742 |
'warnings' => [],
|
|
|
743 |
];
|
|
|
744 |
}
|
|
|
745 |
|
|
|
746 |
/**
|
|
|
747 |
* Returns description of validate_subscription_key() result value.
|
|
|
748 |
*
|
|
|
749 |
* @return \core_external\external_description
|
|
|
750 |
* @since Moodle 3.9
|
|
|
751 |
*/
|
|
|
752 |
public static function validate_subscription_key_returns() {
|
|
|
753 |
return new external_single_structure(
|
|
|
754 |
[
|
|
|
755 |
'validated' => new external_value(PARAM_BOOL, 'Whether the key is validated or not.'),
|
|
|
756 |
'warnings' => new external_warnings(),
|
|
|
757 |
]
|
|
|
758 |
);
|
|
|
759 |
}
|
|
|
760 |
}
|