MDL-65547 tool_mobile: New WS for generating tokens with qr login keys
[moodle.git] / admin / tool / mobile / classes / api.php
CommitLineData
b2478ed0
JL
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 */
25namespace tool_mobile;
26
27use core_component;
28use core_plugin_manager;
0002056f 29use context_system;
c951f1fe 30use moodle_url;
961c9549 31use moodle_exception;
b2551b4c 32use lang_string;
66a159f8 33use curl;
9df51510
JL
34use core_qrcode;
35use stdClass;
b2478ed0
JL
36
37/**
b2551b4c 38 * API exposed by tool_mobile, to be used mostly by external functions and the plugin settings.
b2478ed0
JL
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 */
44class api {
45
c951f1fe
JL
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;
961c9549
JL
52 /** @var int seconds an auto-login key will expire. */
53 const LOGIN_KEY_TTL = 60;
e624a2c8
JL
54 /** @var string URL of the Moodle Apps Portal */
55 const MOODLE_APPS_PORTAL_URL = 'https://apps.moodle.com';
9df51510
JL
56 /** @var int seconds a QR login key will expire. */
57 const LOGIN_QR_KEY_TTL = 600;
c951f1fe 58
b2478ed0
JL
59 /**
60 * Returns a list of Moodle plugins supporting the mobile app.
61 *
62 * @return array an array of objects containing the plugin information
63 */
64 public static function get_plugins_supporting_mobile() {
65 global $CFG;
66 require_once($CFG->libdir . '/adminlib.php');
67
7b1c55ea
JL
68 $cachekey = 'mobileplugins';
69 if (!isloggedin()) {
70 $cachekey = 'authmobileplugins'; // Use a different cache for not logged users.
71 }
72
30fccfcd
SK
73 // Check if we can return this from cache.
74 $cache = \cache::make('tool_mobile', 'plugininfo');
7b1c55ea 75 $pluginsinfo = $cache->get($cachekey);
30fccfcd
SK
76 if ($pluginsinfo !== false) {
77 return (array)$pluginsinfo;
78 }
79
b2478ed0 80 $pluginsinfo = [];
7b1c55ea
JL
81 // For not logged users return only auth plugins.
82 // This is to avoid anyone (not being a registered user) to obtain and download all the site remote add-ons.
83 if (!isloggedin()) {
84 $plugintypes = array('auth' => $CFG->dirroot.'/auth');
85 } else {
86 $plugintypes = core_component::get_plugin_types();
87 }
b2478ed0
JL
88
89 foreach ($plugintypes as $plugintype => $unused) {
90 // We need to include files here.
91 $pluginswithfile = core_component::get_plugin_list_with_file($plugintype, 'db' . DIRECTORY_SEPARATOR . 'mobile.php');
92 foreach ($pluginswithfile as $plugin => $notused) {
93 $path = core_component::get_plugin_directory($plugintype, $plugin);
94 $component = $plugintype . '_' . $plugin;
95 $version = get_component_version($component);
96
30fccfcd 97 require("$path/db/mobile.php");
b2478ed0 98 foreach ($addons as $addonname => $addoninfo) {
73d85fee
JL
99
100 // Add handlers (for site add-ons).
101 $handlers = !empty($addoninfo['handlers']) ? $addoninfo['handlers'] : array();
102 $handlers = json_encode($handlers); // JSON formatted, since it is a complex structure that may vary over time.
103
104 // Now language strings used by the app.
105 $lang = array();
106 if (!empty($addoninfo['lang'])) {
107 $stringmanager = get_string_manager();
8c5850a5 108 $langs = $stringmanager->get_list_of_translations(true);
73d85fee
JL
109 foreach ($langs as $langid => $langname) {
110 foreach ($addoninfo['lang'] as $stringinfo) {
111 $lang[$langid][$stringinfo[0]] =
112 $stringmanager->get_string($stringinfo[0], $stringinfo[1], null, $langid);
113 }
114 }
115 }
116 $lang = json_encode($lang);
117
b2478ed0
JL
118 $plugininfo = array(
119 'component' => $component,
120 'version' => $version,
121 'addon' => $addonname,
b4baee42 122 'dependencies' => !empty($addoninfo['dependencies']) ? $addoninfo['dependencies'] : array(),
b2478ed0
JL
123 'fileurl' => '',
124 'filehash' => '',
73d85fee
JL
125 'filesize' => 0,
126 'handlers' => $handlers,
127 'lang' => $lang,
b2478ed0
JL
128 );
129
130 // All the mobile packages must be under the plugin mobile directory.
1c668c3c 131 $package = $path . '/mobile/' . $addonname . '.zip';
b2478ed0
JL
132 if (file_exists($package)) {
133 $plugininfo['fileurl'] = $CFG->wwwroot . '' . str_replace($CFG->dirroot, '', $package);
134 $plugininfo['filehash'] = sha1_file($package);
135 $plugininfo['filesize'] = filesize($package);
136 }
137 $pluginsinfo[] = $plugininfo;
138 }
139 }
140 }
30fccfcd 141
7b1c55ea 142 $cache->set($cachekey, $pluginsinfo);
30fccfcd 143
b2478ed0
JL
144 return $pluginsinfo;
145 }
146
0002056f
JL
147 /**
148 * Returns a list of the site public settings, those not requiring authentication.
149 *
150 * @return array with the settings and warnings
151 */
7c1cb3bf 152 public static function get_public_config() {
5d5e30c6 153 global $CFG, $SITE, $PAGE, $OUTPUT;
851b2919 154 require_once($CFG->libdir . '/authlib.php');
0002056f
JL
155
156 $context = context_system::instance();
157 // We need this to make work the format text functions.
158 $PAGE->set_context($context);
159
e2fe3bc0
JL
160 list($authinstructions, $notusedformat) = external_format_text($CFG->auth_instructions, FORMAT_MOODLE, $context->id);
161 list($maintenancemessage, $notusedformat) = external_format_text($CFG->maintenance_message, FORMAT_MOODLE, $context->id);
0002056f
JL
162 $settings = array(
163 'wwwroot' => $CFG->wwwroot,
672f4836 164 'httpswwwroot' => $CFG->wwwroot,
0002056f
JL
165 'sitename' => external_format_string($SITE->fullname, $context->id, true),
166 'guestlogin' => $CFG->guestloginbutton,
167 'rememberusername' => $CFG->rememberusername,
168 'authloginviaemail' => $CFG->authloginviaemail,
169 'registerauth' => $CFG->registerauth,
2479a7c4 170 'forgottenpasswordurl' => clean_param($CFG->forgottenpasswordurl, PARAM_URL), // We may expect a mailto: here.
e2fe3bc0 171 'authinstructions' => $authinstructions,
0002056f
JL
172 'authnoneenabled' => (int) is_enabled_auth('none'),
173 'enablewebservices' => $CFG->enablewebservices,
174 'enablemobilewebservice' => $CFG->enablemobilewebservice,
175 'maintenanceenabled' => $CFG->maintenance_enabled,
e2fe3bc0 176 'maintenancemessage' => $maintenancemessage,
91fff391 177 'mobilecssurl' => !empty($CFG->mobilecssurl) ? $CFG->mobilecssurl : '',
b2551b4c 178 'tool_mobile_disabledfeatures' => get_config('tool_mobile', 'disabledfeatures'),
8d9dc60b
JL
179 'country' => clean_param($CFG->country, PARAM_NOTAGS),
180 'agedigitalconsentverification' => \core_auth\digital_consent::is_age_digital_consent_verification_enabled(),
e236259d
JL
181 'autolang' => $CFG->autolang,
182 'lang' => clean_param($CFG->lang, PARAM_LANG), // Avoid breaking WS because of incorrect package langs.
183 'langmenu' => $CFG->langmenu,
184 'langlist' => $CFG->langlist,
185 'locale' => $CFG->locale,
4bf08f5b 186 'tool_mobile_minimumversion' => get_config('tool_mobile', 'minimumversion'),
12a289c7
JL
187 'tool_mobile_iosappid' => get_config('tool_mobile', 'iosappid'),
188 'tool_mobile_androidappid' => get_config('tool_mobile', 'androidappid'),
189 'tool_mobile_setuplink' => clean_param(get_config('tool_mobile', 'setuplink'), PARAM_URL),
0002056f 190 );
c951f1fe
JL
191
192 $typeoflogin = get_config('tool_mobile', 'typeoflogin');
193 // Not found, edge case.
194 if ($typeoflogin === false) {
195 $typeoflogin = self::LOGIN_VIA_APP; // Defaults to via app.
196 }
197 $settings['typeoflogin'] = $typeoflogin;
198
851b2919
JL
199 // Check if the user can sign-up to return the launch URL in that case.
200 $cansignup = signup_is_enabled();
201
b1037978
JL
202 $url = new moodle_url("/$CFG->admin/tool/mobile/launch.php");
203 $settings['launchurl'] = $url->out(false);
5d5e30c6 204
6703e031
JL
205 // Check that we are receiving a moodle_url object, themes can override get_logo_url and may return incorrect values.
206 if (($logourl = $OUTPUT->get_logo_url()) && $logourl instanceof moodle_url) {
818d40a9 207 $settings['logourl'] = clean_param($logourl->out(false), PARAM_URL);
5d5e30c6 208 }
6703e031 209 if (($compactlogourl = $OUTPUT->get_compact_logo_url()) && $compactlogourl instanceof moodle_url) {
818d40a9 210 $settings['compactlogourl'] = clean_param($compactlogourl->out(false), PARAM_URL);
5d5e30c6
JL
211 }
212
a67e3bda
JL
213 // Identity providers.
214 $authsequence = get_enabled_auth_plugins(true);
215 $identityproviders = \auth_plugin_base::get_identity_providers($authsequence);
216 $identityprovidersdata = \auth_plugin_base::prepare_identity_providers_for_output($identityproviders, $OUTPUT);
217 if (!empty($identityprovidersdata)) {
218 $settings['identityproviders'] = $identityprovidersdata;
62a08b1d
JL
219 // Clean URLs to avoid breaking Web Services.
220 // We can't do it in prepare_identity_providers_for_output() because it may break the web output.
221 foreach ($settings['identityproviders'] as &$ip) {
222 $ip['url'] = (!empty($ip['url'])) ? clean_param($ip['url'], PARAM_URL) : '';
223 $ip['iconurl'] = (!empty($ip['iconurl'])) ? clean_param($ip['iconurl'], PARAM_URL) : '';
224 }
a67e3bda
JL
225 }
226
8d9dc60b
JL
227 // If age is verified, return also the admin contact details.
228 if ($settings['agedigitalconsentverification']) {
229 $settings['supportname'] = clean_param($CFG->supportname, PARAM_NOTAGS);
230 $settings['supportemail'] = clean_param($CFG->supportemail, PARAM_EMAIL);
231 }
232
0002056f
JL
233 return $settings;
234 }
235
6b492628
JL
236 /**
237 * Returns a list of site configurations, filtering by section.
238 *
239 * @param string $section section name
240 * @return stdClass object containing the settings
241 */
242 public static function get_config($section) {
243 global $CFG, $SITE;
244
245 $settings = new \stdClass;
246 $context = context_system::instance();
247 $isadmin = has_capability('moodle/site:config', $context);
248
249 if (empty($section) or $section == 'frontpagesettings') {
250 require_once($CFG->dirroot . '/course/format/lib.php');
251 // First settings that anyone can deduce.
e2fe3bc0
JL
252 $settings->fullname = external_format_string($SITE->fullname, $context->id);
253 $settings->shortname = external_format_string($SITE->shortname, $context->id);
b14a04e0
DM
254
255 // Return to a var instead of directly to $settings object because of differences between
256 // list() in php5 and php7. {@link http://php.net/manual/en/function.list.php}
257 $formattedsummary = external_format_text($SITE->summary, $SITE->summaryformat,
e2fe3bc0 258 $context->id);
b14a04e0
DM
259 $settings->summary = $formattedsummary[0];
260 $settings->summaryformat = $formattedsummary[1];
6b492628
JL
261 $settings->frontpage = $CFG->frontpage;
262 $settings->frontpageloggedin = $CFG->frontpageloggedin;
263 $settings->maxcategorydepth = $CFG->maxcategorydepth;
264 $settings->frontpagecourselimit = $CFG->frontpagecourselimit;
89b909f6 265 $settings->numsections = course_get_format($SITE)->get_last_section_number();
6b492628
JL
266 $settings->newsitems = $SITE->newsitems;
267 $settings->commentsperpage = $CFG->commentsperpage;
268
269 // Now, admin settings.
270 if ($isadmin) {
271 $settings->defaultfrontpageroleid = $CFG->defaultfrontpageroleid;
272 }
273 }
274
275 if (empty($section) or $section == 'sitepolicies') {
1727c939
MG
276 $manager = new \core_privacy\local\sitepolicy\manager();
277 $settings->sitepolicy = ($sitepolicy = $manager->get_embed_url()) ? $sitepolicy->out(false) : '';
278 $settings->sitepolicyhandler = $CFG->sitepolicyhandler;
6b492628
JL
279 $settings->disableuserimages = $CFG->disableuserimages;
280 }
281
282 if (empty($section) or $section == 'gradessettings') {
283 require_once($CFG->dirroot . '/user/lib.php');
6703e031
JL
284 $settings->mygradesurl = user_mygrades_url();
285 // The previous function may return moodle_url instances or plain string URLs.
286 if ($settings->mygradesurl instanceof moodle_url) {
287 $settings->mygradesurl = $settings->mygradesurl->out(false);
288 }
6b492628
JL
289 }
290
7bdcf970
JL
291 if (empty($section) or $section == 'mobileapp') {
292 $settings->tool_mobile_forcelogout = get_config('tool_mobile', 'forcelogout');
af1b6043 293 $settings->tool_mobile_customlangstrings = get_config('tool_mobile', 'customlangstrings');
b2551b4c 294 $settings->tool_mobile_disabledfeatures = get_config('tool_mobile', 'disabledfeatures');
63d142e2 295 $settings->tool_mobile_custommenuitems = get_config('tool_mobile', 'custommenuitems');
04df75ce 296 $settings->tool_mobile_apppolicy = get_config('tool_mobile', 'apppolicy');
7bdcf970
JL
297 }
298
ab1b8238
JL
299 if (empty($section) or $section == 'calendar') {
300 $settings->calendartype = $CFG->calendartype;
301 $settings->calendar_site_timeformat = $CFG->calendar_site_timeformat;
302 $settings->calendar_startwday = $CFG->calendar_startwday;
303 $settings->calendar_adminseesall = $CFG->calendar_adminseesall;
304 $settings->calendar_lookahead = $CFG->calendar_lookahead;
305 $settings->calendar_maxevents = $CFG->calendar_maxevents;
306 }
307
666de979
JL
308 if (empty($section) or $section == 'coursecolors') {
309 $colornumbers = range(1, 10);
310 foreach ($colornumbers as $number) {
311 $settings->{'core_admin_coursecolor' . $number} = get_config('core_admin', 'coursecolor' . $number);
312 }
313 }
314
6b492628
JL
315 return $settings;
316 }
317
961c9549
JL
318 /*
319 * Check if all the required conditions are met to allow the auto-login process continue.
320 *
321 * @param int $userid current user id
322 * @since Moodle 3.2
323 * @throws moodle_exception
324 */
325 public static function check_autologin_prerequisites($userid) {
326 global $CFG;
327
328 if (!$CFG->enablewebservices or !$CFG->enablemobilewebservice) {
329 throw new moodle_exception('enablewsdescription', 'webservice');
330 }
331
332 if (!is_https()) {
333 throw new moodle_exception('httpsrequired', 'tool_mobile');
334 }
335
336 if (has_capability('moodle/site:config', context_system::instance(), $userid) or is_siteadmin($userid)) {
337 throw new moodle_exception('autologinnotallowedtoadmins', 'tool_mobile');
338 }
339 }
340
341 /**
342 * Creates an auto-login key for the current user, this key is restricted by time and ip address.
9df51510 343 * This key is used for automatically login the user in the site when the Moodle app opens the site in a mobile browser.
961c9549
JL
344 *
345 * @return string the key
346 * @since Moodle 3.2
347 */
348 public static function get_autologin_key() {
349 global $USER;
350 // Delete previous keys.
351 delete_user_key('tool_mobile', $USER->id);
352
353 // Create a new key.
354 $iprestriction = getremoteaddr();
355 $validuntil = time() + self::LOGIN_KEY_TTL;
356 return create_user_key('tool_mobile', $USER->id, null, $iprestriction, $validuntil);
357 }
b2551b4c 358
9df51510
JL
359 /**
360 * Creates a QR login key for the current user, this key is restricted by time and ip address.
361 * This key is used for automatically login the user in the site when the user scans a QR code in the Moodle app.
362 *
363 * @return string the key
364 * @since Moodle 3.9
365 */
366 public static function get_qrlogin_key() {
367 global $USER;
368 // Delete previous keys.
369 delete_user_key('tool_mobile', $USER->id);
370
371 // Create a new key.
372 $iprestriction = getremoteaddr(null);
373 $validuntil = time() + self::LOGIN_QR_KEY_TTL;
374 return create_user_key('tool_mobile', $USER->id, null, $iprestriction, $validuntil);
375 }
376
b2551b4c
JL
377 /**
378 * Get a list of the Mobile app features.
379 *
380 * @return array array with the features grouped by theirs ubication in the app.
381 * @since Moodle 3.3
382 */
383 public static function get_features_list() {
384 global $CFG;
29aeab4e 385 require_once($CFG->libdir . '/authlib.php');
b2551b4c
JL
386
387 $general = new lang_string('general');
388 $mainmenu = new lang_string('mainmenu', 'tool_mobile');
389 $course = new lang_string('course');
390 $modules = new lang_string('managemodules');
c1720141 391 $blocks = new lang_string('blocks');
b2551b4c
JL
392 $user = new lang_string('user');
393 $files = new lang_string('files');
394 $remoteaddons = new lang_string('remoteaddons', 'tool_mobile');
29aeab4e 395 $identityproviders = new lang_string('oauth2identityproviders', 'tool_mobile');
b2551b4c
JL
396
397 $availablemods = core_plugin_manager::instance()->get_plugins_of_type('mod');
398 $coursemodules = array();
c3e745b7 399 $appsupportedmodules = array('assign', 'book', 'chat', 'choice', 'data', 'feedback', 'folder', 'forum', 'glossary', 'imscp',
d808c220
JL
400 'label', 'lesson', 'lti', 'page', 'quiz', 'resource', 'scorm', 'survey', 'url', 'wiki', 'workshop');
401
b2551b4c
JL
402 foreach ($availablemods as $mod) {
403 if (in_array($mod->name, $appsupportedmodules)) {
404 $coursemodules['$mmCourseDelegate_mmaMod' . ucfirst($mod->name)] = $mod->displayname;
405 }
406 }
bf258846 407 asort($coursemodules);
b2551b4c
JL
408
409 $remoteaddonslist = array();
410 $mobileplugins = self::get_plugins_supporting_mobile();
411 foreach ($mobileplugins as $plugin) {
412 $displayname = core_plugin_manager::instance()->plugin_name($plugin['component']) . " - " . $plugin['addon'];
d6c06935 413 $remoteaddonslist['sitePlugin_' . $plugin['component'] . '_' . $plugin['addon']] = $displayname;
b2551b4c
JL
414
415 }
416
c1720141
JL
417 // Display blocks.
418 $availableblocks = core_plugin_manager::instance()->get_plugins_of_type('block');
419 $courseblocks = array();
420 $appsupportedblocks = array(
421 'activity_modules' => 'CoreBlockDelegate_AddonBlockActivityModules',
422 'site_main_menu' => 'CoreBlockDelegate_AddonBlockSiteMainMenu',
423 'myoverview' => 'CoreBlockDelegate_AddonBlockMyOverview',
424 'timeline' => 'CoreBlockDelegate_AddonBlockTimeline',
425 'recentlyaccessedcourses' => 'CoreBlockDelegate_AddonBlockRecentlyAccessedCourses',
426 'starredcourses' => 'CoreBlockDelegate_AddonBlockStarredCourses',
427 'recentlyaccesseditems' => 'CoreBlockDelegate_AddonBlockRecentlyAccessedItems',
bf258846
JL
428 'badges' => 'CoreBlockDelegate_AddonBlockBadges',
429 'blog_menu' => 'CoreBlockDelegate_AddonBlockBlogMenu',
430 'blog_recent' => 'CoreBlockDelegate_AddonBlockBlogRecent',
431 'blog_tags' => 'CoreBlockDelegate_AddonBlockBlogTags',
432 'calendar_month' => 'CoreBlockDelegate_AddonBlockCalendarMonth',
433 'calendar_upcoming' => 'CoreBlockDelegate_AddonBlockCalendarUpcoming',
434 'comments' => 'CoreBlockDelegate_AddonBlockComments',
435 'completionstatus' => 'CoreBlockDelegate_AddonBlockCompletionStatus',
436 'feedback' => 'CoreBlockDelegate_AddonBlockFeedback',
437 'glossary_random' => 'CoreBlockDelegate_AddonBlockGlossaryRandom',
438 'html' => 'CoreBlockDelegate_AddonBlockHtml',
439 'lp' => 'CoreBlockDelegate_AddonBlockLp',
440 'news_items' => 'CoreBlockDelegate_AddonBlockNewsItems',
441 'online_users' => 'CoreBlockDelegate_AddonBlockOnlineUsers',
442 'selfcompletion' => 'CoreBlockDelegate_AddonBlockSelfCompletion',
443 'tags' => 'CoreBlockDelegate_AddonBlockTags',
c1720141
JL
444 );
445
446 foreach ($availableblocks as $block) {
447 if (isset($appsupportedblocks[$block->name])) {
448 $courseblocks[$appsupportedblocks[$block->name]] = $block->displayname;
449 }
450 }
bf258846 451 asort($courseblocks);
c1720141 452
b2551b4c 453 $features = array(
bf258846
JL
454 "$general" => array(
455 'NoDelegate_CoreOffline' => new lang_string('offlineuse', 'tool_mobile'),
456 'NoDelegate_SiteBlocks' => new lang_string('blocks'),
457 'NoDelegate_CoreComments' => new lang_string('comments'),
458 'NoDelegate_CoreRating' => new lang_string('ratings', 'rating'),
459 'NoDelegate_CoreTag' => new lang_string('tags'),
460 '$mmLoginEmailSignup' => new lang_string('startsignup'),
88932e49 461 'NoDelegate_ForgottenPassword' => new lang_string('forgotten'),
bf258846 462 'NoDelegate_ResponsiveMainMenuItems' => new lang_string('responsivemainmenuitems', 'tool_mobile'),
2ccdc21b 463 'NoDelegate_H5POffline' => new lang_string('h5poffline', 'tool_mobile'),
887f7a96 464 'NoDelegate_DarkMode' => new lang_string('darkmode', 'tool_mobile'),
bf258846 465 ),
b2551b4c 466 "$mainmenu" => array(
b2551b4c 467 '$mmSideMenuDelegate_mmaFrontpage' => new lang_string('sitehome'),
bf258846
JL
468 '$mmSideMenuDelegate_mmCourses' => new lang_string('mycourses'),
469 'CoreMainMenuDelegate_CoreCoursesDashboard' => new lang_string('myhome'),
470 '$mmSideMenuDelegate_mmaCalendar' => new lang_string('calendar', 'calendar'),
b2551b4c
JL
471 '$mmSideMenuDelegate_mmaNotifications' => new lang_string('notifications', 'message'),
472 '$mmSideMenuDelegate_mmaMessages' => new lang_string('messages', 'message'),
bf258846
JL
473 '$mmSideMenuDelegate_mmaGrades' => new lang_string('grades', 'grades'),
474 '$mmSideMenuDelegate_mmaCompetency' => new lang_string('myplans', 'tool_lp'),
475 'CoreMainMenuDelegate_AddonBlog' => new lang_string('blog', 'blog'),
b2551b4c
JL
476 '$mmSideMenuDelegate_mmaFiles' => new lang_string('files'),
477 '$mmSideMenuDelegate_website' => new lang_string('webpage'),
478 '$mmSideMenuDelegate_help' => new lang_string('help'),
479 ),
480 "$course" => array(
bf258846
JL
481 'NoDelegate_CourseBlocks' => new lang_string('blocks'),
482 'CoreCourseOptionsDelegate_AddonBlog' => new lang_string('blog', 'blog'),
b2551b4c
JL
483 '$mmCoursesDelegate_search' => new lang_string('search'),
484 '$mmCoursesDelegate_mmaCompetency' => new lang_string('competencies', 'competency'),
485 '$mmCoursesDelegate_mmaParticipants' => new lang_string('participants'),
486 '$mmCoursesDelegate_mmaGrades' => new lang_string('grades', 'grades'),
487 '$mmCoursesDelegate_mmaCourseCompletion' => new lang_string('coursecompletion', 'completion'),
488 '$mmCoursesDelegate_mmaNotes' => new lang_string('notes', 'notes'),
3c8de97d
JL
489 'NoDelegate_CoreCourseDownload' => new lang_string('downloadcourse', 'tool_mobile'),
490 'NoDelegate_CoreCoursesDownload' => new lang_string('downloadcourses', 'tool_mobile'),
b2551b4c
JL
491 ),
492 "$user" => array(
f72a4828 493 'CoreUserDelegate_AddonBlog:blogs' => new lang_string('blog', 'blog'),
b2551b4c
JL
494 '$mmUserDelegate_mmaBadges' => new lang_string('badges', 'badges'),
495 '$mmUserDelegate_mmaCompetency:learningPlan' => new lang_string('competencies', 'competency'),
496 '$mmUserDelegate_mmaCourseCompletion:viewCompletion' => new lang_string('coursecompletion', 'completion'),
497 '$mmUserDelegate_mmaGrades:viewGrades' => new lang_string('grades', 'grades'),
498 '$mmUserDelegate_mmaMessages:sendMessage' => new lang_string('sendmessage', 'message'),
499 '$mmUserDelegate_mmaMessages:addContact' => new lang_string('addcontact', 'message'),
500 '$mmUserDelegate_mmaMessages:blockContact' => new lang_string('blockcontact', 'message'),
501 '$mmUserDelegate_mmaNotes:addNote' => new lang_string('addnewnote', 'notes'),
502 '$mmUserDelegate_picture' => new lang_string('userpic'),
503 ),
504 "$files" => array(
505 'files_privatefiles' => new lang_string('privatefiles'),
506 'files_sitefiles' => new lang_string('sitefiles'),
507 'files_upload' => new lang_string('upload'),
508 ),
509 "$modules" => $coursemodules,
c1720141 510 "$blocks" => $courseblocks,
b2551b4c
JL
511 );
512
513 if (!empty($remoteaddonslist)) {
514 $features["$remoteaddons"] = $remoteaddonslist;
515 }
516
29aeab4e
DP
517 // Display OAuth 2 identity providers.
518 if (is_enabled_auth('oauth2')) {
519 $identityproviderslist = array();
520 $idps = \auth_plugin_base::get_identity_providers(['oauth2']);
521
522 foreach ($idps as $idp) {
523 // Only add identity providers that have an ID.
524 $id = isset($idp['url']) ? $idp['url']->get_param('id') : null;
525 if ($id != null) {
526 $identityproviderslist['NoDelegate_IdentityProvider_' . $id] = $idp['name'];
527 }
528 }
529
530 if (!empty($identityproviderslist)) {
531 $features["$identityproviders"] = array();
532
533 if (count($identityproviderslist) > 1) {
534 // Include an option to disable them all.
535 $features["$identityproviders"]['NoDelegate_IdentityProviders'] = new lang_string('all');
536 }
537
538 $features["$identityproviders"] = array_merge($features["$identityproviders"], $identityproviderslist);
539 }
540 }
541
b2551b4c
JL
542 return $features;
543 }
66a159f8
JL
544
545 /**
546 * This function check the current site for potential configuration issues that may prevent the mobile app to work.
547 *
548 * @return array list of potential issues
549 * @since Moodle 3.4
550 */
551 public static function get_potential_config_issues() {
552 global $CFG;
553 require_once($CFG->dirroot . "/lib/filelib.php");
554 require_once($CFG->dirroot . '/message/lib.php');
555
556 $warnings = array();
557
558 $curl = new curl();
559 // Return certificate information and verify the certificate.
560 $curl->setopt(array('CURLOPT_CERTINFO' => 1, 'CURLOPT_SSL_VERIFYPEER' => true));
561 $httpswwwroot = str_replace('http:', 'https:', $CFG->wwwroot); // Force https url.
098b16fb
JL
562 // Check https using a page not redirecting or returning exceptions.
563 $curl->head($httpswwwroot . "/$CFG->admin/tool/mobile/mobile.webmanifest.php");
66a159f8
JL
564 $info = $curl->get_info();
565
566 // First of all, check the server certificate (if any).
567 if (empty($info['http_code']) or ($info['http_code'] >= 400)) {
568 $warnings[] = ['nohttpsformobilewarning', 'admin'];
569 } else {
570 // Check the certificate is not self-signed or has an untrusted-root.
571 // This may be weak in some scenarios (when the curl SSL verifier is outdated).
572 if (empty($info['certinfo'])) {
573 $warnings[] = ['selfsignedoruntrustedcertificatewarning', 'tool_mobile'];
574 } else {
575 $timenow = time();
576 $expectedissuer = null;
577 foreach ($info['certinfo'] as $cert) {
578 // Check if the signature algorithm is weak (Android won't work with SHA-1).
579 if ($cert['Signature Algorithm'] == 'sha1WithRSAEncryption' || $cert['Signature Algorithm'] == 'sha1WithRSA') {
580 $warnings[] = ['insecurealgorithmwarning', 'tool_mobile'];
581 }
582 // Check certificate start date.
583 if (strtotime($cert['Start date']) > $timenow) {
584 $warnings[] = ['invalidcertificatestartdatewarning', 'tool_mobile'];
585 }
586 // Check certificate end date.
587 if (strtotime($cert['Expire date']) < $timenow) {
588 $warnings[] = ['invalidcertificateexpiredatewarning', 'tool_mobile'];
589 }
590 // Check the chain.
591 if ($expectedissuer !== null) {
592 if ($expectedissuer !== $cert['Subject'] || $cert['Subject'] === $cert['Issuer']) {
593 $warnings[] = ['invalidcertificatechainwarning', 'tool_mobile'];
594 }
595 }
596 $expectedissuer = $cert['Issuer'];
597 }
598 }
599 }
600 // Now check typical configuration problems.
601 if ((int) $CFG->userquota === PHP_INT_MAX) {
602 // In old Moodle version was a text so was possible to have numeric values > PHP_INT_MAX.
603 $warnings[] = ['invaliduserquotawarning', 'tool_mobile'];
604 }
605 // Check ADOdb debug enabled.
606 if (get_config('auth_db', 'debugauthdb') || get_config('enrol_database', 'debugdb')) {
607 $warnings[] = ['adodbdebugwarning', 'tool_mobile'];
608 }
609 // Check display errors on.
610 if (!empty($CFG->debugdisplay)) {
611 $warnings[] = ['displayerrorswarning', 'tool_mobile'];
612 }
613 // Check mobile notifications.
614 $processors = get_message_processors();
615 $enabled = false;
616 foreach ($processors as $processor => $status) {
617 if ($processor == 'airnotifier' && $status->enabled) {
618 $enabled = true;
619 }
620 }
621 if (!$enabled) {
622 $warnings[] = ['mobilenotificationsdisabledwarning', 'tool_mobile'];
623 }
624
625 return $warnings;
626 }
9df51510
JL
627
628 /**
629 * Generates a QR code for automatic login from the mobile app.
630 *
631 * @param stdClass $mobilesettings tool_mobile settings
632 * @return string base64 data image contents
633 */
634 public static function generate_login_qrcode(stdClass $mobilesettings) {
635 global $CFG, $USER;
636
637 $urlscheme = !empty($mobilesettings->forcedurlscheme) ? $mobilesettings->forcedurlscheme : 'moodlemobile';
638 $qrloginkey = static::get_qrlogin_key();
639 $data = $urlscheme . '://' . $CFG->wwwroot . '?qrlogin=' . $qrloginkey . '&userid=' . $USER->id;
640
641 $qrcode = new core_qrcode($data);
642 $imagedata = 'data:image/png;base64,' . base64_encode($qrcode->getBarcodePngData(5, 5));
643
644 return $imagedata;
645 }
b2478ed0 646}