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 |
* Class for Moodle Mobile tools.
|
|
|
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 |
* @since Moodle 3.1
|
|
|
24 |
*/
|
|
|
25 |
namespace tool_mobile;
|
|
|
26 |
|
|
|
27 |
use core_component;
|
|
|
28 |
use core_plugin_manager;
|
|
|
29 |
use context_system;
|
|
|
30 |
use moodle_url;
|
|
|
31 |
use moodle_exception;
|
|
|
32 |
use lang_string;
|
|
|
33 |
use curl;
|
|
|
34 |
use core_qrcode;
|
|
|
35 |
use stdClass;
|
|
|
36 |
|
|
|
37 |
/**
|
|
|
38 |
* API exposed by tool_mobile, to be used mostly by external functions and the plugin settings.
|
|
|
39 |
*
|
|
|
40 |
* @copyright 2016 Juan Leyva
|
|
|
41 |
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
|
|
42 |
* @since Moodle 3.1
|
|
|
43 |
*/
|
|
|
44 |
class api {
|
|
|
45 |
|
|
|
46 |
/** @var int to identify the login via app. */
|
|
|
47 |
const LOGIN_VIA_APP = 1;
|
|
|
48 |
/** @var int to identify the login via browser. */
|
|
|
49 |
const LOGIN_VIA_BROWSER = 2;
|
|
|
50 |
/** @var int to identify the login via an embedded browser. */
|
|
|
51 |
const LOGIN_VIA_EMBEDDED_BROWSER = 3;
|
|
|
52 |
/** @var int seconds an auto-login key will expire. */
|
|
|
53 |
const LOGIN_KEY_TTL = 60;
|
|
|
54 |
/** @var string URL of the Moodle Apps Portal */
|
|
|
55 |
const MOODLE_APPS_PORTAL_URL = 'https://apps.moodle.com';
|
|
|
56 |
/** @var int default value in seconds a QR login key will expire. */
|
|
|
57 |
const LOGIN_QR_KEY_TTL = 600;
|
|
|
58 |
/** @var int QR code disabled value */
|
|
|
59 |
const QR_CODE_DISABLED = 0;
|
|
|
60 |
/** @var int QR code type URL value */
|
|
|
61 |
const QR_CODE_URL = 1;
|
|
|
62 |
/** @var int QR code type login value */
|
|
|
63 |
const QR_CODE_LOGIN = 2;
|
|
|
64 |
/** @var string Default Android app id */
|
|
|
65 |
const DEFAULT_ANDROID_APP_ID = 'com.moodle.moodlemobile';
|
|
|
66 |
/** @var string Default iOS app id */
|
|
|
67 |
const DEFAULT_IOS_APP_ID = '633359593';
|
|
|
68 |
/** @var int AUTOLOGOUT disabled value */
|
|
|
69 |
const AUTOLOGOUT_DISABLED = 0;
|
|
|
70 |
/** @var int AUTOLOGOUT type inmediate value */
|
|
|
71 |
const AUTOLOGOUT_INMEDIATE = 1;
|
|
|
72 |
/** @var int AUTOLOGOUT type custom value */
|
|
|
73 |
const AUTOLOGOUT_CUSTOM = 2;
|
|
|
74 |
|
|
|
75 |
/**
|
|
|
76 |
* Returns a list of Moodle plugins supporting the mobile app.
|
|
|
77 |
*
|
|
|
78 |
* @return array an array of objects containing the plugin information
|
|
|
79 |
*/
|
|
|
80 |
public static function get_plugins_supporting_mobile() {
|
|
|
81 |
global $CFG;
|
|
|
82 |
require_once($CFG->libdir . '/adminlib.php');
|
|
|
83 |
|
|
|
84 |
$cachekey = 'mobileplugins';
|
|
|
85 |
if (!isloggedin()) {
|
|
|
86 |
$cachekey = 'authmobileplugins'; // Use a different cache for not logged users.
|
|
|
87 |
}
|
|
|
88 |
|
|
|
89 |
// Check if we can return this from cache.
|
|
|
90 |
$cache = \cache::make('tool_mobile', 'plugininfo');
|
|
|
91 |
$pluginsinfo = $cache->get($cachekey);
|
|
|
92 |
if ($pluginsinfo !== false) {
|
|
|
93 |
return (array)$pluginsinfo;
|
|
|
94 |
}
|
|
|
95 |
|
|
|
96 |
$pluginsinfo = [];
|
|
|
97 |
// For not logged users return only auth plugins.
|
|
|
98 |
// This is to avoid anyone (not being a registered user) to obtain and download all the site remote add-ons.
|
|
|
99 |
if (!isloggedin()) {
|
|
|
100 |
$plugintypes = array('auth' => $CFG->dirroot.'/auth');
|
|
|
101 |
} else {
|
|
|
102 |
$plugintypes = core_component::get_plugin_types();
|
|
|
103 |
}
|
|
|
104 |
|
|
|
105 |
foreach ($plugintypes as $plugintype => $unused) {
|
|
|
106 |
// We need to include files here.
|
|
|
107 |
$pluginswithfile = core_component::get_plugin_list_with_file($plugintype, 'db' . DIRECTORY_SEPARATOR . 'mobile.php');
|
|
|
108 |
foreach ($pluginswithfile as $plugin => $notused) {
|
|
|
109 |
$path = core_component::get_plugin_directory($plugintype, $plugin);
|
|
|
110 |
$component = $plugintype . '_' . $plugin;
|
|
|
111 |
$version = get_component_version($component);
|
|
|
112 |
|
|
|
113 |
require("$path/db/mobile.php");
|
|
|
114 |
foreach ($addons as $addonname => $addoninfo) {
|
|
|
115 |
|
|
|
116 |
// Add handlers (for site add-ons).
|
|
|
117 |
$handlers = !empty($addoninfo['handlers']) ? $addoninfo['handlers'] : array();
|
|
|
118 |
$handlers = json_encode($handlers); // JSON formatted, since it is a complex structure that may vary over time.
|
|
|
119 |
|
|
|
120 |
// Now language strings used by the app.
|
|
|
121 |
$lang = array();
|
|
|
122 |
if (!empty($addoninfo['lang'])) {
|
|
|
123 |
$stringmanager = get_string_manager();
|
|
|
124 |
$langs = $stringmanager->get_list_of_translations(true);
|
|
|
125 |
foreach ($langs as $langid => $langname) {
|
|
|
126 |
foreach ($addoninfo['lang'] as $stringinfo) {
|
|
|
127 |
$lang[$langid][$stringinfo[0]] = $stringmanager->get_string(
|
|
|
128 |
$stringinfo[0],
|
|
|
129 |
$stringinfo[1] ?? '',
|
|
|
130 |
null,
|
|
|
131 |
$langid,
|
|
|
132 |
);
|
|
|
133 |
}
|
|
|
134 |
}
|
|
|
135 |
}
|
|
|
136 |
$lang = json_encode($lang);
|
|
|
137 |
|
|
|
138 |
$plugininfo = array(
|
|
|
139 |
'component' => $component,
|
|
|
140 |
'version' => $version,
|
|
|
141 |
'addon' => $addonname,
|
|
|
142 |
'dependencies' => !empty($addoninfo['dependencies']) ? $addoninfo['dependencies'] : array(),
|
|
|
143 |
'fileurl' => '',
|
|
|
144 |
'filehash' => '',
|
|
|
145 |
'filesize' => 0,
|
|
|
146 |
'handlers' => $handlers,
|
|
|
147 |
'lang' => $lang,
|
|
|
148 |
);
|
|
|
149 |
|
|
|
150 |
// All the mobile packages must be under the plugin mobile directory.
|
|
|
151 |
$package = $path . '/mobile/' . $addonname . '.zip';
|
|
|
152 |
if (file_exists($package)) {
|
|
|
153 |
$plugininfo['fileurl'] = $CFG->wwwroot . '' . str_replace($CFG->dirroot, '', $package);
|
|
|
154 |
$plugininfo['filehash'] = sha1_file($package);
|
|
|
155 |
$plugininfo['filesize'] = filesize($package);
|
|
|
156 |
}
|
|
|
157 |
$pluginsinfo[] = $plugininfo;
|
|
|
158 |
}
|
|
|
159 |
}
|
|
|
160 |
}
|
|
|
161 |
|
|
|
162 |
$cache->set($cachekey, $pluginsinfo);
|
|
|
163 |
|
|
|
164 |
return $pluginsinfo;
|
|
|
165 |
}
|
|
|
166 |
|
|
|
167 |
/**
|
|
|
168 |
* Returns a list of the site public settings, those not requiring authentication.
|
|
|
169 |
*
|
|
|
170 |
* @return array with the settings and warnings
|
|
|
171 |
*/
|
|
|
172 |
public static function get_public_config() {
|
|
|
173 |
global $CFG, $SITE, $PAGE, $OUTPUT;
|
|
|
174 |
require_once($CFG->libdir . '/authlib.php');
|
|
|
175 |
|
|
|
176 |
$context = context_system::instance();
|
|
|
177 |
// We need this to make work the format text functions.
|
|
|
178 |
$PAGE->set_context($context);
|
|
|
179 |
|
|
|
180 |
// Check if contacting site support is available to all visitors.
|
|
|
181 |
$sitesupportavailable = (isset($CFG->supportavailability) && $CFG->supportavailability == CONTACT_SUPPORT_ANYONE);
|
|
|
182 |
|
|
|
183 |
[$authinstructions] = \core_external\util::format_text($CFG->auth_instructions, FORMAT_MOODLE, $context->id);
|
|
|
184 |
[$maintenancemessage] = \core_external\util::format_text($CFG->maintenance_message, FORMAT_MOODLE, $context->id);
|
|
|
185 |
$settings = array(
|
|
|
186 |
'wwwroot' => $CFG->wwwroot,
|
|
|
187 |
'httpswwwroot' => $CFG->wwwroot,
|
|
|
188 |
'sitename' => \core_external\util::format_string($SITE->fullname, $context->id, true),
|
|
|
189 |
'guestlogin' => $CFG->guestloginbutton,
|
|
|
190 |
'rememberusername' => $CFG->rememberusername,
|
|
|
191 |
'authloginviaemail' => $CFG->authloginviaemail,
|
|
|
192 |
'registerauth' => $CFG->registerauth,
|
|
|
193 |
'forgottenpasswordurl' => clean_param($CFG->forgottenpasswordurl, PARAM_URL), // We may expect a mailto: here.
|
|
|
194 |
'authinstructions' => $authinstructions,
|
|
|
195 |
'authnoneenabled' => (int) is_enabled_auth('none'),
|
|
|
196 |
'enablewebservices' => $CFG->enablewebservices,
|
|
|
197 |
'enablemobilewebservice' => $CFG->enablemobilewebservice,
|
|
|
198 |
'maintenanceenabled' => $CFG->maintenance_enabled,
|
|
|
199 |
'maintenancemessage' => $maintenancemessage,
|
|
|
200 |
'mobilecssurl' => !empty($CFG->mobilecssurl) ? $CFG->mobilecssurl : '',
|
|
|
201 |
'tool_mobile_disabledfeatures' => get_config('tool_mobile', 'disabledfeatures'),
|
|
|
202 |
'country' => clean_param($CFG->country, PARAM_NOTAGS),
|
|
|
203 |
'agedigitalconsentverification' => \core_auth\digital_consent::is_age_digital_consent_verification_enabled(),
|
|
|
204 |
'autolang' => $CFG->autolang,
|
|
|
205 |
'lang' => clean_param($CFG->lang, PARAM_LANG), // Avoid breaking WS because of incorrect package langs.
|
|
|
206 |
'langmenu' => $CFG->langmenu,
|
|
|
207 |
'langlist' => $CFG->langlist,
|
|
|
208 |
'locale' => $CFG->locale,
|
|
|
209 |
'tool_mobile_minimumversion' => get_config('tool_mobile', 'minimumversion'),
|
|
|
210 |
'tool_mobile_iosappid' => get_config('tool_mobile', 'iosappid'),
|
|
|
211 |
'tool_mobile_androidappid' => get_config('tool_mobile', 'androidappid'),
|
|
|
212 |
'tool_mobile_setuplink' => clean_param(get_config('tool_mobile', 'setuplink'), PARAM_URL),
|
|
|
213 |
'tool_mobile_qrcodetype' => clean_param(get_config('tool_mobile', 'qrcodetype'), PARAM_INT),
|
|
|
214 |
'supportpage' => $sitesupportavailable ? clean_param($CFG->supportpage, PARAM_URL) : '',
|
|
|
215 |
'supportavailability' => clean_param($CFG->supportavailability, PARAM_INT),
|
|
|
216 |
);
|
|
|
217 |
|
|
|
218 |
$typeoflogin = get_config('tool_mobile', 'typeoflogin');
|
|
|
219 |
// Not found, edge case.
|
|
|
220 |
if ($typeoflogin === false) {
|
|
|
221 |
$typeoflogin = self::LOGIN_VIA_APP; // Defaults to via app.
|
|
|
222 |
}
|
|
|
223 |
$settings['typeoflogin'] = $typeoflogin;
|
|
|
224 |
|
|
|
225 |
// Check if the user can sign-up to return the launch URL in that case.
|
|
|
226 |
$cansignup = signup_is_enabled();
|
|
|
227 |
|
|
|
228 |
$url = new moodle_url("/$CFG->admin/tool/mobile/launch.php");
|
|
|
229 |
$settings['launchurl'] = $url->out(false);
|
|
|
230 |
|
|
|
231 |
// Check that we are receiving a moodle_url object, themes can override get_logo_url and may return incorrect values.
|
|
|
232 |
if (($logourl = $OUTPUT->get_logo_url()) && $logourl instanceof moodle_url) {
|
|
|
233 |
$settings['logourl'] = clean_param($logourl->out(false), PARAM_URL);
|
|
|
234 |
}
|
|
|
235 |
if (($compactlogourl = $OUTPUT->get_compact_logo_url()) && $compactlogourl instanceof moodle_url) {
|
|
|
236 |
$settings['compactlogourl'] = clean_param($compactlogourl->out(false), PARAM_URL);
|
|
|
237 |
}
|
|
|
238 |
|
|
|
239 |
// Identity providers.
|
|
|
240 |
$authsequence = get_enabled_auth_plugins();
|
|
|
241 |
$identityproviders = \auth_plugin_base::get_identity_providers($authsequence);
|
|
|
242 |
$identityprovidersdata = \auth_plugin_base::prepare_identity_providers_for_output($identityproviders, $OUTPUT);
|
|
|
243 |
if (!empty($identityprovidersdata)) {
|
|
|
244 |
$settings['identityproviders'] = $identityprovidersdata;
|
|
|
245 |
// Clean URLs to avoid breaking Web Services.
|
|
|
246 |
// We can't do it in prepare_identity_providers_for_output() because it may break the web output.
|
|
|
247 |
foreach ($settings['identityproviders'] as &$ip) {
|
|
|
248 |
$ip['url'] = (!empty($ip['url'])) ? clean_param($ip['url'], PARAM_URL) : '';
|
|
|
249 |
$ip['iconurl'] = (!empty($ip['iconurl'])) ? clean_param($ip['iconurl'], PARAM_URL) : '';
|
|
|
250 |
}
|
|
|
251 |
}
|
|
|
252 |
|
|
|
253 |
// If age is verified or support is available to all visitors, also return the admin contact details.
|
|
|
254 |
if ($settings['agedigitalconsentverification'] || $sitesupportavailable) {
|
|
|
255 |
$settings['supportname'] = clean_param($CFG->supportname, PARAM_NOTAGS);
|
|
|
256 |
$settings['supportemail'] = clean_param($CFG->supportemail, PARAM_EMAIL);
|
|
|
257 |
}
|
|
|
258 |
|
|
|
259 |
return $settings;
|
|
|
260 |
}
|
|
|
261 |
|
|
|
262 |
/**
|
|
|
263 |
* Returns a list of site configurations, filtering by section.
|
|
|
264 |
*
|
|
|
265 |
* @param string $section section name
|
|
|
266 |
* @return stdClass object containing the settings
|
|
|
267 |
*/
|
|
|
268 |
public static function get_config($section) {
|
|
|
269 |
global $CFG, $SITE;
|
|
|
270 |
|
|
|
271 |
$settings = new \stdClass;
|
|
|
272 |
$context = context_system::instance();
|
|
|
273 |
$isadmin = has_capability('moodle/site:config', $context);
|
|
|
274 |
|
|
|
275 |
if (empty($section) or $section == 'frontpagesettings') {
|
|
|
276 |
require_once($CFG->dirroot . '/course/format/lib.php');
|
|
|
277 |
// First settings that anyone can deduce.
|
|
|
278 |
$settings->fullname = \core_external\util::format_string($SITE->fullname, $context->id);
|
|
|
279 |
$settings->shortname = \core_external\util::format_string($SITE->shortname, $context->id);
|
|
|
280 |
|
|
|
281 |
// Return to a var instead of directly to $settings object because of differences between
|
|
|
282 |
// list() in php5 and php7. {@link http://php.net/manual/en/function.list.php}
|
|
|
283 |
$formattedsummary = \core_external\util::format_text($SITE->summary, $SITE->summaryformat,
|
|
|
284 |
$context->id);
|
|
|
285 |
$settings->summary = $formattedsummary[0];
|
|
|
286 |
$settings->summaryformat = $formattedsummary[1];
|
|
|
287 |
$settings->frontpage = $CFG->frontpage;
|
|
|
288 |
$settings->frontpageloggedin = $CFG->frontpageloggedin;
|
|
|
289 |
$settings->maxcategorydepth = $CFG->maxcategorydepth;
|
|
|
290 |
$settings->frontpagecourselimit = $CFG->frontpagecourselimit;
|
|
|
291 |
$settings->numsections = course_get_format($SITE)->get_last_section_number();
|
|
|
292 |
$settings->newsitems = $SITE->newsitems;
|
|
|
293 |
$settings->commentsperpage = $CFG->commentsperpage;
|
|
|
294 |
|
|
|
295 |
// Now, admin settings.
|
|
|
296 |
if ($isadmin) {
|
|
|
297 |
$settings->defaultfrontpageroleid = $CFG->defaultfrontpageroleid;
|
|
|
298 |
}
|
|
|
299 |
}
|
|
|
300 |
|
|
|
301 |
if (empty($section) or $section == 'sitepolicies') {
|
|
|
302 |
$manager = new \core_privacy\local\sitepolicy\manager();
|
|
|
303 |
$settings->sitepolicy = ($sitepolicy = $manager->get_embed_url()) ? $sitepolicy->out(false) : '';
|
|
|
304 |
$settings->sitepolicyhandler = $CFG->sitepolicyhandler;
|
|
|
305 |
$settings->disableuserimages = $CFG->disableuserimages;
|
|
|
306 |
}
|
|
|
307 |
|
|
|
308 |
if (empty($section) or $section == 'gradessettings') {
|
|
|
309 |
require_once($CFG->dirroot . '/user/lib.php');
|
|
|
310 |
$settings->mygradesurl = user_mygrades_url();
|
|
|
311 |
// The previous function may return moodle_url instances or plain string URLs.
|
|
|
312 |
if ($settings->mygradesurl instanceof moodle_url) {
|
|
|
313 |
$settings->mygradesurl = $settings->mygradesurl->out(false);
|
|
|
314 |
}
|
|
|
315 |
}
|
|
|
316 |
|
|
|
317 |
if (empty($section) or $section == 'mobileapp') {
|
|
|
318 |
$settings->tool_mobile_forcelogout = get_config('tool_mobile', 'forcelogout');
|
|
|
319 |
$settings->tool_mobile_customlangstrings = get_config('tool_mobile', 'customlangstrings');
|
|
|
320 |
$settings->tool_mobile_disabledfeatures = get_config('tool_mobile', 'disabledfeatures');
|
|
|
321 |
$settings->tool_mobile_filetypeexclusionlist = get_config('tool_mobile', 'filetypeexclusionlist');
|
|
|
322 |
$settings->tool_mobile_custommenuitems = get_config('tool_mobile', 'custommenuitems');
|
|
|
323 |
$settings->tool_mobile_apppolicy = get_config('tool_mobile', 'apppolicy');
|
|
|
324 |
// This setting could be not set in some edge cases such as bad upgrade.
|
|
|
325 |
$mintimereq = get_config('tool_mobile', 'autologinmintimebetweenreq');
|
|
|
326 |
$mintimereq = empty($mintimereq) ? 6 * MINSECS : $mintimereq;
|
|
|
327 |
$settings->tool_mobile_autologinmintimebetweenreq = $mintimereq;
|
|
|
328 |
$settings->tool_mobile_autologout = get_config('tool_mobile', 'autologout');
|
|
|
329 |
$settings->tool_mobile_autologouttime = get_config('tool_mobile', 'autologouttime');
|
|
|
330 |
}
|
|
|
331 |
|
|
|
332 |
if (empty($section) or $section == 'calendar') {
|
|
|
333 |
$settings->calendartype = $CFG->calendartype;
|
|
|
334 |
$settings->calendar_site_timeformat = $CFG->calendar_site_timeformat;
|
|
|
335 |
$settings->calendar_startwday = $CFG->calendar_startwday;
|
|
|
336 |
$settings->calendar_adminseesall = $CFG->calendar_adminseesall;
|
|
|
337 |
$settings->calendar_lookahead = $CFG->calendar_lookahead;
|
|
|
338 |
$settings->calendar_maxevents = $CFG->calendar_maxevents;
|
|
|
339 |
}
|
|
|
340 |
|
|
|
341 |
if (empty($section) or $section == 'coursecolors') {
|
|
|
342 |
$colornumbers = range(1, 10);
|
|
|
343 |
foreach ($colornumbers as $number) {
|
|
|
344 |
$settings->{'core_admin_coursecolor' . $number} = get_config('core_admin', 'coursecolor' . $number);
|
|
|
345 |
}
|
|
|
346 |
}
|
|
|
347 |
|
|
|
348 |
if (empty($section) or $section == 'supportcontact') {
|
|
|
349 |
$settings->supportavailability = $CFG->supportavailability;
|
|
|
350 |
|
|
|
351 |
if ($CFG->supportavailability == CONTACT_SUPPORT_DISABLED) {
|
|
|
352 |
$settings->supportname = null;
|
|
|
353 |
$settings->supportemail = null;
|
|
|
354 |
$settings->supportpage = null;
|
|
|
355 |
} else {
|
|
|
356 |
$settings->supportname = $CFG->supportname;
|
|
|
357 |
$settings->supportemail = $CFG->supportemail ?? null;
|
|
|
358 |
$settings->supportpage = $CFG->supportpage;
|
|
|
359 |
}
|
|
|
360 |
}
|
|
|
361 |
|
|
|
362 |
if (empty($section) || $section === 'graceperiodsettings') {
|
|
|
363 |
$settings->coursegraceperiodafter = $CFG->coursegraceperiodafter;
|
|
|
364 |
$settings->coursegraceperiodbefore = $CFG->coursegraceperiodbefore;
|
|
|
365 |
}
|
|
|
366 |
|
|
|
367 |
if (empty($section) || $section === 'navigation') {
|
|
|
368 |
$settings->enabledashboard = $CFG->enabledashboard;
|
|
|
369 |
}
|
|
|
370 |
|
|
|
371 |
if (empty($section) || ($section === 'themesettings' || $section === 'themesettingsadvanced')) {
|
|
|
372 |
$settings->customusermenuitems = $CFG->customusermenuitems;
|
|
|
373 |
}
|
|
|
374 |
|
|
|
375 |
if (empty($section) || $section === 'locationsettings') {
|
|
|
376 |
$settings->timezone = $CFG->timezone;
|
|
|
377 |
$settings->forcetimezone = $CFG->forcetimezone;
|
|
|
378 |
}
|
|
|
379 |
|
|
|
380 |
if (empty($section) || $section === 'manageglobalsearch') {
|
|
|
381 |
$settings->searchengine = $CFG->searchengine;
|
|
|
382 |
$settings->searchenablecategories = $CFG->searchenablecategories;
|
|
|
383 |
$settings->searchdefaultcategory = $CFG->searchdefaultcategory;
|
|
|
384 |
$settings->searchhideallcategory = $CFG->searchhideallcategory;
|
|
|
385 |
$settings->searchmaxtopresults = $CFG->searchmaxtopresults;
|
|
|
386 |
$settings->searchbannerenable = $CFG->searchbannerenable;
|
|
|
387 |
$settings->searchbanner = \core_external\util::format_text(
|
|
|
388 |
$CFG->searchbanner, FORMAT_HTML, $context)[0];
|
|
|
389 |
}
|
|
|
390 |
|
|
|
391 |
if (empty($section) || $section === 'privacysettings') {
|
|
|
392 |
$settings->tool_dataprivacy_contactdataprotectionofficer = get_config('tool_dataprivacy', 'contactdataprotectionofficer');
|
|
|
393 |
$settings->tool_dataprivacy_showdataretentionsummary = get_config('tool_dataprivacy', 'showdataretentionsummary');
|
|
|
394 |
}
|
|
|
395 |
|
|
|
396 |
if (empty($section) || $section === 'blog') {
|
|
|
397 |
$settings->useblogassociations = $CFG->useblogassociations;
|
|
|
398 |
$settings->bloglevel = $CFG->bloglevel;
|
|
|
399 |
$settings->blogusecomments = $CFG->blogusecomments;
|
|
|
400 |
}
|
|
|
401 |
|
|
|
402 |
if (empty($section) || $section === 'h5psettings') {
|
|
|
403 |
\core_h5p\local\library\autoloader::register();
|
|
|
404 |
$customcss = \core_h5p\file_storage::get_custom_styles();
|
|
|
405 |
if (!empty($customcss)) {
|
|
|
406 |
$settings->h5pcustomcssurl = $customcss['cssurl']->out() . '?ver=' . $customcss['cssversion'];
|
|
|
407 |
}
|
|
|
408 |
}
|
|
|
409 |
|
|
|
410 |
return $settings;
|
|
|
411 |
}
|
|
|
412 |
|
|
|
413 |
/*
|
|
|
414 |
* Check if all the required conditions are met to allow the auto-login process continue.
|
|
|
415 |
*
|
|
|
416 |
* @param int $userid current user id
|
|
|
417 |
* @since Moodle 3.2
|
|
|
418 |
* @throws moodle_exception
|
|
|
419 |
*/
|
|
|
420 |
public static function check_autologin_prerequisites($userid) {
|
|
|
421 |
global $CFG;
|
|
|
422 |
|
|
|
423 |
if (!$CFG->enablewebservices or !$CFG->enablemobilewebservice) {
|
|
|
424 |
throw new moodle_exception('enablewsdescription', 'webservice');
|
|
|
425 |
}
|
|
|
426 |
|
|
|
427 |
if (!is_https()) {
|
|
|
428 |
throw new moodle_exception('httpsrequired', 'tool_mobile');
|
|
|
429 |
}
|
|
|
430 |
|
|
|
431 |
if (has_capability('moodle/site:config', context_system::instance(), $userid) or is_siteadmin($userid)) {
|
|
|
432 |
throw new moodle_exception('autologinnotallowedtoadmins', 'tool_mobile');
|
|
|
433 |
}
|
|
|
434 |
}
|
|
|
435 |
|
|
|
436 |
/**
|
|
|
437 |
* Creates an auto-login key for the current user, this key is restricted by time and ip address.
|
|
|
438 |
* This key is used for automatically login the user in the site when the Moodle app opens the site in a mobile browser.
|
|
|
439 |
*
|
|
|
440 |
* @return string the key
|
|
|
441 |
* @since Moodle 3.2
|
|
|
442 |
*/
|
|
|
443 |
public static function get_autologin_key() {
|
|
|
444 |
global $USER;
|
|
|
445 |
// Delete previous keys.
|
|
|
446 |
delete_user_key('tool_mobile', $USER->id);
|
|
|
447 |
|
|
|
448 |
// Create a new key.
|
|
|
449 |
$iprestriction = getremoteaddr();
|
|
|
450 |
$validuntil = time() + self::LOGIN_KEY_TTL;
|
|
|
451 |
return create_user_key('tool_mobile', $USER->id, null, $iprestriction, $validuntil);
|
|
|
452 |
}
|
|
|
453 |
|
|
|
454 |
/**
|
|
|
455 |
* Creates a QR login key for the current user, this key is restricted by time and ip address.
|
|
|
456 |
* This key is used for automatically login the user in the site when the user scans a QR code in the Moodle app.
|
|
|
457 |
*
|
|
|
458 |
* @param stdClass $mobilesettings mobile app plugin settings
|
|
|
459 |
* @return string the key
|
|
|
460 |
* @since Moodle 3.9
|
|
|
461 |
*/
|
|
|
462 |
public static function get_qrlogin_key(stdClass $mobilesettings) {
|
|
|
463 |
global $USER;
|
|
|
464 |
// Delete previous keys.
|
|
|
465 |
delete_user_key('tool_mobile/qrlogin', $USER->id);
|
|
|
466 |
|
|
|
467 |
// Create a new key.
|
|
|
468 |
$iprestriction = !empty($mobilesettings->qrsameipcheck) ? getremoteaddr(null) : null;
|
|
|
469 |
$qrkeyttl = !empty($mobilesettings->qrkeyttl) ? $mobilesettings->qrkeyttl : self::LOGIN_QR_KEY_TTL;
|
|
|
470 |
$validuntil = time() + $qrkeyttl;
|
|
|
471 |
return create_user_key('tool_mobile/qrlogin', $USER->id, null, $iprestriction, $validuntil);
|
|
|
472 |
}
|
|
|
473 |
|
|
|
474 |
/**
|
|
|
475 |
* Get a list of the Mobile app features.
|
|
|
476 |
*
|
|
|
477 |
* @return array array with the features grouped by theirs ubication in the app.
|
|
|
478 |
* @since Moodle 3.3
|
|
|
479 |
*/
|
|
|
480 |
public static function get_features_list() {
|
|
|
481 |
global $CFG;
|
|
|
482 |
require_once($CFG->libdir . '/authlib.php');
|
|
|
483 |
|
|
|
484 |
$general = new lang_string('general');
|
|
|
485 |
$mainmenu = new lang_string('mainmenu', 'tool_mobile');
|
|
|
486 |
$course = new lang_string('course');
|
|
|
487 |
$modules = new lang_string('managemodules');
|
|
|
488 |
$blocks = new lang_string('blocks');
|
|
|
489 |
$useraccount = new lang_string('useraccount');
|
|
|
490 |
$participants = new lang_string('participants');
|
|
|
491 |
$files = new lang_string('files');
|
|
|
492 |
$remoteaddons = new lang_string('remoteaddons', 'tool_mobile');
|
|
|
493 |
$identityproviders = new lang_string('oauth2identityproviders', 'tool_mobile');
|
|
|
494 |
|
|
|
495 |
$availablemods = core_plugin_manager::instance()->get_plugins_of_type('mod');
|
|
|
496 |
$coursemodules = array();
|
|
|
497 |
$appsupportedmodules = array(
|
|
|
498 |
'assign', 'bigbluebuttonbn', 'book', 'chat', 'choice', 'data', 'feedback', 'folder', 'forum', 'glossary', 'h5pactivity',
|
|
|
499 |
'imscp', 'label', 'lesson', 'lti', 'page', 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop');
|
|
|
500 |
|
|
|
501 |
foreach ($availablemods as $mod) {
|
|
|
502 |
if (in_array($mod->name, $appsupportedmodules)) {
|
|
|
503 |
$coursemodules['$mmCourseDelegate_mmaMod' . ucfirst($mod->name)] = $mod->displayname;
|
|
|
504 |
}
|
|
|
505 |
}
|
|
|
506 |
asort($coursemodules);
|
|
|
507 |
|
|
|
508 |
$remoteaddonslist = array();
|
|
|
509 |
$mobileplugins = self::get_plugins_supporting_mobile();
|
|
|
510 |
foreach ($mobileplugins as $plugin) {
|
|
|
511 |
$displayname = core_plugin_manager::instance()->plugin_name($plugin['component']) . " - " . $plugin['addon'];
|
|
|
512 |
$remoteaddonslist['sitePlugin_' . $plugin['component'] . '_' . $plugin['addon']] = $displayname;
|
|
|
513 |
|
|
|
514 |
}
|
|
|
515 |
|
|
|
516 |
// Display blocks.
|
|
|
517 |
$availableblocks = core_plugin_manager::instance()->get_plugins_of_type('block');
|
|
|
518 |
$courseblocks = array();
|
|
|
519 |
$appsupportedblocks = array(
|
|
|
520 |
'activity_modules' => 'CoreBlockDelegate_AddonBlockActivityModules',
|
|
|
521 |
'activity_results' => 'CoreBlockDelegate_AddonBlockActivityResults',
|
|
|
522 |
'site_main_menu' => 'CoreBlockDelegate_AddonBlockSiteMainMenu',
|
|
|
523 |
'myoverview' => 'CoreBlockDelegate_AddonBlockMyOverview',
|
|
|
524 |
'course_list' => 'CoreBlockDelegate_AddonBlockCourseList',
|
|
|
525 |
'timeline' => 'CoreBlockDelegate_AddonBlockTimeline',
|
|
|
526 |
'recentlyaccessedcourses' => 'CoreBlockDelegate_AddonBlockRecentlyAccessedCourses',
|
|
|
527 |
'starredcourses' => 'CoreBlockDelegate_AddonBlockStarredCourses',
|
|
|
528 |
'recentlyaccesseditems' => 'CoreBlockDelegate_AddonBlockRecentlyAccessedItems',
|
|
|
529 |
'badges' => 'CoreBlockDelegate_AddonBlockBadges',
|
|
|
530 |
'blog_menu' => 'CoreBlockDelegate_AddonBlockBlogMenu',
|
|
|
531 |
'blog_recent' => 'CoreBlockDelegate_AddonBlockBlogRecent',
|
|
|
532 |
'blog_tags' => 'CoreBlockDelegate_AddonBlockBlogTags',
|
|
|
533 |
'calendar_month' => 'CoreBlockDelegate_AddonBlockCalendarMonth',
|
|
|
534 |
'calendar_upcoming' => 'CoreBlockDelegate_AddonBlockCalendarUpcoming',
|
|
|
535 |
'comments' => 'CoreBlockDelegate_AddonBlockComments',
|
|
|
536 |
'completionstatus' => 'CoreBlockDelegate_AddonBlockCompletionStatus',
|
|
|
537 |
'feedback' => 'CoreBlockDelegate_AddonBlockFeedback',
|
|
|
538 |
'globalsearch' => 'CoreBlockDelegate_AddonBlockGlobalSearch',
|
|
|
539 |
'glossary_random' => 'CoreBlockDelegate_AddonBlockGlossaryRandom',
|
|
|
540 |
'html' => 'CoreBlockDelegate_AddonBlockHtml',
|
|
|
541 |
'lp' => 'CoreBlockDelegate_AddonBlockLp',
|
|
|
542 |
'news_items' => 'CoreBlockDelegate_AddonBlockNewsItems',
|
|
|
543 |
'online_users' => 'CoreBlockDelegate_AddonBlockOnlineUsers',
|
|
|
544 |
'private_files' => 'CoreBlockDelegate_AddonBlockPrivateFiles',
|
|
|
545 |
'recent_activity' => 'CoreBlockDelegate_AddonBlockRecentActivity',
|
|
|
546 |
'rss_client' => 'CoreBlockDelegate_AddonBlockRssClient',
|
|
|
547 |
'search_forums' => 'CoreBlockDelegate_AddonBlockSearchForums',
|
|
|
548 |
'selfcompletion' => 'CoreBlockDelegate_AddonBlockSelfCompletion',
|
|
|
549 |
'tags' => 'CoreBlockDelegate_AddonBlockTags',
|
|
|
550 |
);
|
|
|
551 |
|
|
|
552 |
foreach ($availableblocks as $block) {
|
|
|
553 |
if (isset($appsupportedblocks[$block->name])) {
|
|
|
554 |
$courseblocks[$appsupportedblocks[$block->name]] = $block->displayname;
|
|
|
555 |
}
|
|
|
556 |
}
|
|
|
557 |
asort($courseblocks);
|
|
|
558 |
|
|
|
559 |
$features = array(
|
|
|
560 |
"$general" => array(
|
|
|
561 |
'NoDelegate_CoreOffline' => new lang_string('offlineuse', 'tool_mobile'),
|
|
|
562 |
'NoDelegate_SiteBlocks' => new lang_string('blocks'),
|
|
|
563 |
'NoDelegate_CoreComments' => new lang_string('comments'),
|
|
|
564 |
'NoDelegate_CoreRating' => new lang_string('ratings', 'rating'),
|
|
|
565 |
'NoDelegate_CoreTag' => new lang_string('tags'),
|
|
|
566 |
'$mmLoginEmailSignup' => new lang_string('startsignup'),
|
|
|
567 |
'NoDelegate_ForgottenPassword' => new lang_string('forgotten'),
|
|
|
568 |
'NoDelegate_ResponsiveMainMenuItems' => new lang_string('responsivemainmenuitems', 'tool_mobile'),
|
|
|
569 |
'NoDelegate_H5POffline' => new lang_string('h5poffline', 'tool_mobile'),
|
|
|
570 |
'NoDelegate_DarkMode' => new lang_string('darkmode', 'tool_mobile'),
|
|
|
571 |
'CoreFilterDelegate' => new lang_string('type_filter_plural', 'plugin'),
|
|
|
572 |
'CoreReportBuilderDelegate' => new lang_string('reportbuilder', 'core_reportbuilder'),
|
|
|
573 |
'NoDelegate_CoreUserSupport' => new lang_string('contactsitesupport', 'admin'),
|
|
|
574 |
'NoDelegate_GlobalSearch' => new lang_string('globalsearch', 'search'),
|
|
|
575 |
),
|
|
|
576 |
"$mainmenu" => array(
|
|
|
577 |
'$mmSideMenuDelegate_mmaFrontpage' => new lang_string('sitehome'),
|
|
|
578 |
'CoreMainMenuDelegate_CoreCoursesDashboard' => new lang_string('myhome'),
|
|
|
579 |
'$mmSideMenuDelegate_mmCourses' => new lang_string('mycourses'),
|
|
|
580 |
'$mmSideMenuDelegate_mmaMessages' => new lang_string('messages', 'message'),
|
|
|
581 |
'$mmSideMenuDelegate_mmaNotifications' => new lang_string('notifications', 'message'),
|
|
|
582 |
'$mmSideMenuDelegate_mmaCalendar' => new lang_string('calendar', 'calendar'),
|
|
|
583 |
'CoreMainMenuDelegate_AddonBlog' => new lang_string('blog', 'blog'),
|
|
|
584 |
'CoreMainMenuDelegate_CoreTag' => new lang_string('tags'),
|
|
|
585 |
'CoreMainMenuDelegate_QrReader' => new lang_string('scanqrcode', 'tool_mobile'),
|
|
|
586 |
),
|
|
|
587 |
"$useraccount" => array(
|
|
|
588 |
'$mmSideMenuDelegate_mmaGrades' => new lang_string('grades', 'grades'),
|
|
|
589 |
'$mmSideMenuDelegate_mmaFiles' => new lang_string('files'),
|
|
|
590 |
'CoreUserDelegate_AddonBadges:account' => new lang_string('badges', 'badges'),
|
|
|
591 |
'CoreUserDelegate_AddonBlog:account' => new lang_string('blog', 'blog'),
|
|
|
592 |
'$mmSideMenuDelegate_mmaCompetency' => new lang_string('myplans', 'tool_lp'),
|
|
|
593 |
'CoreUserDelegate_CorePolicy' => new lang_string('policiesagreements', 'tool_policy'),
|
|
|
594 |
'CoreUserDelegate_CoreDataPrivacy' => new lang_string('pluginname', 'tool_dataprivacy'),
|
|
|
595 |
'NoDelegate_SwitchAccount' => new lang_string('switchaccount', 'tool_mobile'),
|
|
|
596 |
),
|
|
|
597 |
"$course" => array(
|
|
|
598 |
'$mmCoursesDelegate_mmaParticipants' => new lang_string('participants'),
|
|
|
599 |
'$mmCoursesDelegate_mmaGrades' => new lang_string('grades', 'grades'),
|
|
|
600 |
'$mmCoursesDelegate_mmaCompetency' => new lang_string('competencies', 'competency'),
|
|
|
601 |
'$mmCoursesDelegate_mmaNotes' => new lang_string('notes', 'notes'),
|
|
|
602 |
'$mmCoursesDelegate_mmaCourseCompletion' => new lang_string('coursecompletion', 'completion'),
|
|
|
603 |
'NoDelegate_CourseBlocks' => new lang_string('blocks'),
|
|
|
604 |
'CoreCourseOptionsDelegate_AddonBlog' => new lang_string('blog', 'blog'),
|
|
|
605 |
'$mmCoursesDelegate_search' => new lang_string('search'),
|
|
|
606 |
'NoDelegate_CoreCourseDownload' => new lang_string('downloadcourse', 'tool_mobile'),
|
|
|
607 |
'NoDelegate_CoreCoursesDownload' => new lang_string('downloadcourses', 'tool_mobile'),
|
|
|
608 |
),
|
|
|
609 |
"$participants" => array(
|
|
|
610 |
'$mmUserDelegate_mmaGrades:viewGrades' => new lang_string('grades', 'grades'),
|
|
|
611 |
'$mmUserDelegate_mmaCourseCompletion:viewCompletion' => new lang_string('coursecompletion', 'completion'),
|
|
|
612 |
'$mmUserDelegate_mmaBadges' => new lang_string('badges', 'badges'),
|
|
|
613 |
'$mmUserDelegate_mmaNotes:addNote' => new lang_string('notes', 'notes'),
|
|
|
614 |
'CoreUserDelegate_AddonBlog:blogs' => new lang_string('blog', 'blog'),
|
|
|
615 |
'$mmUserDelegate_mmaCompetency:learningPlan' => new lang_string('competencies', 'competency'),
|
|
|
616 |
'$mmUserDelegate_mmaMessages:sendMessage' => new lang_string('sendmessage', 'message'),
|
|
|
617 |
'$mmUserDelegate_picture' => new lang_string('userpic'),
|
|
|
618 |
),
|
|
|
619 |
"$files" => array(
|
|
|
620 |
'files_privatefiles' => new lang_string('privatefiles'),
|
|
|
621 |
'files_sitefiles' => new lang_string('sitefiles'),
|
|
|
622 |
'files_upload' => new lang_string('upload'),
|
|
|
623 |
),
|
|
|
624 |
"$modules" => $coursemodules,
|
|
|
625 |
"$blocks" => $courseblocks,
|
|
|
626 |
);
|
|
|
627 |
|
|
|
628 |
if (!empty($remoteaddonslist)) {
|
|
|
629 |
$features["$remoteaddons"] = $remoteaddonslist;
|
|
|
630 |
}
|
|
|
631 |
|
|
|
632 |
if (!empty($availablemods['lti'])) {
|
|
|
633 |
$ltidisplayname = $availablemods['lti']->displayname;
|
|
|
634 |
$features["$ltidisplayname"]['CoreCourseModuleDelegate_AddonModLti:launchViaSite'] =
|
|
|
635 |
new lang_string('launchviasiteinbrowser', 'tool_mobile');
|
|
|
636 |
}
|
|
|
637 |
|
|
|
638 |
// Display OAuth 2 identity providers.
|
|
|
639 |
if (is_enabled_auth('oauth2')) {
|
|
|
640 |
$identityproviderslist = array();
|
|
|
641 |
$idps = \auth_plugin_base::get_identity_providers(['oauth2']);
|
|
|
642 |
|
|
|
643 |
foreach ($idps as $idp) {
|
|
|
644 |
// Only add identity providers that have an ID.
|
|
|
645 |
$id = isset($idp['url']) ? $idp['url']->get_param('id') : null;
|
|
|
646 |
if ($id != null) {
|
|
|
647 |
$identityproviderslist['NoDelegate_IdentityProvider_' . $id] = $idp['name'];
|
|
|
648 |
}
|
|
|
649 |
}
|
|
|
650 |
|
|
|
651 |
if (!empty($identityproviderslist)) {
|
|
|
652 |
$features["$identityproviders"] = array();
|
|
|
653 |
|
|
|
654 |
if (count($identityproviderslist) > 1) {
|
|
|
655 |
// Include an option to disable them all.
|
|
|
656 |
$features["$identityproviders"]['NoDelegate_IdentityProviders'] = new lang_string('all');
|
|
|
657 |
}
|
|
|
658 |
|
|
|
659 |
$features["$identityproviders"] = array_merge($features["$identityproviders"], $identityproviderslist);
|
|
|
660 |
}
|
|
|
661 |
}
|
|
|
662 |
|
|
|
663 |
return $features;
|
|
|
664 |
}
|
|
|
665 |
|
|
|
666 |
/**
|
|
|
667 |
* This function check the current site for potential configuration issues that may prevent the mobile app to work.
|
|
|
668 |
*
|
|
|
669 |
* @return array list of potential issues
|
|
|
670 |
* @since Moodle 3.4
|
|
|
671 |
*/
|
|
|
672 |
public static function get_potential_config_issues() {
|
|
|
673 |
global $CFG;
|
|
|
674 |
require_once($CFG->dirroot . "/lib/filelib.php");
|
|
|
675 |
require_once($CFG->dirroot . '/message/lib.php');
|
|
|
676 |
|
|
|
677 |
$warnings = array();
|
|
|
678 |
|
|
|
679 |
if (is_https()) {
|
|
|
680 |
$curl = new curl();
|
|
|
681 |
// Return certificate information and verify the certificate.
|
|
|
682 |
$curl->setopt(array('CURLOPT_CERTINFO' => 1, 'CURLOPT_SSL_VERIFYPEER' => true));
|
|
|
683 |
// Check https using a page not redirecting or returning exceptions.
|
|
|
684 |
$curl->head("$CFG->wwwroot/$CFG->admin/tool/mobile/mobile.webmanifest.php");
|
|
|
685 |
$info = $curl->get_info();
|
|
|
686 |
|
|
|
687 |
// Check the certificate is not self-signed or has an untrusted-root.
|
|
|
688 |
// This may be weak in some scenarios (when the curl SSL verifier is outdated).
|
|
|
689 |
if (empty($info['http_code']) || empty($info['certinfo'])) {
|
|
|
690 |
$warnings[] = ['selfsignedoruntrustedcertificatewarning', 'tool_mobile'];
|
|
|
691 |
} else {
|
|
|
692 |
$timenow = time();
|
|
|
693 |
$infokeys = array_keys($info['certinfo']);
|
|
|
694 |
$lastkey = end($infokeys);
|
|
|
695 |
|
|
|
696 |
if (count($info['certinfo']) == 1) {
|
|
|
697 |
// This will work in a normal browser because it will complete the chain, but not in a mobile app.
|
|
|
698 |
$warnings[] = ['invalidcertificatechainwarning', 'tool_mobile'];
|
|
|
699 |
}
|
|
|
700 |
|
|
|
701 |
foreach ($info['certinfo'] as $key => $cert) {
|
|
|
702 |
// Convert to lower case the keys, some OS/curl implementations differ.
|
|
|
703 |
$cert = array_change_key_case($cert, CASE_LOWER);
|
|
|
704 |
|
|
|
705 |
// Due to a bug in certain curl/openssl versions the signature algorithm isn't always correctly parsed.
|
|
|
706 |
// See https://github.com/curl/curl/issues/3706 for reference.
|
|
|
707 |
if (!array_key_exists('signature algorithm', $cert)) {
|
|
|
708 |
// The malformed field that does contain the algorithm we're looking for looks like the following:
|
|
|
709 |
// <WHITESPACE>Signature Algorithm: <ALGORITHM><CRLF><ALGORITHM>.
|
|
|
710 |
preg_match('/\s+Signature Algorithm: (?<algorithm>[^\s]+)/', $cert['public key algorithm'], $matches);
|
|
|
711 |
|
|
|
712 |
$signaturealgorithm = $matches['algorithm'] ?? '';
|
|
|
713 |
} else {
|
|
|
714 |
$signaturealgorithm = $cert['signature algorithm'];
|
|
|
715 |
}
|
|
|
716 |
|
|
|
717 |
// Check if the signature algorithm is weak (Android won't work with SHA-1).
|
|
|
718 |
if ($key != $lastkey &&
|
|
|
719 |
($signaturealgorithm == 'sha1WithRSAEncryption' || $signaturealgorithm == 'sha1WithRSA')) {
|
|
|
720 |
$warnings['insecurealgorithmwarning'] = ['insecurealgorithmwarning', 'tool_mobile'];
|
|
|
721 |
}
|
|
|
722 |
// Check certificate start date.
|
|
|
723 |
if (strtotime($cert['start date']) > $timenow) {
|
|
|
724 |
$warnings['invalidcertificatestartdatewarning'] = ['invalidcertificatestartdatewarning', 'tool_mobile'];
|
|
|
725 |
}
|
|
|
726 |
// Check certificate end date.
|
|
|
727 |
if (strtotime($cert['expire date']) < $timenow) {
|
|
|
728 |
$warnings['invalidcertificateexpiredatewarning'] = ['invalidcertificateexpiredatewarning', 'tool_mobile'];
|
|
|
729 |
}
|
|
|
730 |
}
|
|
|
731 |
}
|
|
|
732 |
} else {
|
|
|
733 |
// Warning for non https sites.
|
|
|
734 |
$warnings[] = ['nohttpsformobilewarning', 'admin'];
|
|
|
735 |
}
|
|
|
736 |
|
|
|
737 |
// Check ADOdb debug enabled.
|
|
|
738 |
if (get_config('auth_db', 'debugauthdb') || get_config('enrol_database', 'debugdb')) {
|
|
|
739 |
$warnings[] = ['adodbdebugwarning', 'tool_mobile'];
|
|
|
740 |
}
|
|
|
741 |
// Check display errors on.
|
|
|
742 |
if (!empty($CFG->debugdisplay)) {
|
|
|
743 |
$warnings[] = ['displayerrorswarning', 'tool_mobile'];
|
|
|
744 |
}
|
|
|
745 |
// Check mobile notifications.
|
|
|
746 |
$processors = get_message_processors();
|
|
|
747 |
$enabled = false;
|
|
|
748 |
foreach ($processors as $processor => $status) {
|
|
|
749 |
if ($processor == 'airnotifier' && $status->enabled) {
|
|
|
750 |
$enabled = true;
|
|
|
751 |
}
|
|
|
752 |
}
|
|
|
753 |
if (!$enabled) {
|
|
|
754 |
$warnings[] = ['mobilenotificationsdisabledwarning', 'tool_mobile'];
|
|
|
755 |
}
|
|
|
756 |
|
|
|
757 |
return $warnings;
|
|
|
758 |
}
|
|
|
759 |
|
|
|
760 |
/**
|
|
|
761 |
* Generates a QR code with the site URL or for automatic login from the mobile app.
|
|
|
762 |
*
|
|
|
763 |
* @param stdClass $mobilesettings tool_mobile settings
|
|
|
764 |
* @return string base64 data image contents, null if qr disabled
|
|
|
765 |
*/
|
|
|
766 |
public static function generate_login_qrcode(stdClass $mobilesettings) {
|
|
|
767 |
global $CFG, $USER;
|
|
|
768 |
|
|
|
769 |
if ($mobilesettings->qrcodetype == static::QR_CODE_DISABLED) {
|
|
|
770 |
return null;
|
|
|
771 |
}
|
|
|
772 |
|
|
|
773 |
$urlscheme = !empty($mobilesettings->forcedurlscheme) ? $mobilesettings->forcedurlscheme : 'moodlemobile';
|
|
|
774 |
$data = $urlscheme . '://' . $CFG->wwwroot;
|
|
|
775 |
|
|
|
776 |
if ($mobilesettings->qrcodetype == static::QR_CODE_LOGIN) {
|
|
|
777 |
$qrloginkey = static::get_qrlogin_key($mobilesettings);
|
|
|
778 |
$data .= '?qrlogin=' . $qrloginkey . '&userid=' . $USER->id;
|
|
|
779 |
}
|
|
|
780 |
|
|
|
781 |
$qrcode = new core_qrcode($data);
|
|
|
782 |
$imagedata = 'data:image/png;base64,' . base64_encode($qrcode->getBarcodePngData(5, 5));
|
|
|
783 |
|
|
|
784 |
return $imagedata;
|
|
|
785 |
}
|
|
|
786 |
|
|
|
787 |
/**
|
|
|
788 |
* Gets Moodle app plan subscription information for the current site as it is returned by the Apps Portal.
|
|
|
789 |
*
|
|
|
790 |
* @return array Subscription information
|
|
|
791 |
*/
|
|
|
792 |
public static function get_subscription_information(): ?array {
|
|
|
793 |
global $CFG;
|
|
|
794 |
|
|
|
795 |
// Use session cache to prevent multiple requests.
|
|
|
796 |
$cache = \cache::make('tool_mobile', 'subscriptiondata');
|
|
|
797 |
$subscriptiondata = $cache->get(0);
|
|
|
798 |
if ($subscriptiondata !== false) {
|
|
|
799 |
return $subscriptiondata;
|
|
|
800 |
}
|
|
|
801 |
|
|
|
802 |
$mobilesettings = get_config('tool_mobile');
|
|
|
803 |
|
|
|
804 |
// To validate that the requests come from this site we need to send some private information that only is known by the
|
|
|
805 |
// Moodle Apps portal or the Sites registration database.
|
|
|
806 |
$credentials = [];
|
|
|
807 |
|
|
|
808 |
if (!empty($CFG->airnotifieraccesskey)) {
|
|
|
809 |
$credentials[] = ['type' => 'airnotifieraccesskey', 'value' => $CFG->airnotifieraccesskey];
|
|
|
810 |
}
|
|
|
811 |
if (\core\hub\registration::is_registered()) {
|
|
|
812 |
$credentials[] = ['type' => 'siteid', 'value' => $CFG->siteidentifier];
|
|
|
813 |
}
|
|
|
814 |
// Generate a hash key for validating that the request is coming from this site via WS.
|
|
|
815 |
$key = complex_random_string(32);
|
|
|
816 |
$sitesubscriptionkey = json_encode(['validuntil' => time() + 10 * MINSECS, 'key' => $key]);
|
|
|
817 |
set_config('sitesubscriptionkey', $sitesubscriptionkey, 'tool_mobile');
|
|
|
818 |
$credentials[] = ['type' => 'sitesubscriptionkey', 'value' => $key];
|
|
|
819 |
|
|
|
820 |
// Parameters for the WebService returning site information.
|
|
|
821 |
$androidappid = empty($mobilesettings->androidappid) ? static::DEFAULT_ANDROID_APP_ID : $mobilesettings->androidappid;
|
|
|
822 |
$iosappid = empty($mobilesettings->iosappid) ? static::DEFAULT_IOS_APP_ID : $mobilesettings->iosappid;
|
|
|
823 |
$fnparams = (object) [
|
|
|
824 |
'siteurl' => $CFG->wwwroot,
|
|
|
825 |
'appids' => [$androidappid, $iosappid],
|
|
|
826 |
'credentials' => $credentials,
|
|
|
827 |
];
|
|
|
828 |
// Prepare the arguments for a request to the AJAX nologin endpoint.
|
|
|
829 |
$args = [
|
|
|
830 |
(object) [
|
|
|
831 |
'index' => 0,
|
|
|
832 |
'methodname' => 'local_apps_get_site_info',
|
|
|
833 |
'args' => $fnparams,
|
|
|
834 |
]
|
|
|
835 |
];
|
|
|
836 |
|
|
|
837 |
// Ask the Moodle Apps Portal for the subscription information.
|
|
|
838 |
$curl = new curl();
|
|
|
839 |
$curl->setopt(array('CURLOPT_TIMEOUT' => 10, 'CURLOPT_CONNECTTIMEOUT' => 10));
|
|
|
840 |
|
|
|
841 |
$serverurl = static::MOODLE_APPS_PORTAL_URL . "/lib/ajax/service-nologin.php";
|
|
|
842 |
$query = 'args=' . urlencode(json_encode($args));
|
|
|
843 |
$wsresponse = @json_decode($curl->post($serverurl, $query), true);
|
|
|
844 |
|
|
|
845 |
$info = $curl->get_info();
|
|
|
846 |
if ($curlerrno = $curl->get_errno()) {
|
|
|
847 |
// CURL connection error.
|
|
|
848 |
debugging("Unexpected response from the Moodle Apps Portal server, CURL error number: $curlerrno");
|
|
|
849 |
return null;
|
|
|
850 |
} else if ($info['http_code'] != 200) {
|
|
|
851 |
// Unexpected error from server.
|
|
|
852 |
debugging('Unexpected response from the Moodle Apps Portal server, HTTP code:' . $info['httpcode']);
|
|
|
853 |
return null;
|
|
|
854 |
} else if (!empty($wsresponse[0]['error'])) {
|
|
|
855 |
// Unexpected error from Moodle Apps Portal.
|
|
|
856 |
debugging('Unexpected response from the Moodle Apps Portal server:' . json_encode($wsresponse[0]));
|
|
|
857 |
return null;
|
|
|
858 |
} else if (empty($wsresponse[0]['data'])) {
|
|
|
859 |
debugging('Unexpected response from the Moodle Apps Portal server:' . json_encode($wsresponse));
|
|
|
860 |
return null;
|
|
|
861 |
}
|
|
|
862 |
|
|
|
863 |
$cache->set(0, $wsresponse[0]['data']);
|
|
|
864 |
|
|
|
865 |
return $wsresponse[0]['data'];
|
|
|
866 |
}
|
|
|
867 |
}
|