PageRenderTime 59ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 3ms

/lib/adminlib.php

https://bitbucket.org/moodle/moodle
PHP | 11709 lines | 6516 code | 1364 blank | 3829 comment | 1002 complexity | 716b0648d4355c6bc628990edd7e7ab0 MD5 | raw file
Possible License(s): Apache-2.0, LGPL-2.1, BSD-3-Clause, MIT, GPL-3.0
  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. * Functions and classes used during installation, upgrades and for admin settings.
  18. *
  19. * ADMIN SETTINGS TREE INTRODUCTION
  20. *
  21. * This file performs the following tasks:
  22. * -it defines the necessary objects and interfaces to build the Moodle
  23. * admin hierarchy
  24. * -it defines the admin_externalpage_setup()
  25. *
  26. * ADMIN_SETTING OBJECTS
  27. *
  28. * Moodle settings are represented by objects that inherit from the admin_setting
  29. * class. These objects encapsulate how to read a setting, how to write a new value
  30. * to a setting, and how to appropriately display the HTML to modify the setting.
  31. *
  32. * ADMIN_SETTINGPAGE OBJECTS
  33. *
  34. * The admin_setting objects are then grouped into admin_settingpages. The latter
  35. * appear in the Moodle admin tree block. All interaction with admin_settingpage
  36. * objects is handled by the admin/settings.php file.
  37. *
  38. * ADMIN_EXTERNALPAGE OBJECTS
  39. *
  40. * There are some settings in Moodle that are too complex to (efficiently) handle
  41. * with admin_settingpages. (Consider, for example, user management and displaying
  42. * lists of users.) In this case, we use the admin_externalpage object. This object
  43. * places a link to an external PHP file in the admin tree block.
  44. *
  45. * If you're using an admin_externalpage object for some settings, you can take
  46. * advantage of the admin_externalpage_* functions. For example, suppose you wanted
  47. * to add a foo.php file into admin. First off, you add the following line to
  48. * admin/settings/first.php (at the end of the file) or to some other file in
  49. * admin/settings:
  50. * <code>
  51. * $ADMIN->add('userinterface', new admin_externalpage('foo', get_string('foo'),
  52. * $CFG->wwwdir . '/' . '$CFG->admin . '/foo.php', 'some_role_permission'));
  53. * </code>
  54. *
  55. * Next, in foo.php, your file structure would resemble the following:
  56. * <code>
  57. * require(__DIR__.'/../../config.php');
  58. * require_once($CFG->libdir.'/adminlib.php');
  59. * admin_externalpage_setup('foo');
  60. * // functionality like processing form submissions goes here
  61. * echo $OUTPUT->header();
  62. * // your HTML goes here
  63. * echo $OUTPUT->footer();
  64. * </code>
  65. *
  66. * The admin_externalpage_setup() function call ensures the user is logged in,
  67. * and makes sure that they have the proper role permission to access the page.
  68. * It also configures all $PAGE properties needed for navigation.
  69. *
  70. * ADMIN_CATEGORY OBJECTS
  71. *
  72. * Above and beyond all this, we have admin_category objects. These objects
  73. * appear as folders in the admin tree block. They contain admin_settingpage's,
  74. * admin_externalpage's, and other admin_category's.
  75. *
  76. * OTHER NOTES
  77. *
  78. * admin_settingpage's, admin_externalpage's, and admin_category's all inherit
  79. * from part_of_admin_tree (a pseudointerface). This interface insists that
  80. * a class has a check_access method for access permissions, a locate method
  81. * used to find a specific node in the admin tree and find parent path.
  82. *
  83. * admin_category's inherit from parentable_part_of_admin_tree. This pseudo-
  84. * interface ensures that the class implements a recursive add function which
  85. * accepts a part_of_admin_tree object and searches for the proper place to
  86. * put it. parentable_part_of_admin_tree implies part_of_admin_tree.
  87. *
  88. * Please note that the $this->name field of any part_of_admin_tree must be
  89. * UNIQUE throughout the ENTIRE admin tree.
  90. *
  91. * The $this->name field of an admin_setting object (which is *not* part_of_
  92. * admin_tree) must be unique on the respective admin_settingpage where it is
  93. * used.
  94. *
  95. * Original author: Vincenzo K. Marcovecchio
  96. * Maintainer: Petr Skoda
  97. *
  98. * @package core
  99. * @subpackage admin
  100. * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
  101. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  102. */
  103. use core_admin\local\settings\linkable_settings_page;
  104. defined('MOODLE_INTERNAL') || die();
  105. /// Add libraries
  106. require_once($CFG->libdir.'/ddllib.php');
  107. require_once($CFG->libdir.'/xmlize.php');
  108. require_once($CFG->libdir.'/messagelib.php');
  109. // Add classes, traits, and interfaces which should be autoloaded.
  110. // The autoloader is configured late in setup.php, after ABORT_AFTER_CONFIG.
  111. // This is also required where the setup system is not included at all.
  112. require_once($CFG->dirroot.'/admin/classes/local/settings/linkable_settings_page.php');
  113. define('INSECURE_DATAROOT_WARNING', 1);
  114. define('INSECURE_DATAROOT_ERROR', 2);
  115. /**
  116. * Automatically clean-up all plugin data and remove the plugin DB tables
  117. *
  118. * NOTE: do not call directly, use new /admin/plugins.php?uninstall=component instead!
  119. *
  120. * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
  121. * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
  122. * @uses global $OUTPUT to produce notices and other messages
  123. * @return void
  124. */
  125. function uninstall_plugin($type, $name) {
  126. global $CFG, $DB, $OUTPUT;
  127. // This may take a long time.
  128. core_php_time_limit::raise();
  129. // Recursively uninstall all subplugins first.
  130. $subplugintypes = core_component::get_plugin_types_with_subplugins();
  131. if (isset($subplugintypes[$type])) {
  132. $base = core_component::get_plugin_directory($type, $name);
  133. $subpluginsfile = "{$base}/db/subplugins.json";
  134. if (file_exists($subpluginsfile)) {
  135. $subplugins = (array) json_decode(file_get_contents($subpluginsfile))->plugintypes;
  136. } else if (file_exists("{$base}/db/subplugins.php")) {
  137. debugging('Use of subplugins.php has been deprecated. ' .
  138. 'Please update your plugin to provide a subplugins.json file instead.',
  139. DEBUG_DEVELOPER);
  140. $subplugins = [];
  141. include("{$base}/db/subplugins.php");
  142. }
  143. if (!empty($subplugins)) {
  144. foreach (array_keys($subplugins) as $subplugintype) {
  145. $instances = core_component::get_plugin_list($subplugintype);
  146. foreach ($instances as $subpluginname => $notusedpluginpath) {
  147. uninstall_plugin($subplugintype, $subpluginname);
  148. }
  149. }
  150. }
  151. }
  152. $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
  153. if ($type === 'mod') {
  154. $pluginname = $name; // eg. 'forum'
  155. if (get_string_manager()->string_exists('modulename', $component)) {
  156. $strpluginname = get_string('modulename', $component);
  157. } else {
  158. $strpluginname = $component;
  159. }
  160. } else {
  161. $pluginname = $component;
  162. if (get_string_manager()->string_exists('pluginname', $component)) {
  163. $strpluginname = get_string('pluginname', $component);
  164. } else {
  165. $strpluginname = $component;
  166. }
  167. }
  168. echo $OUTPUT->heading($pluginname);
  169. // Delete all tag areas, collections and instances associated with this plugin.
  170. core_tag_area::uninstall($component);
  171. // Custom plugin uninstall.
  172. $plugindirectory = core_component::get_plugin_directory($type, $name);
  173. $uninstalllib = $plugindirectory . '/db/uninstall.php';
  174. if (file_exists($uninstalllib)) {
  175. require_once($uninstalllib);
  176. $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
  177. if (function_exists($uninstallfunction)) {
  178. // Do not verify result, let plugin complain if necessary.
  179. $uninstallfunction();
  180. }
  181. }
  182. // Specific plugin type cleanup.
  183. $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
  184. if ($plugininfo) {
  185. $plugininfo->uninstall_cleanup();
  186. core_plugin_manager::reset_caches();
  187. }
  188. $plugininfo = null;
  189. // perform clean-up task common for all the plugin/subplugin types
  190. //delete the web service functions and pre-built services
  191. require_once($CFG->dirroot.'/lib/externallib.php');
  192. external_delete_descriptions($component);
  193. // delete calendar events
  194. $DB->delete_records('event', array('modulename' => $pluginname));
  195. $DB->delete_records('event', ['component' => $component]);
  196. // Delete scheduled tasks.
  197. $DB->delete_records('task_adhoc', ['component' => $component]);
  198. $DB->delete_records('task_scheduled', array('component' => $component));
  199. // Delete Inbound Message datakeys.
  200. $DB->delete_records_select('messageinbound_datakeys',
  201. 'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
  202. // Delete Inbound Message handlers.
  203. $DB->delete_records('messageinbound_handlers', array('component' => $component));
  204. // delete all the logs
  205. $DB->delete_records('log', array('module' => $pluginname));
  206. // delete log_display information
  207. $DB->delete_records('log_display', array('component' => $component));
  208. // delete the module configuration records
  209. unset_all_config_for_plugin($component);
  210. if ($type === 'mod') {
  211. unset_all_config_for_plugin($pluginname);
  212. }
  213. // delete message provider
  214. message_provider_uninstall($component);
  215. // delete the plugin tables
  216. $xmldbfilepath = $plugindirectory . '/db/install.xml';
  217. drop_plugin_tables($component, $xmldbfilepath, false);
  218. if ($type === 'mod' or $type === 'block') {
  219. // non-frankenstyle table prefixes
  220. drop_plugin_tables($name, $xmldbfilepath, false);
  221. }
  222. // delete the capabilities that were defined by this module
  223. capabilities_cleanup($component);
  224. // Delete all remaining files in the filepool owned by the component.
  225. $fs = get_file_storage();
  226. $fs->delete_component_files($component);
  227. // Finally purge all caches.
  228. purge_all_caches();
  229. // Invalidate the hash used for upgrade detections.
  230. set_config('allversionshash', '');
  231. echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
  232. }
  233. /**
  234. * Returns the version of installed component
  235. *
  236. * @param string $component component name
  237. * @param string $source either 'disk' or 'installed' - where to get the version information from
  238. * @return string|bool version number or false if the component is not found
  239. */
  240. function get_component_version($component, $source='installed') {
  241. global $CFG, $DB;
  242. list($type, $name) = core_component::normalize_component($component);
  243. // moodle core or a core subsystem
  244. if ($type === 'core') {
  245. if ($source === 'installed') {
  246. if (empty($CFG->version)) {
  247. return false;
  248. } else {
  249. return $CFG->version;
  250. }
  251. } else {
  252. if (!is_readable($CFG->dirroot.'/version.php')) {
  253. return false;
  254. } else {
  255. $version = null; //initialize variable for IDEs
  256. include($CFG->dirroot.'/version.php');
  257. return $version;
  258. }
  259. }
  260. }
  261. // activity module
  262. if ($type === 'mod') {
  263. if ($source === 'installed') {
  264. if ($CFG->version < 2013092001.02) {
  265. return $DB->get_field('modules', 'version', array('name'=>$name));
  266. } else {
  267. return get_config('mod_'.$name, 'version');
  268. }
  269. } else {
  270. $mods = core_component::get_plugin_list('mod');
  271. if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
  272. return false;
  273. } else {
  274. $plugin = new stdClass();
  275. $plugin->version = null;
  276. $module = $plugin;
  277. include($mods[$name].'/version.php');
  278. return $plugin->version;
  279. }
  280. }
  281. }
  282. // block
  283. if ($type === 'block') {
  284. if ($source === 'installed') {
  285. if ($CFG->version < 2013092001.02) {
  286. return $DB->get_field('block', 'version', array('name'=>$name));
  287. } else {
  288. return get_config('block_'.$name, 'version');
  289. }
  290. } else {
  291. $blocks = core_component::get_plugin_list('block');
  292. if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
  293. return false;
  294. } else {
  295. $plugin = new stdclass();
  296. include($blocks[$name].'/version.php');
  297. return $plugin->version;
  298. }
  299. }
  300. }
  301. // all other plugin types
  302. if ($source === 'installed') {
  303. return get_config($type.'_'.$name, 'version');
  304. } else {
  305. $plugins = core_component::get_plugin_list($type);
  306. if (empty($plugins[$name])) {
  307. return false;
  308. } else {
  309. $plugin = new stdclass();
  310. include($plugins[$name].'/version.php');
  311. return $plugin->version;
  312. }
  313. }
  314. }
  315. /**
  316. * Delete all plugin tables
  317. *
  318. * @param string $name Name of plugin, used as table prefix
  319. * @param string $file Path to install.xml file
  320. * @param bool $feedback defaults to true
  321. * @return bool Always returns true
  322. */
  323. function drop_plugin_tables($name, $file, $feedback=true) {
  324. global $CFG, $DB;
  325. // first try normal delete
  326. if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
  327. return true;
  328. }
  329. // then try to find all tables that start with name and are not in any xml file
  330. $used_tables = get_used_table_names();
  331. $tables = $DB->get_tables();
  332. /// Iterate over, fixing id fields as necessary
  333. foreach ($tables as $table) {
  334. if (in_array($table, $used_tables)) {
  335. continue;
  336. }
  337. if (strpos($table, $name) !== 0) {
  338. continue;
  339. }
  340. // found orphan table --> delete it
  341. if ($DB->get_manager()->table_exists($table)) {
  342. $xmldb_table = new xmldb_table($table);
  343. $DB->get_manager()->drop_table($xmldb_table);
  344. }
  345. }
  346. return true;
  347. }
  348. /**
  349. * Returns names of all known tables == tables that moodle knows about.
  350. *
  351. * @return array Array of lowercase table names
  352. */
  353. function get_used_table_names() {
  354. $table_names = array();
  355. $dbdirs = get_db_directories();
  356. foreach ($dbdirs as $dbdir) {
  357. $file = $dbdir.'/install.xml';
  358. $xmldb_file = new xmldb_file($file);
  359. if (!$xmldb_file->fileExists()) {
  360. continue;
  361. }
  362. $loaded = $xmldb_file->loadXMLStructure();
  363. $structure = $xmldb_file->getStructure();
  364. if ($loaded and $tables = $structure->getTables()) {
  365. foreach($tables as $table) {
  366. $table_names[] = strtolower($table->getName());
  367. }
  368. }
  369. }
  370. return $table_names;
  371. }
  372. /**
  373. * Returns list of all directories where we expect install.xml files
  374. * @return array Array of paths
  375. */
  376. function get_db_directories() {
  377. global $CFG;
  378. $dbdirs = array();
  379. /// First, the main one (lib/db)
  380. $dbdirs[] = $CFG->libdir.'/db';
  381. /// Then, all the ones defined by core_component::get_plugin_types()
  382. $plugintypes = core_component::get_plugin_types();
  383. foreach ($plugintypes as $plugintype => $pluginbasedir) {
  384. if ($plugins = core_component::get_plugin_list($plugintype)) {
  385. foreach ($plugins as $plugin => $plugindir) {
  386. $dbdirs[] = $plugindir.'/db';
  387. }
  388. }
  389. }
  390. return $dbdirs;
  391. }
  392. /**
  393. * Try to obtain or release the cron lock.
  394. * @param string $name name of lock
  395. * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
  396. * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
  397. * @return bool true if lock obtained
  398. */
  399. function set_cron_lock($name, $until, $ignorecurrent=false) {
  400. global $DB;
  401. if (empty($name)) {
  402. debugging("Tried to get a cron lock for a null fieldname");
  403. return false;
  404. }
  405. // remove lock by force == remove from config table
  406. if (is_null($until)) {
  407. set_config($name, null);
  408. return true;
  409. }
  410. if (!$ignorecurrent) {
  411. // read value from db - other processes might have changed it
  412. $value = $DB->get_field('config', 'value', array('name'=>$name));
  413. if ($value and $value > time()) {
  414. //lock active
  415. return false;
  416. }
  417. }
  418. set_config($name, $until);
  419. return true;
  420. }
  421. /**
  422. * Test if and critical warnings are present
  423. * @return bool
  424. */
  425. function admin_critical_warnings_present() {
  426. global $SESSION;
  427. if (!has_capability('moodle/site:config', context_system::instance())) {
  428. return 0;
  429. }
  430. if (!isset($SESSION->admin_critical_warning)) {
  431. $SESSION->admin_critical_warning = 0;
  432. if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
  433. $SESSION->admin_critical_warning = 1;
  434. }
  435. }
  436. return $SESSION->admin_critical_warning;
  437. }
  438. /**
  439. * Detects if float supports at least 10 decimal digits
  440. *
  441. * Detects if float supports at least 10 decimal digits
  442. * and also if float-->string conversion works as expected.
  443. *
  444. * @return bool true if problem found
  445. */
  446. function is_float_problem() {
  447. $num1 = 2009010200.01;
  448. $num2 = 2009010200.02;
  449. return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
  450. }
  451. /**
  452. * Try to verify that dataroot is not accessible from web.
  453. *
  454. * Try to verify that dataroot is not accessible from web.
  455. * It is not 100% correct but might help to reduce number of vulnerable sites.
  456. * Protection from httpd.conf and .htaccess is not detected properly.
  457. *
  458. * @uses INSECURE_DATAROOT_WARNING
  459. * @uses INSECURE_DATAROOT_ERROR
  460. * @param bool $fetchtest try to test public access by fetching file, default false
  461. * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
  462. */
  463. function is_dataroot_insecure($fetchtest=false) {
  464. global $CFG;
  465. $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
  466. $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
  467. $rp = strrev(trim($rp, '/'));
  468. $rp = explode('/', $rp);
  469. foreach($rp as $r) {
  470. if (strpos($siteroot, '/'.$r.'/') === 0) {
  471. $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
  472. } else {
  473. break; // probably alias root
  474. }
  475. }
  476. $siteroot = strrev($siteroot);
  477. $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
  478. if (strpos($dataroot, $siteroot) !== 0) {
  479. return false;
  480. }
  481. if (!$fetchtest) {
  482. return INSECURE_DATAROOT_WARNING;
  483. }
  484. // now try all methods to fetch a test file using http protocol
  485. $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
  486. preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
  487. $httpdocroot = $matches[1];
  488. $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
  489. make_upload_directory('diag');
  490. $testfile = $CFG->dataroot.'/diag/public.txt';
  491. if (!file_exists($testfile)) {
  492. file_put_contents($testfile, 'test file, do not delete');
  493. @chmod($testfile, $CFG->filepermissions);
  494. }
  495. $teststr = trim(file_get_contents($testfile));
  496. if (empty($teststr)) {
  497. // hmm, strange
  498. return INSECURE_DATAROOT_WARNING;
  499. }
  500. $testurl = $datarooturl.'/diag/public.txt';
  501. if (extension_loaded('curl') and
  502. !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
  503. !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
  504. ($ch = @curl_init($testurl)) !== false) {
  505. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  506. curl_setopt($ch, CURLOPT_HEADER, false);
  507. $data = curl_exec($ch);
  508. if (!curl_errno($ch)) {
  509. $data = trim($data);
  510. if ($data === $teststr) {
  511. curl_close($ch);
  512. return INSECURE_DATAROOT_ERROR;
  513. }
  514. }
  515. curl_close($ch);
  516. }
  517. if ($data = @file_get_contents($testurl)) {
  518. $data = trim($data);
  519. if ($data === $teststr) {
  520. return INSECURE_DATAROOT_ERROR;
  521. }
  522. }
  523. preg_match('|https?://([^/]+)|i', $testurl, $matches);
  524. $sitename = $matches[1];
  525. $error = 0;
  526. if ($fp = @fsockopen($sitename, 80, $error)) {
  527. preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
  528. $localurl = $matches[1];
  529. $out = "GET $localurl HTTP/1.1\r\n";
  530. $out .= "Host: $sitename\r\n";
  531. $out .= "Connection: Close\r\n\r\n";
  532. fwrite($fp, $out);
  533. $data = '';
  534. $incoming = false;
  535. while (!feof($fp)) {
  536. if ($incoming) {
  537. $data .= fgets($fp, 1024);
  538. } else if (@fgets($fp, 1024) === "\r\n") {
  539. $incoming = true;
  540. }
  541. }
  542. fclose($fp);
  543. $data = trim($data);
  544. if ($data === $teststr) {
  545. return INSECURE_DATAROOT_ERROR;
  546. }
  547. }
  548. return INSECURE_DATAROOT_WARNING;
  549. }
  550. /**
  551. * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
  552. */
  553. function enable_cli_maintenance_mode() {
  554. global $CFG, $SITE;
  555. if (file_exists("$CFG->dataroot/climaintenance.html")) {
  556. unlink("$CFG->dataroot/climaintenance.html");
  557. }
  558. if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
  559. $data = $CFG->maintenance_message;
  560. $data = bootstrap_renderer::early_error_content($data, null, null, null);
  561. $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
  562. } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
  563. $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
  564. } else {
  565. $data = get_string('sitemaintenance', 'admin');
  566. $data = bootstrap_renderer::early_error_content($data, null, null, null);
  567. $data = bootstrap_renderer::plain_page(get_string('sitemaintenancetitle', 'admin',
  568. format_string($SITE->fullname, true, ['context' => context_system::instance()])), $data);
  569. }
  570. file_put_contents("$CFG->dataroot/climaintenance.html", $data);
  571. chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
  572. }
  573. /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
  574. /**
  575. * Interface for anything appearing in the admin tree
  576. *
  577. * The interface that is implemented by anything that appears in the admin tree
  578. * block. It forces inheriting classes to define a method for checking user permissions
  579. * and methods for finding something in the admin tree.
  580. *
  581. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  582. */
  583. interface part_of_admin_tree {
  584. /**
  585. * Finds a named part_of_admin_tree.
  586. *
  587. * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
  588. * and not parentable_part_of_admin_tree, then this function should only check if
  589. * $this->name matches $name. If it does, it should return a reference to $this,
  590. * otherwise, it should return a reference to NULL.
  591. *
  592. * If a class inherits parentable_part_of_admin_tree, this method should be called
  593. * recursively on all child objects (assuming, of course, the parent object's name
  594. * doesn't match the search criterion).
  595. *
  596. * @param string $name The internal name of the part_of_admin_tree we're searching for.
  597. * @return mixed An object reference or a NULL reference.
  598. */
  599. public function locate($name);
  600. /**
  601. * Removes named part_of_admin_tree.
  602. *
  603. * @param string $name The internal name of the part_of_admin_tree we want to remove.
  604. * @return bool success.
  605. */
  606. public function prune($name);
  607. /**
  608. * Search using query
  609. * @param string $query
  610. * @return mixed array-object structure of found settings and pages
  611. */
  612. public function search($query);
  613. /**
  614. * Verifies current user's access to this part_of_admin_tree.
  615. *
  616. * Used to check if the current user has access to this part of the admin tree or
  617. * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
  618. * then this method is usually just a call to has_capability() in the site context.
  619. *
  620. * If a class inherits parentable_part_of_admin_tree, this method should return the
  621. * logical OR of the return of check_access() on all child objects.
  622. *
  623. * @return bool True if the user has access, false if she doesn't.
  624. */
  625. public function check_access();
  626. /**
  627. * Mostly useful for removing of some parts of the tree in admin tree block.
  628. *
  629. * @return True is hidden from normal list view
  630. */
  631. public function is_hidden();
  632. /**
  633. * Show we display Save button at the page bottom?
  634. * @return bool
  635. */
  636. public function show_save();
  637. }
  638. /**
  639. * Interface implemented by any part_of_admin_tree that has children.
  640. *
  641. * The interface implemented by any part_of_admin_tree that can be a parent
  642. * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
  643. * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
  644. * include an add method for adding other part_of_admin_tree objects as children.
  645. *
  646. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  647. */
  648. interface parentable_part_of_admin_tree extends part_of_admin_tree {
  649. /**
  650. * Adds a part_of_admin_tree object to the admin tree.
  651. *
  652. * Used to add a part_of_admin_tree object to this object or a child of this
  653. * object. $something should only be added if $destinationname matches
  654. * $this->name. If it doesn't, add should be called on child objects that are
  655. * also parentable_part_of_admin_tree's.
  656. *
  657. * $something should be appended as the last child in the $destinationname. If the
  658. * $beforesibling is specified, $something should be prepended to it. If the given
  659. * sibling is not found, $something should be appended to the end of $destinationname
  660. * and a developer debugging message should be displayed.
  661. *
  662. * @param string $destinationname The internal name of the new parent for $something.
  663. * @param part_of_admin_tree $something The object to be added.
  664. * @return bool True on success, false on failure.
  665. */
  666. public function add($destinationname, $something, $beforesibling = null);
  667. }
  668. /**
  669. * The object used to represent folders (a.k.a. categories) in the admin tree block.
  670. *
  671. * Each admin_category object contains a number of part_of_admin_tree objects.
  672. *
  673. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  674. */
  675. class admin_category implements parentable_part_of_admin_tree, linkable_settings_page {
  676. /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
  677. protected $children;
  678. /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
  679. public $name;
  680. /** @var string The displayed name for this category. Usually obtained through get_string() */
  681. public $visiblename;
  682. /** @var bool Should this category be hidden in admin tree block? */
  683. public $hidden;
  684. /** @var mixed Either a string or an array or strings */
  685. public $path;
  686. /** @var mixed Either a string or an array or strings */
  687. public $visiblepath;
  688. /** @var array fast lookup category cache, all categories of one tree point to one cache */
  689. protected $category_cache;
  690. /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
  691. protected $sort = false;
  692. /** @var bool If set to true children will be sorted in ascending order. */
  693. protected $sortasc = true;
  694. /** @var bool If set to true sub categories and pages will be split and then sorted.. */
  695. protected $sortsplit = true;
  696. /** @var bool $sorted True if the children have been sorted and don't need resorting */
  697. protected $sorted = false;
  698. /**
  699. * Constructor for an empty admin category
  700. *
  701. * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
  702. * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
  703. * @param bool $hidden hide category in admin tree block, defaults to false
  704. */
  705. public function __construct($name, $visiblename, $hidden=false) {
  706. $this->children = array();
  707. $this->name = $name;
  708. $this->visiblename = $visiblename;
  709. $this->hidden = $hidden;
  710. }
  711. /**
  712. * Get the URL to view this settings page.
  713. *
  714. * @return moodle_url
  715. */
  716. public function get_settings_page_url(): moodle_url {
  717. return new moodle_url(
  718. '/admin/category.php',
  719. [
  720. 'category' => $this->name,
  721. ]
  722. );
  723. }
  724. /**
  725. * Returns a reference to the part_of_admin_tree object with internal name $name.
  726. *
  727. * @param string $name The internal name of the object we want.
  728. * @param bool $findpath initialize path and visiblepath arrays
  729. * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
  730. * defaults to false
  731. */
  732. public function locate($name, $findpath=false) {
  733. if (!isset($this->category_cache[$this->name])) {
  734. // somebody much have purged the cache
  735. $this->category_cache[$this->name] = $this;
  736. }
  737. if ($this->name == $name) {
  738. if ($findpath) {
  739. $this->visiblepath[] = $this->visiblename;
  740. $this->path[] = $this->name;
  741. }
  742. return $this;
  743. }
  744. // quick category lookup
  745. if (!$findpath and isset($this->category_cache[$name])) {
  746. return $this->category_cache[$name];
  747. }
  748. $return = NULL;
  749. foreach($this->children as $childid=>$unused) {
  750. if ($return = $this->children[$childid]->locate($name, $findpath)) {
  751. break;
  752. }
  753. }
  754. if (!is_null($return) and $findpath) {
  755. $return->visiblepath[] = $this->visiblename;
  756. $return->path[] = $this->name;
  757. }
  758. return $return;
  759. }
  760. /**
  761. * Search using query
  762. *
  763. * @param string query
  764. * @return mixed array-object structure of found settings and pages
  765. */
  766. public function search($query) {
  767. $result = array();
  768. foreach ($this->get_children() as $child) {
  769. $subsearch = $child->search($query);
  770. if (!is_array($subsearch)) {
  771. debugging('Incorrect search result from '.$child->name);
  772. continue;
  773. }
  774. $result = array_merge($result, $subsearch);
  775. }
  776. return $result;
  777. }
  778. /**
  779. * Removes part_of_admin_tree object with internal name $name.
  780. *
  781. * @param string $name The internal name of the object we want to remove.
  782. * @return bool success
  783. */
  784. public function prune($name) {
  785. if ($this->name == $name) {
  786. return false; //can not remove itself
  787. }
  788. foreach($this->children as $precedence => $child) {
  789. if ($child->name == $name) {
  790. // clear cache and delete self
  791. while($this->category_cache) {
  792. // delete the cache, but keep the original array address
  793. array_pop($this->category_cache);
  794. }
  795. unset($this->children[$precedence]);
  796. return true;
  797. } else if ($this->children[$precedence]->prune($name)) {
  798. return true;
  799. }
  800. }
  801. return false;
  802. }
  803. /**
  804. * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
  805. *
  806. * By default the new part of the tree is appended as the last child of the parent. You
  807. * can specify a sibling node that the new part should be prepended to. If the given
  808. * sibling is not found, the part is appended to the end (as it would be by default) and
  809. * a developer debugging message is displayed.
  810. *
  811. * @throws coding_exception if the $beforesibling is empty string or is not string at all.
  812. * @param string $destinationame The internal name of the immediate parent that we want for $something.
  813. * @param mixed $something A part_of_admin_tree or setting instance to be added.
  814. * @param string $beforesibling The name of the parent's child the $something should be prepended to.
  815. * @return bool True if successfully added, false if $something can not be added.
  816. */
  817. public function add($parentname, $something, $beforesibling = null) {
  818. global $CFG;
  819. $parent = $this->locate($parentname);
  820. if (is_null($parent)) {
  821. debugging('parent does not exist!');
  822. return false;
  823. }
  824. if ($something instanceof part_of_admin_tree) {
  825. if (!($parent instanceof parentable_part_of_admin_tree)) {
  826. debugging('error - parts of tree can be inserted only into parentable parts');
  827. return false;
  828. }
  829. if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
  830. // The name of the node is already used, simply warn the developer that this should not happen.
  831. // It is intentional to check for the debug level before performing the check.
  832. debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
  833. }
  834. if (is_null($beforesibling)) {
  835. // Append $something as the parent's last child.
  836. $parent->children[] = $something;
  837. } else {
  838. if (!is_string($beforesibling) or trim($beforesibling) === '') {
  839. throw new coding_exception('Unexpected value of the beforesibling parameter');
  840. }
  841. // Try to find the position of the sibling.
  842. $siblingposition = null;
  843. foreach ($parent->children as $childposition => $child) {
  844. if ($child->name === $beforesibling) {
  845. $siblingposition = $childposition;
  846. break;
  847. }
  848. }
  849. if (is_null($siblingposition)) {
  850. debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
  851. $parent->children[] = $something;
  852. } else {
  853. $parent->children = array_merge(
  854. array_slice($parent->children, 0, $siblingposition),
  855. array($something),
  856. array_slice($parent->children, $siblingposition)
  857. );
  858. }
  859. }
  860. if ($something instanceof admin_category) {
  861. if (isset($this->category_cache[$something->name])) {
  862. debugging('Duplicate admin category name: '.$something->name);
  863. } else {
  864. $this->category_cache[$something->name] = $something;
  865. $something->category_cache =& $this->category_cache;
  866. foreach ($something->children as $child) {
  867. // just in case somebody already added subcategories
  868. if ($child instanceof admin_category) {
  869. if (isset($this->category_cache[$child->name])) {
  870. debugging('Duplicate admin category name: '.$child->name);
  871. } else {
  872. $this->category_cache[$child->name] = $child;
  873. $child->category_cache =& $this->category_cache;
  874. }
  875. }
  876. }
  877. }
  878. }
  879. return true;
  880. } else {
  881. debugging('error - can not add this element');
  882. return false;
  883. }
  884. }
  885. /**
  886. * Checks if the user has access to anything in this category.
  887. *
  888. * @return bool True if the user has access to at least one child in this category, false otherwise.
  889. */
  890. public function check_access() {
  891. foreach ($this->children as $child) {
  892. if ($child->check_access()) {
  893. return true;
  894. }
  895. }
  896. return false;
  897. }
  898. /**
  899. * Is this category hidden in admin tree block?
  900. *
  901. * @return bool True if hidden
  902. */
  903. public function is_hidden() {
  904. return $this->hidden;
  905. }
  906. /**
  907. * Show we display Save button at the page bottom?
  908. * @return bool
  909. */
  910. public function show_save() {
  911. foreach ($this->children as $child) {
  912. if ($child->show_save()) {
  913. return true;
  914. }
  915. }
  916. return false;
  917. }
  918. /**
  919. * Sets sorting on this category.
  920. *
  921. * Please note this function doesn't actually do the sorting.
  922. * It can be called anytime.
  923. * Sorting occurs when the user calls get_children.
  924. * Code using the children array directly won't see the sorted results.
  925. *
  926. * @param bool $sort If set to true children will be sorted, if false they won't be.
  927. * @param bool $asc If true sorting will be ascending, otherwise descending.
  928. * @param bool $split If true we sort pages and sub categories separately.
  929. */
  930. public function set_sorting($sort, $asc = true, $split = true) {
  931. $this->sort = (bool)$sort;
  932. $this->sortasc = (bool)$asc;
  933. $this->sortsplit = (bool)$split;
  934. }
  935. /**
  936. * Returns the children associated with this category.
  937. *
  938. * @return part_of_admin_tree[]
  939. */
  940. public function get_children() {
  941. // If we should sort and it hasn't already been sorted.
  942. if ($this->sort && !$this->sorted) {
  943. if ($this->sortsplit) {
  944. $categories = array();
  945. $pages = array();
  946. foreach ($this->children as $child) {
  947. if ($child instanceof admin_category) {
  948. $categories[] = $child;
  949. } else {
  950. $pages[] = $child;
  951. }
  952. }
  953. core_collator::asort_objects_by_property($categories, 'visiblename');
  954. core_collator::asort_objects_by_property($pages, 'visiblename');
  955. if (!$this->sortasc) {
  956. $categories = array_reverse($categories);
  957. $pages = array_reverse($pages);
  958. }
  959. $this->children = array_merge($pages, $categories);
  960. } else {
  961. core_collator::asort_objects_by_property($this->children, 'visiblename');
  962. if (!$this->sortasc) {
  963. $this->children = array_reverse($this->children);
  964. }
  965. }
  966. $this->sorted = true;
  967. }
  968. return $this->children;
  969. }
  970. /**
  971. * Magically gets a property from this object.
  972. *
  973. * @param $property
  974. * @return part_of_admin_tree[]
  975. * @throws coding_exception
  976. */
  977. public function __get($property) {
  978. if ($property === 'children') {
  979. return $this->get_children();
  980. }
  981. throw new coding_exception('Invalid property requested.');
  982. }
  983. /**
  984. * Magically sets a property against this object.
  985. *
  986. * @param string $property
  987. * @param mixed $value
  988. * @throws coding_exception
  989. */
  990. public function __set($property, $value) {
  991. if ($property === 'children') {
  992. $this->sorted = false;
  993. $this->children = $value;
  994. } else {
  995. throw new coding_exception('Invalid property requested.');
  996. }
  997. }
  998. /**
  999. * Checks if an inaccessible property is set.
  1000. *
  1001. * @param string $property
  1002. * @return bool
  1003. * @throws coding_exception
  1004. */
  1005. public function __isset($property) {
  1006. if ($property === 'children') {
  1007. return isset($this->children);
  1008. }
  1009. throw new coding_exception('Invalid property requested.');
  1010. }
  1011. }
  1012. /**
  1013. * Root of admin settings tree, does not have any parent.
  1014. *
  1015. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1016. */
  1017. class admin_root extends admin_category {
  1018. /** @var array List of errors */
  1019. public $errors;
  1020. /** @var string search query */
  1021. public $search;
  1022. /** @var bool full tree flag - true means all settings required, false only pages required */
  1023. public $fulltree;
  1024. /** @var bool flag indicating loaded tree */
  1025. public $loaded;
  1026. /** @var mixed site custom defaults overriding defaults in settings files*/
  1027. public $custom_defaults;
  1028. /**
  1029. * @param bool $fulltree true means all settings required,
  1030. * false only pages required
  1031. */
  1032. public function __construct($fulltree) {
  1033. global $CFG;
  1034. parent::__construct('root', get_string('administration'), false);
  1035. $this->errors = array();
  1036. $this->search = '';
  1037. $this->fulltree = $fulltree;
  1038. $this->loaded = false;
  1039. $this->category_cache = array();
  1040. // load custom defaults if found
  1041. $this->custom_defaults = null;
  1042. $defaultsfile = "$CFG->dirroot/local/defaults.php";
  1043. if (is_readable($defaultsfile)) {
  1044. $defaults = array();
  1045. include($defaultsfile);
  1046. if (is_array($defaults) and count($defaults)) {
  1047. $this->custom_defaults = $defaults;
  1048. }
  1049. }
  1050. }
  1051. /**
  1052. * Empties children array, and sets loaded to false
  1053. *
  1054. * @param bool $requirefulltree
  1055. */
  1056. public function purge_children($requirefulltree) {
  1057. $this->children = array();
  1058. $this->fulltree = ($requirefulltree || $this->fulltree);
  1059. $this->loaded = false;
  1060. //break circular dependencies - this helps PHP 5.2
  1061. while($this->category_cache) {
  1062. array_pop($this->category_cache);
  1063. }
  1064. $this->category_cache = array();
  1065. }
  1066. }
  1067. /**
  1068. * Links external PHP pages into the admin tree.
  1069. *
  1070. * See detailed usage example at the top of this document (adminlib.php)
  1071. *
  1072. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1073. */
  1074. class admin_externalpage implements part_of_admin_tree, linkable_settings_page {
  1075. /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
  1076. public $name;
  1077. /** @var string The displayed name for this external page. Usually obtained through get_string(). */
  1078. public $visiblename;
  1079. /** @var string The external URL that we should link to when someone requests this external page. */
  1080. public $url;
  1081. /** @var array The role capability/permission a user must have to access this external page. */
  1082. public $req_capability;
  1083. /** @var object The context in which capability/permission should be checked, default is site context. */
  1084. public $context;
  1085. /** @var bool hidden in admin tree block. */
  1086. public $hidden;
  1087. /** @var mixed either string or array of string */
  1088. public $path;
  1089. /** @var array list of visible names of page parents */
  1090. public $visiblepath;
  1091. /**
  1092. * Constructor for adding an external page into the admin tree.
  1093. *
  1094. * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
  1095. * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
  1096. * @param string $url The external URL that we should link to when someone requests this external page.
  1097. * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
  1098. * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
  1099. * @param stdClass $context The context the page relates to. Not sure what happens
  1100. * if you specify something other than system or front page. Defaults to system.
  1101. */
  1102. public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
  1103. $this->name = $name;
  1104. $this->visiblename = $visiblename;
  1105. $this->url = $url;
  1106. if (is_array($req_capability)) {
  1107. $this->req_capability = $req_capability;
  1108. } else {
  1109. $this->req_capability = array($req_capability);
  1110. }
  1111. $this->hidden = $hidden;
  1112. $this->context = $context;
  1113. }
  1114. /**
  1115. * Get the URL to view this settings page.
  1116. *
  1117. * @return moodle_url
  1118. */
  1119. public function get_settings_page_url(): moodle_url {
  1120. return new moodle_url($this->url);
  1121. }
  1122. /**
  1123. * Returns a reference to the part_of_admin_tree object with internal name $name.
  1124. *
  1125. * @param string $name The internal name of the object we want.
  1126. * @param bool $findpath defaults to false
  1127. * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
  1128. */
  1129. public function locate($name, $findpath=false) {
  1130. if ($this->name == $name) {
  1131. if ($findpath) {
  1132. $this->visiblepath = array($this->visiblename);
  1133. $this->path = array($this->name);
  1134. }
  1135. return $this;
  1136. } else {
  1137. $return = NULL;
  1138. return $return;
  1139. }
  1140. }
  1141. /**
  1142. * This function always returns false, required function by interface
  1143. *
  1144. * @param string $name
  1145. * @return false
  1146. */
  1147. public function prune($name) {
  1148. return false;
  1149. }
  1150. /**
  1151. * Search using query
  1152. *
  1153. * @param string $query
  1154. * @return mixed array-object structure of found settings and pages
  1155. */
  1156. public function search($query) {
  1157. $found = false;
  1158. if (strpos(strtolower($this->name), $query) !== false) {
  1159. $found = true;
  1160. } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
  1161. $found = true;
  1162. }
  1163. if ($found) {
  1164. $result = new stdClass();
  1165. $result->page = $this;
  1166. $result->settings = array();
  1167. return array($this->name => $result);
  1168. } else {
  1169. return array();
  1170. }
  1171. }
  1172. /**
  1173. * Determines if the current user has access to this external page based on $this->req_capability.
  1174. *
  1175. * @return bool True if user has access, false otherwise.
  1176. */
  1177. public function check_access() {
  1178. global $CFG;
  1179. $context = empty($this->context) ? context_system::instance() : $this->context;
  1180. foreach($this->req_capability as $cap) {
  1181. if (has_capability($cap, $context)) {
  1182. return true;
  1183. }
  1184. }
  1185. return false;
  1186. }
  1187. /**
  1188. * Is this external page hidden in admin tree block?
  1189. *
  1190. * @return bool True if hidden
  1191. */
  1192. public function is_hidden() {
  1193. return $this->hidden;
  1194. }
  1195. /**
  1196. * Show we display Save button at the page bottom?
  1197. * @return bool
  1198. */
  1199. public function show_save() {
  1200. return false;
  1201. }
  1202. }
  1203. /**
  1204. * Used to store details of the dependency between two settings elements.
  1205. *
  1206. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1207. * @copyright 2017 Davo Smith, Synergy Learning
  1208. */
  1209. class admin_settingdependency {
  1210. /** @var string the name of the setting to be shown/hidden */
  1211. public $settingname;
  1212. /** @var string the setting this is dependent on */
  1213. public $dependenton;
  1214. /** @var string the condition to show/hide the element */
  1215. public $condition;
  1216. /** @var string the value to compare against */
  1217. public $value;
  1218. /** @var string[] list of valid conditions */
  1219. private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
  1220. /**
  1221. * admin_settingdependency constructor.
  1222. * @param string $settingname
  1223. * @param string $dependenton
  1224. * @param string $condition
  1225. * @param string $value
  1226. * @throws \coding_exception
  1227. */
  1228. public function __construct($settingname, $dependenton, $condition, $value) {
  1229. $this->settingname = $this->parse_name($settingname);
  1230. $this->dependenton = $this->parse_name($dependenton);
  1231. $this->condition = $condition;
  1232. $this->value = $value;
  1233. if (!in_array($this->condition, self::$validconditions)) {
  1234. throw new coding_exception("Invalid condition '$condition'");
  1235. }
  1236. }
  1237. /**
  1238. * Convert the setting name into the form field name.
  1239. * @param string $name
  1240. * @return string
  1241. */
  1242. private function parse_name($name) {
  1243. $bits = explode('/', $name);
  1244. $name = array_pop($bits);
  1245. $plugin = '';
  1246. if ($bits) {
  1247. $plugin = array_pop($bits);
  1248. if ($plugin === 'moodle') {
  1249. $plugin = '';
  1250. }
  1251. }
  1252. return 's_'.$plugin.'_'.$name;
  1253. }
  1254. /**
  1255. * Gather together all the dependencies in a format suitable for initialising javascript
  1256. * @param admin_settingdependency[] $dependencies
  1257. * @return array
  1258. */
  1259. public static function prepare_for_javascript($dependencies) {
  1260. $result = [];
  1261. foreach ($dependencies as $d) {
  1262. if (!isset($result[$d->dependenton])) {
  1263. $result[$d->dependenton] = [];
  1264. }
  1265. if (!isset($result[$d->dependenton][$d->condition])) {
  1266. $result[$d->dependenton][$d->condition] = [];
  1267. }
  1268. if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
  1269. $result[$d->dependenton][$d->condition][$d->value] = [];
  1270. }
  1271. $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
  1272. }
  1273. return $result;
  1274. }
  1275. }
  1276. /**
  1277. * Used to group a number of admin_setting objects into a page and add them to the admin tree.
  1278. *
  1279. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1280. */
  1281. class admin_settingpage implements part_of_admin_tree, linkable_settings_page {
  1282. /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
  1283. public $name;
  1284. /** @var string The displayed name for this external page. Usually obtained through get_string(). */
  1285. public $visiblename;
  1286. /** @var mixed An array of admin_setting objects that are part of this setting page. */
  1287. public $settings;
  1288. /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
  1289. protected $dependencies = [];
  1290. /** @var array The role capability/permission a user must have to access this external page. */
  1291. public $req_capability;
  1292. /** @var object The context in which capability/permission should be checked, default is site context. */
  1293. public $context;
  1294. /** @var bool hidden in admin tree block. */
  1295. public $hidden;
  1296. /** @var mixed string of paths or array of strings of paths */
  1297. public $path;
  1298. /** @var array list of visible names of page parents */
  1299. public $visiblepath;
  1300. /**
  1301. * see admin_settingpage for details of this function
  1302. *
  1303. * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
  1304. * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
  1305. * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
  1306. * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
  1307. * @param stdClass $context The context the page relates to. Not sure what happens
  1308. * if you specify something other than system or front page. Defaults to system.
  1309. */
  1310. public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
  1311. $this->settings = new stdClass();
  1312. $this->name = $name;
  1313. $this->visiblename = $visiblename;
  1314. if (is_array($req_capability)) {
  1315. $this->req_capability = $req_capability;
  1316. } else {
  1317. $this->req_capability = array($req_capability);
  1318. }
  1319. $this->hidden = $hidden;
  1320. $this->context = $context;
  1321. }
  1322. /**
  1323. * Get the URL to view this page.
  1324. *
  1325. * @return moodle_url
  1326. */
  1327. public function get_settings_page_url(): moodle_url {
  1328. return new moodle_url(
  1329. '/admin/settings.php',
  1330. [
  1331. 'section' => $this->name,
  1332. ]
  1333. );
  1334. }
  1335. /**
  1336. * see admin_category
  1337. *
  1338. * @param string $name
  1339. * @param bool $findpath
  1340. * @return mixed Object (this) if name == this->name, else returns null
  1341. */
  1342. public function locate($name, $findpath=false) {
  1343. if ($this->name == $name) {
  1344. if ($findpath) {
  1345. $this->visiblepath = array($this->visiblename);
  1346. $this->path = array($this->name);
  1347. }
  1348. return $this;
  1349. } else {
  1350. $return = NULL;
  1351. return $return;
  1352. }
  1353. }
  1354. /**
  1355. * Search string in settings page.
  1356. *
  1357. * @param string $query
  1358. * @return array
  1359. */
  1360. public function search($query) {
  1361. $found = array();
  1362. foreach ($this->settings as $setting) {
  1363. if ($setting->is_related($query)) {
  1364. $found[] = $setting;
  1365. }
  1366. }
  1367. if ($found) {
  1368. $result = new stdClass();
  1369. $result->page = $this;
  1370. $result->settings = $found;
  1371. return array($this->name => $result);
  1372. }
  1373. $found = false;
  1374. if (strpos(strtolower($this->name), $query) !== false) {
  1375. $found = true;
  1376. } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
  1377. $found = true;
  1378. }
  1379. if ($found) {
  1380. $result = new stdClass();
  1381. $result->page = $this;
  1382. $result->settings = array();
  1383. return array($this->name => $result);
  1384. } else {
  1385. return array();
  1386. }
  1387. }
  1388. /**
  1389. * This function always returns false, required by interface
  1390. *
  1391. * @param string $name
  1392. * @return bool Always false
  1393. */
  1394. public function prune($name) {
  1395. return false;
  1396. }
  1397. /**
  1398. * adds an admin_setting to this admin_settingpage
  1399. *
  1400. * not the same as add for admin_category. adds an admin_setting to this admin_settingpage. settings appear (on the settingpage) in the order in which they're added
  1401. * n.b. each admin_setting in an admin_settingpage must have a unique internal name
  1402. *
  1403. * @param object $setting is the admin_setting object you want to add
  1404. * @return bool true if successful, false if not
  1405. */
  1406. public function add($setting) {
  1407. if (!($setting instanceof admin_setting)) {
  1408. debugging('error - not a setting instance');
  1409. return false;
  1410. }
  1411. $name = $setting->name;
  1412. if ($setting->plugin) {
  1413. $name = $setting->plugin . $name;
  1414. }
  1415. $this->settings->{$name} = $setting;
  1416. return true;
  1417. }
  1418. /**
  1419. * Hide the named setting if the specified condition is matched.
  1420. *
  1421. * @param string $settingname
  1422. * @param string $dependenton
  1423. * @param string $condition
  1424. * @param string $value
  1425. */
  1426. public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
  1427. $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
  1428. // Reformat the dependency name to the plugin | name format used in the display.
  1429. $dependenton = str_replace('/', ' | ', $dependenton);
  1430. // Let the setting know, so it can be displayed underneath.
  1431. $findname = str_replace('/', '', $settingname);
  1432. foreach ($this->settings as $name => $setting) {
  1433. if ($name === $findname) {
  1434. $setting->add_dependent_on($dependenton);
  1435. }
  1436. }
  1437. }
  1438. /**
  1439. * see admin_externalpage
  1440. *
  1441. * @return bool Returns true for yes false for no
  1442. */
  1443. public function check_access() {
  1444. global $CFG;
  1445. $context = empty($this->context) ? context_system::instance() : $this->context;
  1446. foreach($this->req_capability as $cap) {
  1447. if (has_capability($cap, $context)) {
  1448. return true;
  1449. }
  1450. }
  1451. return false;
  1452. }
  1453. /**
  1454. * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
  1455. * @return string Returns an XHTML string
  1456. */
  1457. public function output_html() {
  1458. $adminroot = admin_get_root();
  1459. $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
  1460. foreach($this->settings as $setting) {
  1461. $fullname = $setting->get_full_name();
  1462. if (array_key_exists($fullname, $adminroot->errors)) {
  1463. $data = $adminroot->errors[$fullname]->data;
  1464. } else {
  1465. $data = $setting->get_setting();
  1466. // do not use defaults if settings not available - upgrade settings handles the defaults!
  1467. }
  1468. $return .= $setting->output_html($data);
  1469. }
  1470. $return .= '</fieldset>';
  1471. return $return;
  1472. }
  1473. /**
  1474. * Is this settings page hidden in admin tree block?
  1475. *
  1476. * @return bool True if hidden
  1477. */
  1478. public function is_hidden() {
  1479. return $this->hidden;
  1480. }
  1481. /**
  1482. * Show we display Save button at the page bottom?
  1483. * @return bool
  1484. */
  1485. public function show_save() {
  1486. foreach($this->settings as $setting) {
  1487. if (empty($setting->nosave)) {
  1488. return true;
  1489. }
  1490. }
  1491. return false;
  1492. }
  1493. /**
  1494. * Should any of the settings on this page be shown / hidden based on conditions?
  1495. * @return bool
  1496. */
  1497. public function has_dependencies() {
  1498. return (bool)$this->dependencies;
  1499. }
  1500. /**
  1501. * Format the setting show/hide conditions ready to initialise the page javascript
  1502. * @return array
  1503. */
  1504. public function get_dependencies_for_javascript() {
  1505. if (!$this->has_dependencies()) {
  1506. return [];
  1507. }
  1508. return admin_settingdependency::prepare_for_javascript($this->dependencies);
  1509. }
  1510. }
  1511. /**
  1512. * Admin settings class. Only exists on setting pages.
  1513. * Read & write happens at this level; no authentication.
  1514. *
  1515. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1516. */
  1517. abstract class admin_setting {
  1518. /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
  1519. public $name;
  1520. /** @var string localised name */
  1521. public $visiblename;
  1522. /** @var string localised long description in Markdown format */
  1523. public $description;
  1524. /** @var mixed Can be string or array of string */
  1525. public $defaultsetting;
  1526. /** @var string */
  1527. public $updatedcallback;
  1528. /** @var mixed can be String or Null. Null means main config table */
  1529. public $plugin; // null means main config table
  1530. /** @var bool true indicates this setting does not actually save anything, just information */
  1531. public $nosave = false;
  1532. /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
  1533. public $affectsmodinfo = false;
  1534. /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
  1535. private $flags = array();
  1536. /** @var bool Whether this field must be forced LTR. */
  1537. private $forceltr = null;
  1538. /** @var array list of other settings that may cause this setting to be hidden */
  1539. private $dependenton = [];
  1540. /** @var bool Whether this setting uses a custom form control */
  1541. protected $customcontrol = false;
  1542. /**
  1543. * Constructor
  1544. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  1545. * or 'myplugin/mysetting' for ones in config_plugins.
  1546. * @param string $visiblename localised name
  1547. * @param string $description localised long description
  1548. * @param mixed $defaultsetting string or array depending on implementation
  1549. */
  1550. public function __construct($name, $visiblename, $description, $defaultsetting) {
  1551. $this->parse_setting_name($name);
  1552. $this->visiblename = $visiblename;
  1553. $this->description = $description;
  1554. $this->defaultsetting = $defaultsetting;
  1555. }
  1556. /**
  1557. * Generic function to add a flag to this admin setting.
  1558. *
  1559. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
  1560. * @param bool $default - The default for the flag
  1561. * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
  1562. * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
  1563. */
  1564. protected function set_flag_options($enabled, $default, $shortname, $displayname) {
  1565. if (empty($this->flags[$shortname])) {
  1566. $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
  1567. } else {
  1568. $this->flags[$shortname]->set_options($enabled, $default);
  1569. }
  1570. }
  1571. /**
  1572. * Set the enabled options flag on this admin setting.
  1573. *
  1574. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
  1575. * @param bool $default - The default for the flag
  1576. */
  1577. public function set_enabled_flag_options($enabled, $default) {
  1578. $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
  1579. }
  1580. /**
  1581. * Set the advanced options flag on this admin setting.
  1582. *
  1583. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
  1584. * @param bool $default - The default for the flag
  1585. */
  1586. public function set_advanced_flag_options($enabled, $default) {
  1587. $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
  1588. }
  1589. /**
  1590. * Set the locked options flag on this admin setting.
  1591. *
  1592. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
  1593. * @param bool $default - The default for the flag
  1594. */
  1595. public function set_locked_flag_options($enabled, $default) {
  1596. $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
  1597. }
  1598. /**
  1599. * Set the required options flag on this admin setting.
  1600. *
  1601. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
  1602. * @param bool $default - The default for the flag.
  1603. */
  1604. public function set_required_flag_options($enabled, $default) {
  1605. $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
  1606. }
  1607. /**
  1608. * Is this option forced in config.php?
  1609. *
  1610. * @return bool
  1611. */
  1612. public function is_readonly(): bool {
  1613. global $CFG;
  1614. if (empty($this->plugin)) {
  1615. if (array_key_exists($this->name, $CFG->config_php_settings)) {
  1616. return true;
  1617. }
  1618. } else {
  1619. if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
  1620. and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
  1621. return true;
  1622. }
  1623. }
  1624. return false;
  1625. }
  1626. /**
  1627. * Get the currently saved value for a setting flag
  1628. *
  1629. * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
  1630. * @return bool
  1631. */
  1632. public function get_setting_flag_value(admin_setting_flag $flag) {
  1633. $value = $this->config_read($this->name . '_' . $flag->get_shortname());
  1634. if (!isset($value)) {
  1635. $value = $flag->get_default();
  1636. }
  1637. return !empty($value);
  1638. }
  1639. /**
  1640. * Get the list of defaults for the flags on this setting.
  1641. *
  1642. * @param array of strings describing the defaults for this setting. This is appended to by this function.
  1643. */
  1644. public function get_setting_flag_defaults(& $defaults) {
  1645. foreach ($this->flags as $flag) {
  1646. if ($flag->is_enabled() && $flag->get_default()) {
  1647. $defaults[] = $flag->get_displayname();
  1648. }
  1649. }
  1650. }
  1651. /**
  1652. * Output the input fields for the advanced and locked flags on this setting.
  1653. *
  1654. * @param bool $adv - The current value of the advanced flag.
  1655. * @param bool $locked - The current value of the locked flag.
  1656. * @return string $output - The html for the flags.
  1657. */
  1658. public function output_setting_flags() {
  1659. $output = '';
  1660. foreach ($this->flags as $flag) {
  1661. if ($flag->is_enabled()) {
  1662. $output .= $flag->output_setting_flag($this);
  1663. }
  1664. }
  1665. if (!empty($output)) {
  1666. return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
  1667. }
  1668. return $output;
  1669. }
  1670. /**
  1671. * Write the values of the flags for this admin setting.
  1672. *
  1673. * @param array $data - The data submitted from the form or null to set the default value for new installs.
  1674. * @return bool - true if successful.
  1675. */
  1676. public function write_setting_flags($data) {
  1677. $result = true;
  1678. foreach ($this->flags as $flag) {
  1679. $result = $result && $flag->write_setting_flag($this, $data);
  1680. }
  1681. return $result;
  1682. }
  1683. /**
  1684. * Set up $this->name and potentially $this->plugin
  1685. *
  1686. * Set up $this->name and possibly $this->plugin based on whether $name looks
  1687. * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
  1688. * on the names, that is, output a developer debug warning if the name
  1689. * contains anything other than [a-zA-Z0-9_]+.
  1690. *
  1691. * @param string $name the setting name passed in to the constructor.
  1692. */
  1693. private function parse_setting_name($name) {
  1694. $bits = explode('/', $name);
  1695. if (count($bits) > 2) {
  1696. throw new moodle_exception('invalidadminsettingname', '', '', $name);
  1697. }
  1698. $this->name = array_pop($bits);
  1699. if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
  1700. throw new moodle_exception('invalidadminsettingname', '', '', $name);
  1701. }
  1702. if (!empty($bits)) {
  1703. $this->plugin = array_pop($bits);
  1704. if ($this->plugin === 'moodle') {
  1705. $this->plugin = null;
  1706. } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
  1707. throw new moodle_exception('invalidadminsettingname', '', '', $name);
  1708. }
  1709. }
  1710. }
  1711. /**
  1712. * Returns the fullname prefixed by the plugin
  1713. * @return string
  1714. */
  1715. public function get_full_name() {
  1716. return 's_'.$this->plugin.'_'.$this->name;
  1717. }
  1718. /**
  1719. * Returns the ID string based on plugin and name
  1720. * @return string
  1721. */
  1722. public function get_id() {
  1723. return 'id_s_'.$this->plugin.'_'.$this->name;
  1724. }
  1725. /**
  1726. * @param bool $affectsmodinfo If true, changes to this setting will
  1727. * cause the course cache to be rebuilt
  1728. */
  1729. public function set_affects_modinfo($affectsmodinfo) {
  1730. $this->affectsmodinfo = $affectsmodinfo;
  1731. }
  1732. /**
  1733. * Returns the config if possible
  1734. *
  1735. * @return mixed returns config if successful else null
  1736. */
  1737. public function config_read($name) {
  1738. global $CFG;
  1739. if (!empty($this->plugin)) {
  1740. $value = get_config($this->plugin, $name);
  1741. return $value === false ? NULL : $value;
  1742. } else {
  1743. if (isset($CFG->$name)) {
  1744. return $CFG->$name;
  1745. } else {
  1746. return NULL;
  1747. }
  1748. }
  1749. }
  1750. /**
  1751. * Used to set a config pair and log change
  1752. *
  1753. * @param string $name
  1754. * @param mixed $value Gets converted to string if not null
  1755. * @return bool Write setting to config table
  1756. */
  1757. public function config_write($name, $value) {
  1758. global $DB, $USER, $CFG;
  1759. if ($this->nosave) {
  1760. return true;
  1761. }
  1762. // make sure it is a real change
  1763. $oldvalue = get_config($this->plugin, $name);
  1764. $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
  1765. $value = is_null($value) ? null : (string)$value;
  1766. if ($oldvalue === $value) {
  1767. return true;
  1768. }
  1769. // store change
  1770. set_config($name, $value, $this->plugin);
  1771. // Some admin settings affect course modinfo
  1772. if ($this->affectsmodinfo) {
  1773. // Clear course cache for all courses
  1774. rebuild_course_cache(0, true);
  1775. }
  1776. $this->add_to_config_log($name, $oldvalue, $value);
  1777. return true; // BC only
  1778. }
  1779. /**
  1780. * Log config changes if necessary.
  1781. * @param string $name
  1782. * @param string $oldvalue
  1783. * @param string $value
  1784. */
  1785. protected function add_to_config_log($name, $oldvalue, $value) {
  1786. add_to_config_log($name, $oldvalue, $value, $this->plugin);
  1787. }
  1788. /**
  1789. * Returns current value of this setting
  1790. * @return mixed array or string depending on instance, NULL means not set yet
  1791. */
  1792. public abstract function get_setting();
  1793. /**
  1794. * Returns default setting if exists
  1795. * @return mixed array or string depending on instance; NULL means no default, user must supply
  1796. */
  1797. public function get_defaultsetting() {
  1798. $adminroot = admin_get_root(false, false);
  1799. if (!empty($adminroot->custom_defaults)) {
  1800. $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
  1801. if (isset($adminroot->custom_defaults[$plugin])) {
  1802. if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
  1803. return $adminroot->custom_defaults[$plugin][$this->name];
  1804. }
  1805. }
  1806. }
  1807. return $this->defaultsetting;
  1808. }
  1809. /**
  1810. * Store new setting
  1811. *
  1812. * @param mixed $data string or array, must not be NULL
  1813. * @return string empty string if ok, string error message otherwise
  1814. */
  1815. public abstract function write_setting($data);
  1816. /**
  1817. * Return part of form with setting
  1818. * This function should always be overwritten
  1819. *
  1820. * @param mixed $data array or string depending on setting
  1821. * @param string $query
  1822. * @return string
  1823. */
  1824. public function output_html($data, $query='') {
  1825. // should be overridden
  1826. return;
  1827. }
  1828. /**
  1829. * Function called if setting updated - cleanup, cache reset, etc.
  1830. * @param string $functionname Sets the function name
  1831. * @return void
  1832. */
  1833. public function set_updatedcallback($functionname) {
  1834. $this->updatedcallback = $functionname;
  1835. }
  1836. /**
  1837. * Execute postupdatecallback if necessary.
  1838. * @param mixed $original original value before write_setting()
  1839. * @return bool true if changed, false if not.
  1840. */
  1841. public function post_write_settings($original) {
  1842. // Comparison must work for arrays too.
  1843. if (serialize($original) === serialize($this->get_setting())) {
  1844. return false;
  1845. }
  1846. $callbackfunction = $this->updatedcallback;
  1847. if (!empty($callbackfunction) and is_callable($callbackfunction)) {
  1848. $callbackfunction($this->get_full_name());
  1849. }
  1850. return true;
  1851. }
  1852. /**
  1853. * Is setting related to query text - used when searching
  1854. * @param string $query
  1855. * @return bool
  1856. */
  1857. public function is_related($query) {
  1858. if (strpos(strtolower($this->name), $query) !== false) {
  1859. return true;
  1860. }
  1861. if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
  1862. return true;
  1863. }
  1864. if (strpos(core_text::strtolower($this->description), $query) !== false) {
  1865. return true;
  1866. }
  1867. $current = $this->get_setting();
  1868. if (!is_null($current)) {
  1869. if (is_string($current)) {
  1870. if (strpos(core_text::strtolower($current), $query) !== false) {
  1871. return true;
  1872. }
  1873. }
  1874. }
  1875. $default = $this->get_defaultsetting();
  1876. if (!is_null($default)) {
  1877. if (is_string($default)) {
  1878. if (strpos(core_text::strtolower($default), $query) !== false) {
  1879. return true;
  1880. }
  1881. }
  1882. }
  1883. return false;
  1884. }
  1885. /**
  1886. * Get whether this should be displayed in LTR mode.
  1887. *
  1888. * @return bool|null
  1889. */
  1890. public function get_force_ltr() {
  1891. return $this->forceltr;
  1892. }
  1893. /**
  1894. * Set whether to force LTR or not.
  1895. *
  1896. * @param bool $value True when forced, false when not force, null when unknown.
  1897. */
  1898. public function set_force_ltr($value) {
  1899. $this->forceltr = $value;
  1900. }
  1901. /**
  1902. * Add a setting to the list of those that could cause this one to be hidden
  1903. * @param string $dependenton
  1904. */
  1905. public function add_dependent_on($dependenton) {
  1906. $this->dependenton[] = $dependenton;
  1907. }
  1908. /**
  1909. * Get a list of the settings that could cause this one to be hidden.
  1910. * @return array
  1911. */
  1912. public function get_dependent_on() {
  1913. return $this->dependenton;
  1914. }
  1915. /**
  1916. * Whether this setting uses a custom form control.
  1917. * This function is especially useful to decide if we should render a label element for this setting or not.
  1918. *
  1919. * @return bool
  1920. */
  1921. public function has_custom_form_control(): bool {
  1922. return $this->customcontrol;
  1923. }
  1924. }
  1925. /**
  1926. * An additional option that can be applied to an admin setting.
  1927. * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
  1928. *
  1929. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1930. */
  1931. class admin_setting_flag {
  1932. /** @var bool Flag to indicate if this option can be toggled for this setting */
  1933. private $enabled = false;
  1934. /** @var bool Flag to indicate if this option defaults to true or false */
  1935. private $default = false;
  1936. /** @var string Short string used to create setting name - e.g. 'adv' */
  1937. private $shortname = '';
  1938. /** @var string String used as the label for this flag */
  1939. private $displayname = '';
  1940. /** @const Checkbox for this flag is displayed in admin page */
  1941. const ENABLED = true;
  1942. /** @const Checkbox for this flag is not displayed in admin page */
  1943. const DISABLED = false;
  1944. /**
  1945. * Constructor
  1946. *
  1947. * @param bool $enabled Can this option can be toggled.
  1948. * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
  1949. * @param bool $default The default checked state for this setting option.
  1950. * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
  1951. * @param string $displayname The displayname of this flag. Used as a label for the flag.
  1952. */
  1953. public function __construct($enabled, $default, $shortname, $displayname) {
  1954. $this->shortname = $shortname;
  1955. $this->displayname = $displayname;
  1956. $this->set_options($enabled, $default);
  1957. }
  1958. /**
  1959. * Update the values of this setting options class
  1960. *
  1961. * @param bool $enabled Can this option can be toggled.
  1962. * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
  1963. * @param bool $default The default checked state for this setting option.
  1964. */
  1965. public function set_options($enabled, $default) {
  1966. $this->enabled = $enabled;
  1967. $this->default = $default;
  1968. }
  1969. /**
  1970. * Should this option appear in the interface and be toggleable?
  1971. *
  1972. * @return bool Is it enabled?
  1973. */
  1974. public function is_enabled() {
  1975. return $this->enabled;
  1976. }
  1977. /**
  1978. * Should this option be checked by default?
  1979. *
  1980. * @return bool Is it on by default?
  1981. */
  1982. public function get_default() {
  1983. return $this->default;
  1984. }
  1985. /**
  1986. * Return the short name for this flag. e.g. 'adv' or 'locked'
  1987. *
  1988. * @return string
  1989. */
  1990. public function get_shortname() {
  1991. return $this->shortname;
  1992. }
  1993. /**
  1994. * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
  1995. *
  1996. * @return string
  1997. */
  1998. public function get_displayname() {
  1999. return $this->displayname;
  2000. }
  2001. /**
  2002. * Save the submitted data for this flag - or set it to the default if $data is null.
  2003. *
  2004. * @param admin_setting $setting - The admin setting for this flag
  2005. * @param array $data - The data submitted from the form or null to set the default value for new installs.
  2006. * @return bool
  2007. */
  2008. public function write_setting_flag(admin_setting $setting, $data) {
  2009. $result = true;
  2010. if ($this->is_enabled()) {
  2011. if (!isset($data)) {
  2012. $value = $this->get_default();
  2013. } else {
  2014. $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
  2015. }
  2016. $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
  2017. }
  2018. return $result;
  2019. }
  2020. /**
  2021. * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
  2022. *
  2023. * @param admin_setting $setting - The admin setting for this flag
  2024. * @return string - The html for the checkbox.
  2025. */
  2026. public function output_setting_flag(admin_setting $setting) {
  2027. global $OUTPUT;
  2028. $value = $setting->get_setting_flag_value($this);
  2029. $context = new stdClass();
  2030. $context->id = $setting->get_id() . '_' . $this->get_shortname();
  2031. $context->name = $setting->get_full_name() . '_' . $this->get_shortname();
  2032. $context->value = 1;
  2033. $context->checked = $value ? true : false;
  2034. $context->label = $this->get_displayname();
  2035. return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
  2036. }
  2037. }
  2038. /**
  2039. * No setting - just heading and text.
  2040. *
  2041. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2042. */
  2043. class admin_setting_heading extends admin_setting {
  2044. /**
  2045. * not a setting, just text
  2046. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2047. * @param string $heading heading
  2048. * @param string $information text in box
  2049. */
  2050. public function __construct($name, $heading, $information) {
  2051. $this->nosave = true;
  2052. parent::__construct($name, $heading, $information, '');
  2053. }
  2054. /**
  2055. * Always returns true
  2056. * @return bool Always returns true
  2057. */
  2058. public function get_setting() {
  2059. return true;
  2060. }
  2061. /**
  2062. * Always returns true
  2063. * @return bool Always returns true
  2064. */
  2065. public function get_defaultsetting() {
  2066. return true;
  2067. }
  2068. /**
  2069. * Never write settings
  2070. * @return string Always returns an empty string
  2071. */
  2072. public function write_setting($data) {
  2073. // do not write any setting
  2074. return '';
  2075. }
  2076. /**
  2077. * Returns an HTML string
  2078. * @return string Returns an HTML string
  2079. */
  2080. public function output_html($data, $query='') {
  2081. global $OUTPUT;
  2082. $context = new stdClass();
  2083. $context->title = $this->visiblename;
  2084. $context->description = $this->description;
  2085. $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
  2086. return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
  2087. }
  2088. }
  2089. /**
  2090. * No setting - just name and description in same row.
  2091. *
  2092. * @copyright 2018 onwards Amaia Anabitarte
  2093. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2094. */
  2095. class admin_setting_description extends admin_setting {
  2096. /**
  2097. * Not a setting, just text
  2098. *
  2099. * @param string $name
  2100. * @param string $visiblename
  2101. * @param string $description
  2102. */
  2103. public function __construct($name, $visiblename, $description) {
  2104. $this->nosave = true;
  2105. parent::__construct($name, $visiblename, $description, '');
  2106. }
  2107. /**
  2108. * Always returns true
  2109. *
  2110. * @return bool Always returns true
  2111. */
  2112. public function get_setting() {
  2113. return true;
  2114. }
  2115. /**
  2116. * Always returns true
  2117. *
  2118. * @return bool Always returns true
  2119. */
  2120. public function get_defaultsetting() {
  2121. return true;
  2122. }
  2123. /**
  2124. * Never write settings
  2125. *
  2126. * @param mixed $data Gets converted to str for comparison against yes value
  2127. * @return string Always returns an empty string
  2128. */
  2129. public function write_setting($data) {
  2130. // Do not write any setting.
  2131. return '';
  2132. }
  2133. /**
  2134. * Returns an HTML string
  2135. *
  2136. * @param string $data
  2137. * @param string $query
  2138. * @return string Returns an HTML string
  2139. */
  2140. public function output_html($data, $query='') {
  2141. global $OUTPUT;
  2142. $context = new stdClass();
  2143. $context->title = $this->visiblename;
  2144. $context->description = $this->description;
  2145. return $OUTPUT->render_from_template('core_admin/setting_description', $context);
  2146. }
  2147. }
  2148. /**
  2149. * The most flexible setting, the user enters text.
  2150. *
  2151. * This type of field should be used for config settings which are using
  2152. * English words and are not localised (passwords, database name, list of values, ...).
  2153. *
  2154. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2155. */
  2156. class admin_setting_configtext extends admin_setting {
  2157. /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
  2158. public $paramtype;
  2159. /** @var int default field size */
  2160. public $size;
  2161. /**
  2162. * Config text constructor
  2163. *
  2164. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2165. * @param string $visiblename localised
  2166. * @param string $description long localised info
  2167. * @param string $defaultsetting
  2168. * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
  2169. * @param int $size default field size
  2170. */
  2171. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
  2172. $this->paramtype = $paramtype;
  2173. if (!is_null($size)) {
  2174. $this->size = $size;
  2175. } else {
  2176. $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
  2177. }
  2178. parent::__construct($name, $visiblename, $description, $defaultsetting);
  2179. }
  2180. /**
  2181. * Get whether this should be displayed in LTR mode.
  2182. *
  2183. * Try to guess from the PARAM type unless specifically set.
  2184. */
  2185. public function get_force_ltr() {
  2186. $forceltr = parent::get_force_ltr();
  2187. if ($forceltr === null) {
  2188. return !is_rtl_compatible($this->paramtype);
  2189. }
  2190. return $forceltr;
  2191. }
  2192. /**
  2193. * Return the setting
  2194. *
  2195. * @return mixed returns config if successful else null
  2196. */
  2197. public function get_setting() {
  2198. return $this->config_read($this->name);
  2199. }
  2200. public function write_setting($data) {
  2201. if ($this->paramtype === PARAM_INT and $data === '') {
  2202. // do not complain if '' used instead of 0
  2203. $data = 0;
  2204. }
  2205. // $data is a string
  2206. $validated = $this->validate($data);
  2207. if ($validated !== true) {
  2208. return $validated;
  2209. }
  2210. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  2211. }
  2212. /**
  2213. * Validate data before storage
  2214. * @param string data
  2215. * @return mixed true if ok string if error found
  2216. */
  2217. public function validate($data) {
  2218. // allow paramtype to be a custom regex if it is the form of /pattern/
  2219. if (preg_match('#^/.*/$#', $this->paramtype)) {
  2220. if (preg_match($this->paramtype, $data)) {
  2221. return true;
  2222. } else {
  2223. return get_string('validateerror', 'admin');
  2224. }
  2225. } else if ($this->paramtype === PARAM_RAW) {
  2226. return true;
  2227. } else {
  2228. $cleaned = clean_param($data, $this->paramtype);
  2229. if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
  2230. return true;
  2231. } else {
  2232. return get_string('validateerror', 'admin');
  2233. }
  2234. }
  2235. }
  2236. /**
  2237. * Return an XHTML string for the setting
  2238. * @return string Returns an XHTML string
  2239. */
  2240. public function output_html($data, $query='') {
  2241. global $OUTPUT;
  2242. $default = $this->get_defaultsetting();
  2243. $context = (object) [
  2244. 'size' => $this->size,
  2245. 'id' => $this->get_id(),
  2246. 'name' => $this->get_full_name(),
  2247. 'value' => $data,
  2248. 'forceltr' => $this->get_force_ltr(),
  2249. 'readonly' => $this->is_readonly(),
  2250. ];
  2251. $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
  2252. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  2253. }
  2254. }
  2255. /**
  2256. * Text input with a maximum length constraint.
  2257. *
  2258. * @copyright 2015 onwards Ankit Agarwal
  2259. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2260. */
  2261. class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
  2262. /** @var int maximum number of chars allowed. */
  2263. protected $maxlength;
  2264. /**
  2265. * Config text constructor
  2266. *
  2267. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  2268. * or 'myplugin/mysetting' for ones in config_plugins.
  2269. * @param string $visiblename localised
  2270. * @param string $description long localised info
  2271. * @param string $defaultsetting
  2272. * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
  2273. * @param int $size default field size
  2274. * @param mixed $maxlength int maxlength allowed, 0 for infinite.
  2275. */
  2276. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
  2277. $size=null, $maxlength = 0) {
  2278. $this->maxlength = $maxlength;
  2279. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
  2280. }
  2281. /**
  2282. * Validate data before storage
  2283. *
  2284. * @param string $data data
  2285. * @return mixed true if ok string if error found
  2286. */
  2287. public function validate($data) {
  2288. $parentvalidation = parent::validate($data);
  2289. if ($parentvalidation === true) {
  2290. if ($this->maxlength > 0) {
  2291. // Max length check.
  2292. $length = core_text::strlen($data);
  2293. if ($length > $this->maxlength) {
  2294. return get_string('maximumchars', 'moodle', $this->maxlength);
  2295. }
  2296. return true;
  2297. } else {
  2298. return true; // No max length check needed.
  2299. }
  2300. } else {
  2301. return $parentvalidation;
  2302. }
  2303. }
  2304. }
  2305. /**
  2306. * General text area without html editor.
  2307. *
  2308. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2309. */
  2310. class admin_setting_configtextarea extends admin_setting_configtext {
  2311. private $rows;
  2312. private $cols;
  2313. /**
  2314. * @param string $name
  2315. * @param string $visiblename
  2316. * @param string $description
  2317. * @param mixed $defaultsetting string or array
  2318. * @param mixed $paramtype
  2319. * @param string $cols The number of columns to make the editor
  2320. * @param string $rows The number of rows to make the editor
  2321. */
  2322. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
  2323. $this->rows = $rows;
  2324. $this->cols = $cols;
  2325. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
  2326. }
  2327. /**
  2328. * Returns an XHTML string for the editor
  2329. *
  2330. * @param string $data
  2331. * @param string $query
  2332. * @return string XHTML string for the editor
  2333. */
  2334. public function output_html($data, $query='') {
  2335. global $OUTPUT;
  2336. $default = $this->get_defaultsetting();
  2337. $defaultinfo = $default;
  2338. if (!is_null($default) and $default !== '') {
  2339. $defaultinfo = "\n".$default;
  2340. }
  2341. $context = (object) [
  2342. 'cols' => $this->cols,
  2343. 'rows' => $this->rows,
  2344. 'id' => $this->get_id(),
  2345. 'name' => $this->get_full_name(),
  2346. 'value' => $data,
  2347. 'forceltr' => $this->get_force_ltr(),
  2348. 'readonly' => $this->is_readonly(),
  2349. ];
  2350. $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
  2351. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
  2352. }
  2353. }
  2354. /**
  2355. * General text area with html editor.
  2356. */
  2357. class admin_setting_confightmleditor extends admin_setting_configtextarea {
  2358. /**
  2359. * @param string $name
  2360. * @param string $visiblename
  2361. * @param string $description
  2362. * @param mixed $defaultsetting string or array
  2363. * @param mixed $paramtype
  2364. */
  2365. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
  2366. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
  2367. $this->set_force_ltr(false);
  2368. editors_head_setup();
  2369. }
  2370. /**
  2371. * Returns an XHTML string for the editor
  2372. *
  2373. * @param string $data
  2374. * @param string $query
  2375. * @return string XHTML string for the editor
  2376. */
  2377. public function output_html($data, $query='') {
  2378. $editor = editors_get_preferred_editor(FORMAT_HTML);
  2379. $editor->set_text($data);
  2380. $editor->use_editor($this->get_id(), array('noclean'=>true));
  2381. return parent::output_html($data, $query);
  2382. }
  2383. /**
  2384. * Checks if data has empty html.
  2385. *
  2386. * @param string $data
  2387. * @return string Empty when no errors.
  2388. */
  2389. public function write_setting($data) {
  2390. if (trim(html_to_text($data)) === '') {
  2391. $data = '';
  2392. }
  2393. return parent::write_setting($data);
  2394. }
  2395. }
  2396. /**
  2397. * Password field, allows unmasking of password
  2398. *
  2399. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2400. */
  2401. class admin_setting_configpasswordunmask extends admin_setting_configtext {
  2402. /**
  2403. * Constructor
  2404. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2405. * @param string $visiblename localised
  2406. * @param string $description long localised info
  2407. * @param string $defaultsetting default password
  2408. */
  2409. public function __construct($name, $visiblename, $description, $defaultsetting) {
  2410. parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
  2411. }
  2412. /**
  2413. * Log config changes if necessary.
  2414. * @param string $name
  2415. * @param string $oldvalue
  2416. * @param string $value
  2417. */
  2418. protected function add_to_config_log($name, $oldvalue, $value) {
  2419. if ($value !== '') {
  2420. $value = '********';
  2421. }
  2422. if ($oldvalue !== '' and $oldvalue !== null) {
  2423. $oldvalue = '********';
  2424. }
  2425. parent::add_to_config_log($name, $oldvalue, $value);
  2426. }
  2427. /**
  2428. * Returns HTML for the field.
  2429. *
  2430. * @param string $data Value for the field
  2431. * @param string $query Passed as final argument for format_admin_setting
  2432. * @return string Rendered HTML
  2433. */
  2434. public function output_html($data, $query='') {
  2435. global $OUTPUT;
  2436. $context = (object) [
  2437. 'id' => $this->get_id(),
  2438. 'name' => $this->get_full_name(),
  2439. 'size' => $this->size,
  2440. 'value' => $this->is_readonly() ? null : $data,
  2441. 'forceltr' => $this->get_force_ltr(),
  2442. 'readonly' => $this->is_readonly(),
  2443. ];
  2444. $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
  2445. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
  2446. }
  2447. }
  2448. /**
  2449. * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
  2450. *
  2451. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2452. * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
  2453. */
  2454. class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
  2455. /**
  2456. * Constructor
  2457. *
  2458. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2459. * @param string $visiblename localised
  2460. * @param string $description long localised info
  2461. * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
  2462. */
  2463. public function __construct($name, $visiblename, $description, $defaultsetting) {
  2464. parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
  2465. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  2466. }
  2467. }
  2468. /**
  2469. * Admin setting class for encrypted values using secure encryption.
  2470. *
  2471. * @copyright 2019 The Open University
  2472. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2473. */
  2474. class admin_setting_encryptedpassword extends admin_setting {
  2475. /**
  2476. * Constructor. Same as parent except that the default value is always an empty string.
  2477. *
  2478. * @param string $name Internal name used in config table
  2479. * @param string $visiblename Name shown on form
  2480. * @param string $description Description that appears below field
  2481. */
  2482. public function __construct(string $name, string $visiblename, string $description) {
  2483. parent::__construct($name, $visiblename, $description, '');
  2484. }
  2485. public function get_setting() {
  2486. return $this->config_read($this->name);
  2487. }
  2488. public function write_setting($data) {
  2489. $data = trim($data);
  2490. if ($data === '') {
  2491. // Value can really be set to nothing.
  2492. $savedata = '';
  2493. } else {
  2494. // Encrypt value before saving it.
  2495. $savedata = \core\encryption::encrypt($data);
  2496. }
  2497. return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin'));
  2498. }
  2499. public function output_html($data, $query='') {
  2500. global $OUTPUT;
  2501. $default = $this->get_defaultsetting();
  2502. $context = (object) [
  2503. 'id' => $this->get_id(),
  2504. 'name' => $this->get_full_name(),
  2505. 'set' => $data !== '',
  2506. 'novalue' => $this->get_setting() === null
  2507. ];
  2508. $element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context);
  2509. return format_admin_setting($this, $this->visiblename, $element, $this->description,
  2510. true, '', $default, $query);
  2511. }
  2512. }
  2513. /**
  2514. * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
  2515. * Note: Only advanced makes sense right now - locked does not.
  2516. *
  2517. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2518. */
  2519. class admin_setting_configempty extends admin_setting_configtext {
  2520. /**
  2521. * @param string $name
  2522. * @param string $visiblename
  2523. * @param string $description
  2524. */
  2525. public function __construct($name, $visiblename, $description) {
  2526. parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
  2527. }
  2528. /**
  2529. * Returns an XHTML string for the hidden field
  2530. *
  2531. * @param string $data
  2532. * @param string $query
  2533. * @return string XHTML string for the editor
  2534. */
  2535. public function output_html($data, $query='') {
  2536. global $OUTPUT;
  2537. $context = (object) [
  2538. 'id' => $this->get_id(),
  2539. 'name' => $this->get_full_name()
  2540. ];
  2541. $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
  2542. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
  2543. }
  2544. }
  2545. /**
  2546. * Path to directory
  2547. *
  2548. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2549. */
  2550. class admin_setting_configfile extends admin_setting_configtext {
  2551. /**
  2552. * Constructor
  2553. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2554. * @param string $visiblename localised
  2555. * @param string $description long localised info
  2556. * @param string $defaultdirectory default directory location
  2557. */
  2558. public function __construct($name, $visiblename, $description, $defaultdirectory) {
  2559. parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
  2560. }
  2561. /**
  2562. * Returns XHTML for the field
  2563. *
  2564. * Returns XHTML for the field and also checks whether the file
  2565. * specified in $data exists using file_exists()
  2566. *
  2567. * @param string $data File name and path to use in value attr
  2568. * @param string $query
  2569. * @return string XHTML field
  2570. */
  2571. public function output_html($data, $query='') {
  2572. global $CFG, $OUTPUT;
  2573. $default = $this->get_defaultsetting();
  2574. $context = (object) [
  2575. 'id' => $this->get_id(),
  2576. 'name' => $this->get_full_name(),
  2577. 'size' => $this->size,
  2578. 'value' => $data,
  2579. 'showvalidity' => !empty($data),
  2580. 'valid' => $data && file_exists($data),
  2581. 'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(),
  2582. 'forceltr' => $this->get_force_ltr(),
  2583. ];
  2584. if ($context->readonly) {
  2585. $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
  2586. }
  2587. $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
  2588. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  2589. }
  2590. /**
  2591. * Checks if execpatch has been disabled in config.php
  2592. */
  2593. public function write_setting($data) {
  2594. global $CFG;
  2595. if (!empty($CFG->preventexecpath)) {
  2596. if ($this->get_setting() === null) {
  2597. // Use default during installation.
  2598. $data = $this->get_defaultsetting();
  2599. if ($data === null) {
  2600. $data = '';
  2601. }
  2602. } else {
  2603. return '';
  2604. }
  2605. }
  2606. return parent::write_setting($data);
  2607. }
  2608. }
  2609. /**
  2610. * Path to executable file
  2611. *
  2612. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2613. */
  2614. class admin_setting_configexecutable extends admin_setting_configfile {
  2615. /**
  2616. * Returns an XHTML field
  2617. *
  2618. * @param string $data This is the value for the field
  2619. * @param string $query
  2620. * @return string XHTML field
  2621. */
  2622. public function output_html($data, $query='') {
  2623. global $CFG, $OUTPUT;
  2624. $default = $this->get_defaultsetting();
  2625. require_once("$CFG->libdir/filelib.php");
  2626. $context = (object) [
  2627. 'id' => $this->get_id(),
  2628. 'name' => $this->get_full_name(),
  2629. 'size' => $this->size,
  2630. 'value' => $data,
  2631. 'showvalidity' => !empty($data),
  2632. 'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
  2633. 'readonly' => !empty($CFG->preventexecpath),
  2634. 'forceltr' => $this->get_force_ltr()
  2635. ];
  2636. if (!empty($CFG->preventexecpath)) {
  2637. $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
  2638. }
  2639. $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
  2640. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  2641. }
  2642. }
  2643. /**
  2644. * Path to directory
  2645. *
  2646. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2647. */
  2648. class admin_setting_configdirectory extends admin_setting_configfile {
  2649. /**
  2650. * Returns an XHTML field
  2651. *
  2652. * @param string $data This is the value for the field
  2653. * @param string $query
  2654. * @return string XHTML
  2655. */
  2656. public function output_html($data, $query='') {
  2657. global $CFG, $OUTPUT;
  2658. $default = $this->get_defaultsetting();
  2659. $context = (object) [
  2660. 'id' => $this->get_id(),
  2661. 'name' => $this->get_full_name(),
  2662. 'size' => $this->size,
  2663. 'value' => $data,
  2664. 'showvalidity' => !empty($data),
  2665. 'valid' => $data && file_exists($data) && is_dir($data),
  2666. 'readonly' => !empty($CFG->preventexecpath),
  2667. 'forceltr' => $this->get_force_ltr()
  2668. ];
  2669. if (!empty($CFG->preventexecpath)) {
  2670. $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
  2671. }
  2672. $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
  2673. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  2674. }
  2675. }
  2676. /**
  2677. * Checkbox
  2678. *
  2679. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2680. */
  2681. class admin_setting_configcheckbox extends admin_setting {
  2682. /** @var string Value used when checked */
  2683. public $yes;
  2684. /** @var string Value used when not checked */
  2685. public $no;
  2686. /**
  2687. * Constructor
  2688. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2689. * @param string $visiblename localised
  2690. * @param string $description long localised info
  2691. * @param string $defaultsetting
  2692. * @param string $yes value used when checked
  2693. * @param string $no value used when not checked
  2694. */
  2695. public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
  2696. parent::__construct($name, $visiblename, $description, $defaultsetting);
  2697. $this->yes = (string)$yes;
  2698. $this->no = (string)$no;
  2699. }
  2700. /**
  2701. * Retrieves the current setting using the objects name
  2702. *
  2703. * @return string
  2704. */
  2705. public function get_setting() {
  2706. return $this->config_read($this->name);
  2707. }
  2708. /**
  2709. * Sets the value for the setting
  2710. *
  2711. * Sets the value for the setting to either the yes or no values
  2712. * of the object by comparing $data to yes
  2713. *
  2714. * @param mixed $data Gets converted to str for comparison against yes value
  2715. * @return string empty string or error
  2716. */
  2717. public function write_setting($data) {
  2718. if ((string)$data === $this->yes) { // convert to strings before comparison
  2719. $data = $this->yes;
  2720. } else {
  2721. $data = $this->no;
  2722. }
  2723. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  2724. }
  2725. /**
  2726. * Returns an XHTML checkbox field
  2727. *
  2728. * @param string $data If $data matches yes then checkbox is checked
  2729. * @param string $query
  2730. * @return string XHTML field
  2731. */
  2732. public function output_html($data, $query='') {
  2733. global $OUTPUT;
  2734. $context = (object) [
  2735. 'id' => $this->get_id(),
  2736. 'name' => $this->get_full_name(),
  2737. 'no' => $this->no,
  2738. 'value' => $this->yes,
  2739. 'checked' => (string) $data === $this->yes,
  2740. 'readonly' => $this->is_readonly(),
  2741. ];
  2742. $default = $this->get_defaultsetting();
  2743. if (!is_null($default)) {
  2744. if ((string)$default === $this->yes) {
  2745. $defaultinfo = get_string('checkboxyes', 'admin');
  2746. } else {
  2747. $defaultinfo = get_string('checkboxno', 'admin');
  2748. }
  2749. } else {
  2750. $defaultinfo = NULL;
  2751. }
  2752. $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
  2753. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
  2754. }
  2755. }
  2756. /**
  2757. * Multiple checkboxes, each represents different value, stored in csv format
  2758. *
  2759. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2760. */
  2761. class admin_setting_configmulticheckbox extends admin_setting {
  2762. /** @var array Array of choices value=>label */
  2763. public $choices;
  2764. /** @var callable|null Loader function for choices */
  2765. protected $choiceloader = null;
  2766. /**
  2767. * Constructor: uses parent::__construct
  2768. *
  2769. * The $choices parameter may be either an array of $value => $label format,
  2770. * e.g. [1 => get_string('yes')], or a callback function which takes no parameters and
  2771. * returns an array in that format.
  2772. *
  2773. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2774. * @param string $visiblename localised
  2775. * @param string $description long localised info
  2776. * @param array $defaultsetting array of selected
  2777. * @param array|callable $choices array of $value => $label for each checkbox, or a callback
  2778. */
  2779. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  2780. if (is_array($choices)) {
  2781. $this->choices = $choices;
  2782. }
  2783. if (is_callable($choices)) {
  2784. $this->choiceloader = $choices;
  2785. }
  2786. parent::__construct($name, $visiblename, $description, $defaultsetting);
  2787. }
  2788. /**
  2789. * This function may be used in ancestors for lazy loading of choices
  2790. *
  2791. * Override this method if loading of choices is expensive, such
  2792. * as when it requires multiple db requests.
  2793. *
  2794. * @return bool true if loaded, false if error
  2795. */
  2796. public function load_choices() {
  2797. if ($this->choiceloader) {
  2798. if (!is_array($this->choices)) {
  2799. $this->choices = call_user_func($this->choiceloader);
  2800. }
  2801. }
  2802. return true;
  2803. }
  2804. /**
  2805. * Is setting related to query text - used when searching
  2806. *
  2807. * @param string $query
  2808. * @return bool true on related, false on not or failure
  2809. */
  2810. public function is_related($query) {
  2811. if (!$this->load_choices() or empty($this->choices)) {
  2812. return false;
  2813. }
  2814. if (parent::is_related($query)) {
  2815. return true;
  2816. }
  2817. foreach ($this->choices as $desc) {
  2818. if (strpos(core_text::strtolower($desc), $query) !== false) {
  2819. return true;
  2820. }
  2821. }
  2822. return false;
  2823. }
  2824. /**
  2825. * Returns the current setting if it is set
  2826. *
  2827. * @return mixed null if null, else an array
  2828. */
  2829. public function get_setting() {
  2830. $result = $this->config_read($this->name);
  2831. if (is_null($result)) {
  2832. return NULL;
  2833. }
  2834. if ($result === '') {
  2835. return array();
  2836. }
  2837. $enabled = explode(',', $result);
  2838. $setting = array();
  2839. foreach ($enabled as $option) {
  2840. $setting[$option] = 1;
  2841. }
  2842. return $setting;
  2843. }
  2844. /**
  2845. * Saves the setting(s) provided in $data
  2846. *
  2847. * @param array $data An array of data, if not array returns empty str
  2848. * @return mixed empty string on useless data or bool true=success, false=failed
  2849. */
  2850. public function write_setting($data) {
  2851. if (!is_array($data)) {
  2852. return ''; // ignore it
  2853. }
  2854. if (!$this->load_choices() or empty($this->choices)) {
  2855. return '';
  2856. }
  2857. unset($data['xxxxx']);
  2858. $result = array();
  2859. foreach ($data as $key => $value) {
  2860. if ($value and array_key_exists($key, $this->choices)) {
  2861. $result[] = $key;
  2862. }
  2863. }
  2864. return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
  2865. }
  2866. /**
  2867. * Returns XHTML field(s) as required by choices
  2868. *
  2869. * Relies on data being an array should data ever be another valid vartype with
  2870. * acceptable value this may cause a warning/error
  2871. * if (!is_array($data)) would fix the problem
  2872. *
  2873. * @todo Add vartype handling to ensure $data is an array
  2874. *
  2875. * @param array $data An array of checked values
  2876. * @param string $query
  2877. * @return string XHTML field
  2878. */
  2879. public function output_html($data, $query='') {
  2880. global $OUTPUT;
  2881. if (!$this->load_choices() or empty($this->choices)) {
  2882. return '';
  2883. }
  2884. $default = $this->get_defaultsetting();
  2885. if (is_null($default)) {
  2886. $default = array();
  2887. }
  2888. if (is_null($data)) {
  2889. $data = array();
  2890. }
  2891. $context = (object) [
  2892. 'id' => $this->get_id(),
  2893. 'name' => $this->get_full_name(),
  2894. ];
  2895. $options = array();
  2896. $defaults = array();
  2897. foreach ($this->choices as $key => $description) {
  2898. if (!empty($default[$key])) {
  2899. $defaults[] = $description;
  2900. }
  2901. $options[] = [
  2902. 'key' => $key,
  2903. 'checked' => !empty($data[$key]),
  2904. 'label' => highlightfast($query, $description)
  2905. ];
  2906. }
  2907. if (is_null($default)) {
  2908. $defaultinfo = null;
  2909. } else if (!empty($defaults)) {
  2910. $defaultinfo = implode(', ', $defaults);
  2911. } else {
  2912. $defaultinfo = get_string('none');
  2913. }
  2914. $context->options = $options;
  2915. $context->hasoptions = !empty($options);
  2916. $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
  2917. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
  2918. }
  2919. }
  2920. /**
  2921. * Multiple checkboxes 2, value stored as string 00101011
  2922. *
  2923. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2924. */
  2925. class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
  2926. /**
  2927. * Returns the setting if set
  2928. *
  2929. * @return mixed null if not set, else an array of set settings
  2930. */
  2931. public function get_setting() {
  2932. $result = $this->config_read($this->name);
  2933. if (is_null($result)) {
  2934. return NULL;
  2935. }
  2936. if (!$this->load_choices()) {
  2937. return NULL;
  2938. }
  2939. $result = str_pad($result, count($this->choices), '0');
  2940. $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
  2941. $setting = array();
  2942. foreach ($this->choices as $key=>$unused) {
  2943. $value = array_shift($result);
  2944. if ($value) {
  2945. $setting[$key] = 1;
  2946. }
  2947. }
  2948. return $setting;
  2949. }
  2950. /**
  2951. * Save setting(s) provided in $data param
  2952. *
  2953. * @param array $data An array of settings to save
  2954. * @return mixed empty string for bad data or bool true=>success, false=>error
  2955. */
  2956. public function write_setting($data) {
  2957. if (!is_array($data)) {
  2958. return ''; // ignore it
  2959. }
  2960. if (!$this->load_choices() or empty($this->choices)) {
  2961. return '';
  2962. }
  2963. $result = '';
  2964. foreach ($this->choices as $key=>$unused) {
  2965. if (!empty($data[$key])) {
  2966. $result .= '1';
  2967. } else {
  2968. $result .= '0';
  2969. }
  2970. }
  2971. return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
  2972. }
  2973. }
  2974. /**
  2975. * Select one value from list
  2976. *
  2977. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2978. */
  2979. class admin_setting_configselect extends admin_setting {
  2980. /** @var array Array of choices value=>label */
  2981. public $choices;
  2982. /** @var array Array of choices grouped using optgroups */
  2983. public $optgroups;
  2984. /** @var callable|null Loader function for choices */
  2985. protected $choiceloader = null;
  2986. /** @var callable|null Validation function */
  2987. protected $validatefunction = null;
  2988. /**
  2989. * Constructor.
  2990. *
  2991. * If you want to lazy-load the choices, pass a callback function that returns a choice
  2992. * array for the $choices parameter.
  2993. *
  2994. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2995. * @param string $visiblename localised
  2996. * @param string $description long localised info
  2997. * @param string|int $defaultsetting
  2998. * @param array|callable|null $choices array of $value=>$label for each selection, or callback
  2999. */
  3000. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  3001. // Look for optgroup and single options.
  3002. if (is_array($choices)) {
  3003. $this->choices = [];
  3004. foreach ($choices as $key => $val) {
  3005. if (is_array($val)) {
  3006. $this->optgroups[$key] = $val;
  3007. $this->choices = array_merge($this->choices, $val);
  3008. } else {
  3009. $this->choices[$key] = $val;
  3010. }
  3011. }
  3012. }
  3013. if (is_callable($choices)) {
  3014. $this->choiceloader = $choices;
  3015. }
  3016. parent::__construct($name, $visiblename, $description, $defaultsetting);
  3017. }
  3018. /**
  3019. * Sets a validate function.
  3020. *
  3021. * The callback will be passed one parameter, the new setting value, and should return either
  3022. * an empty string '' if the value is OK, or an error message if not.
  3023. *
  3024. * @param callable|null $validatefunction Validate function or null to clear
  3025. * @since Moodle 3.10
  3026. */
  3027. public function set_validate_function(?callable $validatefunction = null) {
  3028. $this->validatefunction = $validatefunction;
  3029. }
  3030. /**
  3031. * This function may be used in ancestors for lazy loading of choices
  3032. *
  3033. * Override this method if loading of choices is expensive, such
  3034. * as when it requires multiple db requests.
  3035. *
  3036. * @return bool true if loaded, false if error
  3037. */
  3038. public function load_choices() {
  3039. if ($this->choiceloader) {
  3040. if (!is_array($this->choices)) {
  3041. $this->choices = call_user_func($this->choiceloader);
  3042. }
  3043. return true;
  3044. }
  3045. return true;
  3046. }
  3047. /**
  3048. * Check if this is $query is related to a choice
  3049. *
  3050. * @param string $query
  3051. * @return bool true if related, false if not
  3052. */
  3053. public function is_related($query) {
  3054. if (parent::is_related($query)) {
  3055. return true;
  3056. }
  3057. if (!$this->load_choices()) {
  3058. return false;
  3059. }
  3060. foreach ($this->choices as $key=>$value) {
  3061. if (strpos(core_text::strtolower($key), $query) !== false) {
  3062. return true;
  3063. }
  3064. if (strpos(core_text::strtolower($value), $query) !== false) {
  3065. return true;
  3066. }
  3067. }
  3068. return false;
  3069. }
  3070. /**
  3071. * Return the setting
  3072. *
  3073. * @return mixed returns config if successful else null
  3074. */
  3075. public function get_setting() {
  3076. return $this->config_read($this->name);
  3077. }
  3078. /**
  3079. * Save a setting
  3080. *
  3081. * @param string $data
  3082. * @return string empty of error string
  3083. */
  3084. public function write_setting($data) {
  3085. if (!$this->load_choices() or empty($this->choices)) {
  3086. return '';
  3087. }
  3088. if (!array_key_exists($data, $this->choices)) {
  3089. return ''; // ignore it
  3090. }
  3091. // Validate the new setting.
  3092. $error = $this->validate_setting($data);
  3093. if ($error) {
  3094. return $error;
  3095. }
  3096. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  3097. }
  3098. /**
  3099. * Validate the setting. This uses the callback function if provided; subclasses could override
  3100. * to carry out validation directly in the class.
  3101. *
  3102. * @param string $data New value being set
  3103. * @return string Empty string if valid, or error message text
  3104. * @since Moodle 3.10
  3105. */
  3106. protected function validate_setting(string $data): string {
  3107. // If validation function is specified, call it now.
  3108. if ($this->validatefunction) {
  3109. return call_user_func($this->validatefunction, $data);
  3110. } else {
  3111. return '';
  3112. }
  3113. }
  3114. /**
  3115. * Returns XHTML select field
  3116. *
  3117. * Ensure the options are loaded, and generate the XHTML for the select
  3118. * element and any warning message. Separating this out from output_html
  3119. * makes it easier to subclass this class.
  3120. *
  3121. * @param string $data the option to show as selected.
  3122. * @param string $current the currently selected option in the database, null if none.
  3123. * @param string $default the default selected option.
  3124. * @return array the HTML for the select element, and a warning message.
  3125. * @deprecated since Moodle 3.2
  3126. */
  3127. public function output_select_html($data, $current, $default, $extraname = '') {
  3128. debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
  3129. }
  3130. /**
  3131. * Returns XHTML select field and wrapping div(s)
  3132. *
  3133. * @see output_select_html()
  3134. *
  3135. * @param string $data the option to show as selected
  3136. * @param string $query
  3137. * @return string XHTML field and wrapping div
  3138. */
  3139. public function output_html($data, $query='') {
  3140. global $OUTPUT;
  3141. $default = $this->get_defaultsetting();
  3142. $current = $this->get_setting();
  3143. if (!$this->load_choices() || empty($this->choices)) {
  3144. return '';
  3145. }
  3146. $context = (object) [
  3147. 'id' => $this->get_id(),
  3148. 'name' => $this->get_full_name(),
  3149. ];
  3150. if (!is_null($default) && array_key_exists($default, $this->choices)) {
  3151. $defaultinfo = $this->choices[$default];
  3152. } else {
  3153. $defaultinfo = NULL;
  3154. }
  3155. // Warnings.
  3156. $warning = '';
  3157. if ($current === null) {
  3158. // First run.
  3159. } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
  3160. // No warning.
  3161. } else if (!array_key_exists($current, $this->choices)) {
  3162. $warning = get_string('warningcurrentsetting', 'admin', $current);
  3163. if (!is_null($default) && $data == $current) {
  3164. $data = $default; // Use default instead of first value when showing the form.
  3165. }
  3166. }
  3167. $options = [];
  3168. $template = 'core_admin/setting_configselect';
  3169. if (!empty($this->optgroups)) {
  3170. $optgroups = [];
  3171. foreach ($this->optgroups as $label => $choices) {
  3172. $optgroup = array('label' => $label, 'options' => []);
  3173. foreach ($choices as $value => $name) {
  3174. $optgroup['options'][] = [
  3175. 'value' => $value,
  3176. 'name' => $name,
  3177. 'selected' => (string) $value == $data
  3178. ];
  3179. unset($this->choices[$value]);
  3180. }
  3181. $optgroups[] = $optgroup;
  3182. }
  3183. $context->options = $options;
  3184. $context->optgroups = $optgroups;
  3185. $template = 'core_admin/setting_configselect_optgroup';
  3186. }
  3187. foreach ($this->choices as $value => $name) {
  3188. $options[] = [
  3189. 'value' => $value,
  3190. 'name' => $name,
  3191. 'selected' => (string) $value == $data
  3192. ];
  3193. }
  3194. $context->options = $options;
  3195. $context->readonly = $this->is_readonly();
  3196. $element = $OUTPUT->render_from_template($template, $context);
  3197. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
  3198. }
  3199. }
  3200. /**
  3201. * Select multiple items from list
  3202. *
  3203. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3204. */
  3205. class admin_setting_configmultiselect extends admin_setting_configselect {
  3206. /**
  3207. * Constructor
  3208. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  3209. * @param string $visiblename localised
  3210. * @param string $description long localised info
  3211. * @param array $defaultsetting array of selected items
  3212. * @param array $choices array of $value=>$label for each list item
  3213. */
  3214. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  3215. parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
  3216. }
  3217. /**
  3218. * Returns the select setting(s)
  3219. *
  3220. * @return mixed null or array. Null if no settings else array of setting(s)
  3221. */
  3222. public function get_setting() {
  3223. $result = $this->config_read($this->name);
  3224. if (is_null($result)) {
  3225. return NULL;
  3226. }
  3227. if ($result === '') {
  3228. return array();
  3229. }
  3230. return explode(',', $result);
  3231. }
  3232. /**
  3233. * Saves setting(s) provided through $data
  3234. *
  3235. * Potential bug in the works should anyone call with this function
  3236. * using a vartype that is not an array
  3237. *
  3238. * @param array $data
  3239. */
  3240. public function write_setting($data) {
  3241. if (!is_array($data)) {
  3242. return ''; //ignore it
  3243. }
  3244. if (!$this->load_choices() or empty($this->choices)) {
  3245. return '';
  3246. }
  3247. unset($data['xxxxx']);
  3248. $save = array();
  3249. foreach ($data as $value) {
  3250. if (!array_key_exists($value, $this->choices)) {
  3251. continue; // ignore it
  3252. }
  3253. $save[] = $value;
  3254. }
  3255. return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
  3256. }
  3257. /**
  3258. * Is setting related to query text - used when searching
  3259. *
  3260. * @param string $query
  3261. * @return bool true if related, false if not
  3262. */
  3263. public function is_related($query) {
  3264. if (!$this->load_choices() or empty($this->choices)) {
  3265. return false;
  3266. }
  3267. if (parent::is_related($query)) {
  3268. return true;
  3269. }
  3270. foreach ($this->choices as $desc) {
  3271. if (strpos(core_text::strtolower($desc), $query) !== false) {
  3272. return true;
  3273. }
  3274. }
  3275. return false;
  3276. }
  3277. /**
  3278. * Returns XHTML multi-select field
  3279. *
  3280. * @todo Add vartype handling to ensure $data is an array
  3281. * @param array $data Array of values to select by default
  3282. * @param string $query
  3283. * @return string XHTML multi-select field
  3284. */
  3285. public function output_html($data, $query='') {
  3286. global $OUTPUT;
  3287. if (!$this->load_choices() or empty($this->choices)) {
  3288. return '';
  3289. }
  3290. $default = $this->get_defaultsetting();
  3291. if (is_null($default)) {
  3292. $default = array();
  3293. }
  3294. if (is_null($data)) {
  3295. $data = array();
  3296. }
  3297. $context = (object) [
  3298. 'id' => $this->get_id(),
  3299. 'name' => $this->get_full_name(),
  3300. 'size' => min(10, count($this->choices))
  3301. ];
  3302. $defaults = [];
  3303. $options = [];
  3304. $template = 'core_admin/setting_configmultiselect';
  3305. if (!empty($this->optgroups)) {
  3306. $optgroups = [];
  3307. foreach ($this->optgroups as $label => $choices) {
  3308. $optgroup = array('label' => $label, 'options' => []);
  3309. foreach ($choices as $value => $name) {
  3310. if (in_array($value, $default)) {
  3311. $defaults[] = $name;
  3312. }
  3313. $optgroup['options'][] = [
  3314. 'value' => $value,
  3315. 'name' => $name,
  3316. 'selected' => in_array($value, $data)
  3317. ];
  3318. unset($this->choices[$value]);
  3319. }
  3320. $optgroups[] = $optgroup;
  3321. }
  3322. $context->optgroups = $optgroups;
  3323. $template = 'core_admin/setting_configmultiselect_optgroup';
  3324. }
  3325. foreach ($this->choices as $value => $name) {
  3326. if (in_array($value, $default)) {
  3327. $defaults[] = $name;
  3328. }
  3329. $options[] = [
  3330. 'value' => $value,
  3331. 'name' => $name,
  3332. 'selected' => in_array($value, $data)
  3333. ];
  3334. }
  3335. $context->options = $options;
  3336. $context->readonly = $this->is_readonly();
  3337. if (is_null($default)) {
  3338. $defaultinfo = NULL;
  3339. } if (!empty($defaults)) {
  3340. $defaultinfo = implode(', ', $defaults);
  3341. } else {
  3342. $defaultinfo = get_string('none');
  3343. }
  3344. $element = $OUTPUT->render_from_template($template, $context);
  3345. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
  3346. }
  3347. }
  3348. /**
  3349. * Time selector
  3350. *
  3351. * This is a liiitle bit messy. we're using two selects, but we're returning
  3352. * them as an array named after $name (so we only use $name2 internally for the setting)
  3353. *
  3354. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3355. */
  3356. class admin_setting_configtime extends admin_setting {
  3357. /** @var string Used for setting second select (minutes) */
  3358. public $name2;
  3359. /**
  3360. * Constructor
  3361. * @param string $hoursname setting for hours
  3362. * @param string $minutesname setting for hours
  3363. * @param string $visiblename localised
  3364. * @param string $description long localised info
  3365. * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
  3366. */
  3367. public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
  3368. $this->name2 = $minutesname;
  3369. parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
  3370. }
  3371. /**
  3372. * Get the selected time
  3373. *
  3374. * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
  3375. */
  3376. public function get_setting() {
  3377. $result1 = $this->config_read($this->name);
  3378. $result2 = $this->config_read($this->name2);
  3379. if (is_null($result1) or is_null($result2)) {
  3380. return NULL;
  3381. }
  3382. return array('h' => $result1, 'm' => $result2);
  3383. }
  3384. /**
  3385. * Store the time (hours and minutes)
  3386. *
  3387. * @param array $data Must be form 'h'=>xx, 'm'=>xx
  3388. * @return bool true if success, false if not
  3389. */
  3390. public function write_setting($data) {
  3391. if (!is_array($data)) {
  3392. return '';
  3393. }
  3394. $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
  3395. return ($result ? '' : get_string('errorsetting', 'admin'));
  3396. }
  3397. /**
  3398. * Returns XHTML time select fields
  3399. *
  3400. * @param array $data Must be form 'h'=>xx, 'm'=>xx
  3401. * @param string $query
  3402. * @return string XHTML time select fields and wrapping div(s)
  3403. */
  3404. public function output_html($data, $query='') {
  3405. global $OUTPUT;
  3406. $default = $this->get_defaultsetting();
  3407. if (is_array($default)) {
  3408. $defaultinfo = $default['h'].':'.$default['m'];
  3409. } else {
  3410. $defaultinfo = NULL;
  3411. }
  3412. $context = (object) [
  3413. 'id' => $this->get_id(),
  3414. 'name' => $this->get_full_name(),
  3415. 'readonly' => $this->is_readonly(),
  3416. 'hours' => array_map(function($i) use ($data) {
  3417. return [
  3418. 'value' => $i,
  3419. 'name' => $i,
  3420. 'selected' => $i == $data['h']
  3421. ];
  3422. }, range(0, 23)),
  3423. 'minutes' => array_map(function($i) use ($data) {
  3424. return [
  3425. 'value' => $i,
  3426. 'name' => $i,
  3427. 'selected' => $i == $data['m']
  3428. ];
  3429. }, range(0, 59, 5))
  3430. ];
  3431. $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
  3432. return format_admin_setting($this, $this->visiblename, $element, $this->description,
  3433. $this->get_id() . 'h', '', $defaultinfo, $query);
  3434. }
  3435. }
  3436. /**
  3437. * Seconds duration setting.
  3438. *
  3439. * @copyright 2012 Petr Skoda (http://skodak.org)
  3440. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3441. */
  3442. class admin_setting_configduration extends admin_setting {
  3443. /** @var int default duration unit */
  3444. protected $defaultunit;
  3445. /** @var callable|null Validation function */
  3446. protected $validatefunction = null;
  3447. /**
  3448. * Constructor
  3449. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  3450. * or 'myplugin/mysetting' for ones in config_plugins.
  3451. * @param string $visiblename localised name
  3452. * @param string $description localised long description
  3453. * @param mixed $defaultsetting string or array depending on implementation
  3454. * @param int $defaultunit - day, week, etc. (in seconds)
  3455. */
  3456. public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
  3457. if (is_number($defaultsetting)) {
  3458. $defaultsetting = self::parse_seconds($defaultsetting);
  3459. }
  3460. $units = self::get_units();
  3461. if (isset($units[$defaultunit])) {
  3462. $this->defaultunit = $defaultunit;
  3463. } else {
  3464. $this->defaultunit = 86400;
  3465. }
  3466. parent::__construct($name, $visiblename, $description, $defaultsetting);
  3467. }
  3468. /**
  3469. * Sets a validate function.
  3470. *
  3471. * The callback will be passed one parameter, the new setting value, and should return either
  3472. * an empty string '' if the value is OK, or an error message if not.
  3473. *
  3474. * @param callable|null $validatefunction Validate function or null to clear
  3475. * @since Moodle 3.10
  3476. */
  3477. public function set_validate_function(?callable $validatefunction = null) {
  3478. $this->validatefunction = $validatefunction;
  3479. }
  3480. /**
  3481. * Validate the setting. This uses the callback function if provided; subclasses could override
  3482. * to carry out validation directly in the class.
  3483. *
  3484. * @param int $data New value being set
  3485. * @return string Empty string if valid, or error message text
  3486. * @since Moodle 3.10
  3487. */
  3488. protected function validate_setting(int $data): string {
  3489. // If validation function is specified, call it now.
  3490. if ($this->validatefunction) {
  3491. return call_user_func($this->validatefunction, $data);
  3492. } else {
  3493. if ($data < 0) {
  3494. return get_string('errorsetting', 'admin');
  3495. }
  3496. return '';
  3497. }
  3498. }
  3499. /**
  3500. * Returns selectable units.
  3501. * @static
  3502. * @return array
  3503. */
  3504. protected static function get_units() {
  3505. return array(
  3506. 604800 => get_string('weeks'),
  3507. 86400 => get_string('days'),
  3508. 3600 => get_string('hours'),
  3509. 60 => get_string('minutes'),
  3510. 1 => get_string('seconds'),
  3511. );
  3512. }
  3513. /**
  3514. * Converts seconds to some more user friendly string.
  3515. * @static
  3516. * @param int $seconds
  3517. * @return string
  3518. */
  3519. protected static function get_duration_text($seconds) {
  3520. if (empty($seconds)) {
  3521. return get_string('none');
  3522. }
  3523. $data = self::parse_seconds($seconds);
  3524. switch ($data['u']) {
  3525. case (60*60*24*7):
  3526. return get_string('numweeks', '', $data['v']);
  3527. case (60*60*24):
  3528. return get_string('numdays', '', $data['v']);
  3529. case (60*60):
  3530. return get_string('numhours', '', $data['v']);
  3531. case (60):
  3532. return get_string('numminutes', '', $data['v']);
  3533. default:
  3534. return get_string('numseconds', '', $data['v']*$data['u']);
  3535. }
  3536. }
  3537. /**
  3538. * Finds suitable units for given duration.
  3539. * @static
  3540. * @param int $seconds
  3541. * @return array
  3542. */
  3543. protected static function parse_seconds($seconds) {
  3544. foreach (self::get_units() as $unit => $unused) {
  3545. if ($seconds % $unit === 0) {
  3546. return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
  3547. }
  3548. }
  3549. return array('v'=>(int)$seconds, 'u'=>1);
  3550. }
  3551. /**
  3552. * Get the selected duration as array.
  3553. *
  3554. * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
  3555. */
  3556. public function get_setting() {
  3557. $seconds = $this->config_read($this->name);
  3558. if (is_null($seconds)) {
  3559. return null;
  3560. }
  3561. return self::parse_seconds($seconds);
  3562. }
  3563. /**
  3564. * Store the duration as seconds.
  3565. *
  3566. * @param array $data Must be form 'h'=>xx, 'm'=>xx
  3567. * @return bool true if success, false if not
  3568. */
  3569. public function write_setting($data) {
  3570. if (!is_array($data)) {
  3571. return '';
  3572. }
  3573. $seconds = (int)($data['v']*$data['u']);
  3574. // Validate the new setting.
  3575. $error = $this->validate_setting($seconds);
  3576. if ($error) {
  3577. return $error;
  3578. }
  3579. $result = $this->config_write($this->name, $seconds);
  3580. return ($result ? '' : get_string('errorsetting', 'admin'));
  3581. }
  3582. /**
  3583. * Returns duration text+select fields.
  3584. *
  3585. * @param array $data Must be form 'v'=>xx, 'u'=>xx
  3586. * @param string $query
  3587. * @return string duration text+select fields and wrapping div(s)
  3588. */
  3589. public function output_html($data, $query='') {
  3590. global $OUTPUT;
  3591. $default = $this->get_defaultsetting();
  3592. if (is_number($default)) {
  3593. $defaultinfo = self::get_duration_text($default);
  3594. } else if (is_array($default)) {
  3595. $defaultinfo = self::get_duration_text($default['v']*$default['u']);
  3596. } else {
  3597. $defaultinfo = null;
  3598. }
  3599. $inputid = $this->get_id() . 'v';
  3600. $units = self::get_units();
  3601. $defaultunit = $this->defaultunit;
  3602. $context = (object) [
  3603. 'id' => $this->get_id(),
  3604. 'name' => $this->get_full_name(),
  3605. 'value' => $data['v'],
  3606. 'readonly' => $this->is_readonly(),
  3607. 'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
  3608. return [
  3609. 'value' => $unit,
  3610. 'name' => $units[$unit],
  3611. 'selected' => ($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']
  3612. ];
  3613. }, array_keys($units))
  3614. ];
  3615. $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
  3616. return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
  3617. }
  3618. }
  3619. /**
  3620. * Seconds duration setting with an advanced checkbox, that controls a additional
  3621. * $name.'_adv' setting.
  3622. *
  3623. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3624. * @copyright 2014 The Open University
  3625. */
  3626. class admin_setting_configduration_with_advanced extends admin_setting_configduration {
  3627. /**
  3628. * Constructor
  3629. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  3630. * or 'myplugin/mysetting' for ones in config_plugins.
  3631. * @param string $visiblename localised name
  3632. * @param string $description localised long description
  3633. * @param array $defaultsetting array of int value, and bool whether it is
  3634. * is advanced by default.
  3635. * @param int $defaultunit - day, week, etc. (in seconds)
  3636. */
  3637. public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
  3638. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
  3639. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  3640. }
  3641. }
  3642. /**
  3643. * Used to validate a textarea used for ip addresses
  3644. *
  3645. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3646. * @copyright 2011 Petr Skoda (http://skodak.org)
  3647. */
  3648. class admin_setting_configiplist extends admin_setting_configtextarea {
  3649. /**
  3650. * Validate the contents of the textarea as IP addresses
  3651. *
  3652. * Used to validate a new line separated list of IP addresses collected from
  3653. * a textarea control
  3654. *
  3655. * @param string $data A list of IP Addresses separated by new lines
  3656. * @return mixed bool true for success or string:error on failure
  3657. */
  3658. public function validate($data) {
  3659. if(!empty($data)) {
  3660. $lines = explode("\n", $data);
  3661. } else {
  3662. return true;
  3663. }
  3664. $result = true;
  3665. $badips = array();
  3666. foreach ($lines as $line) {
  3667. $tokens = explode('#', $line);
  3668. $ip = trim($tokens[0]);
  3669. if (empty($ip)) {
  3670. continue;
  3671. }
  3672. if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
  3673. preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
  3674. preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
  3675. } else {
  3676. $result = false;
  3677. $badips[] = $ip;
  3678. }
  3679. }
  3680. if($result) {
  3681. return true;
  3682. } else {
  3683. return get_string('validateiperror', 'admin', join(', ', $badips));
  3684. }
  3685. }
  3686. }
  3687. /**
  3688. * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
  3689. *
  3690. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3691. * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
  3692. */
  3693. class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
  3694. /**
  3695. * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
  3696. * Used to validate a new line separated list of entries collected from a textarea control.
  3697. *
  3698. * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
  3699. * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
  3700. * via the get_setting() method, which has been overriden.
  3701. *
  3702. * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
  3703. * @return mixed bool true for success or string:error on failure
  3704. */
  3705. public function validate($data) {
  3706. if (empty($data)) {
  3707. return true;
  3708. }
  3709. $entries = explode("\n", $data);
  3710. $badentries = [];
  3711. foreach ($entries as $key => $entry) {
  3712. $entry = trim($entry);
  3713. if (empty($entry)) {
  3714. return get_string('validateemptylineerror', 'admin');
  3715. }
  3716. // Validate each string entry against the supported formats.
  3717. if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
  3718. || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
  3719. || \core\ip_utils::is_domain_matching_pattern($entry)) {
  3720. continue;
  3721. }
  3722. // Otherwise, the entry is invalid.
  3723. $badentries[] = $entry;
  3724. }
  3725. if ($badentries) {
  3726. return get_string('validateerrorlist', 'admin', join(', ', $badentries));
  3727. }
  3728. return true;
  3729. }
  3730. /**
  3731. * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
  3732. *
  3733. * @param string $data the setting data, as sent from the web form.
  3734. * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
  3735. */
  3736. protected function ace_encode($data) {
  3737. if (empty($data)) {
  3738. return $data;
  3739. }
  3740. $entries = explode("\n", $data);
  3741. foreach ($entries as $key => $entry) {
  3742. $entry = trim($entry);
  3743. // This regex matches any string that has non-ascii character.
  3744. if (preg_match('/[^\x00-\x7f]/', $entry)) {
  3745. // If we can convert the unicode string to an idn, do so.
  3746. // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
  3747. $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
  3748. $entries[$key] = $val ? $val : $entry;
  3749. }
  3750. }
  3751. return implode("\n", $entries);
  3752. }
  3753. /**
  3754. * Decode any ascii-encoded domain names back to their utf-8 representation for display.
  3755. *
  3756. * @param string $data the setting data, as found in the database.
  3757. * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
  3758. */
  3759. protected function ace_decode($data) {
  3760. $entries = explode("\n", $data);
  3761. foreach ($entries as $key => $entry) {
  3762. $entry = trim($entry);
  3763. if (strpos($entry, 'xn--') !== false) {
  3764. $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
  3765. }
  3766. }
  3767. return implode("\n", $entries);
  3768. }
  3769. /**
  3770. * Override, providing utf8-decoding for ascii-encoded IDN strings.
  3771. *
  3772. * @return mixed returns punycode-converted setting string if successful, else null.
  3773. */
  3774. public function get_setting() {
  3775. // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
  3776. $data = $this->config_read($this->name);
  3777. if (function_exists('idn_to_utf8') && !is_null($data)) {
  3778. $data = $this->ace_decode($data);
  3779. }
  3780. return $data;
  3781. }
  3782. /**
  3783. * Override, providing ascii-encoding for utf8 (native) IDN strings.
  3784. *
  3785. * @param string $data
  3786. * @return string
  3787. */
  3788. public function write_setting($data) {
  3789. if ($this->paramtype === PARAM_INT and $data === '') {
  3790. // Do not complain if '' used instead of 0.
  3791. $data = 0;
  3792. }
  3793. // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
  3794. if (function_exists('idn_to_ascii')) {
  3795. $data = $this->ace_encode($data);
  3796. }
  3797. $validated = $this->validate($data);
  3798. if ($validated !== true) {
  3799. return $validated;
  3800. }
  3801. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  3802. }
  3803. }
  3804. /**
  3805. * Used to validate a textarea used for port numbers.
  3806. *
  3807. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3808. * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
  3809. */
  3810. class admin_setting_configportlist extends admin_setting_configtextarea {
  3811. /**
  3812. * Validate the contents of the textarea as port numbers.
  3813. * Used to validate a new line separated list of ports collected from a textarea control.
  3814. *
  3815. * @param string $data A list of ports separated by new lines
  3816. * @return mixed bool true for success or string:error on failure
  3817. */
  3818. public function validate($data) {
  3819. if (empty($data)) {
  3820. return true;
  3821. }
  3822. $ports = explode("\n", $data);
  3823. $badentries = [];
  3824. foreach ($ports as $port) {
  3825. $port = trim($port);
  3826. if (empty($port)) {
  3827. return get_string('validateemptylineerror', 'admin');
  3828. }
  3829. // Is the string a valid integer number?
  3830. if (strval(intval($port)) !== $port || intval($port) <= 0) {
  3831. $badentries[] = $port;
  3832. }
  3833. }
  3834. if ($badentries) {
  3835. return get_string('validateerrorlist', 'admin', $badentries);
  3836. }
  3837. return true;
  3838. }
  3839. }
  3840. /**
  3841. * An admin setting for selecting one or more users who have a capability
  3842. * in the system context
  3843. *
  3844. * An admin setting for selecting one or more users, who have a particular capability
  3845. * in the system context. Warning, make sure the list will never be too long. There is
  3846. * no paging or searching of this list.
  3847. *
  3848. * To correctly get a list of users from this config setting, you need to call the
  3849. * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
  3850. *
  3851. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3852. */
  3853. class admin_setting_users_with_capability extends admin_setting_configmultiselect {
  3854. /** @var string The capabilities name */
  3855. protected $capability;
  3856. /** @var int include admin users too */
  3857. protected $includeadmins;
  3858. /**
  3859. * Constructor.
  3860. *
  3861. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  3862. * @param string $visiblename localised name
  3863. * @param string $description localised long description
  3864. * @param array $defaultsetting array of usernames
  3865. * @param string $capability string capability name.
  3866. * @param bool $includeadmins include administrators
  3867. */
  3868. function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
  3869. $this->capability = $capability;
  3870. $this->includeadmins = $includeadmins;
  3871. parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
  3872. }
  3873. /**
  3874. * Load all of the uses who have the capability into choice array
  3875. *
  3876. * @return bool Always returns true
  3877. */
  3878. function load_choices() {
  3879. if (is_array($this->choices)) {
  3880. return true;
  3881. }
  3882. list($sort, $sortparams) = users_order_by_sql('u');
  3883. if (!empty($sortparams)) {
  3884. throw new coding_exception('users_order_by_sql returned some query parameters. ' .
  3885. 'This is unexpected, and a problem because there is no way to pass these ' .
  3886. 'parameters to get_users_by_capability. See MDL-34657.');
  3887. }
  3888. $userfieldsapi = \core_user\fields::for_name();
  3889. $userfields = 'u.id, u.username, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects;
  3890. $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
  3891. $this->choices = array(
  3892. '$@NONE@$' => get_string('nobody'),
  3893. '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
  3894. );
  3895. if ($this->includeadmins) {
  3896. $admins = get_admins();
  3897. foreach ($admins as $user) {
  3898. $this->choices[$user->id] = fullname($user);
  3899. }
  3900. }
  3901. if (is_array($users)) {
  3902. foreach ($users as $user) {
  3903. $this->choices[$user->id] = fullname($user);
  3904. }
  3905. }
  3906. return true;
  3907. }
  3908. /**
  3909. * Returns the default setting for class
  3910. *
  3911. * @return mixed Array, or string. Empty string if no default
  3912. */
  3913. public function get_defaultsetting() {
  3914. $this->load_choices();
  3915. $defaultsetting = parent::get_defaultsetting();
  3916. if (empty($defaultsetting)) {
  3917. return array('$@NONE@$');
  3918. } else if (array_key_exists($defaultsetting, $this->choices)) {
  3919. return $defaultsetting;
  3920. } else {
  3921. return '';
  3922. }
  3923. }
  3924. /**
  3925. * Returns the current setting
  3926. *
  3927. * @return mixed array or string
  3928. */
  3929. public function get_setting() {
  3930. $result = parent::get_setting();
  3931. if ($result === null) {
  3932. // this is necessary for settings upgrade
  3933. return null;
  3934. }
  3935. if (empty($result)) {
  3936. $result = array('$@NONE@$');
  3937. }
  3938. return $result;
  3939. }
  3940. /**
  3941. * Save the chosen setting provided as $data
  3942. *
  3943. * @param array $data
  3944. * @return mixed string or array
  3945. */
  3946. public function write_setting($data) {
  3947. // If all is selected, remove any explicit options.
  3948. if (in_array('$@ALL@$', $data)) {
  3949. $data = array('$@ALL@$');
  3950. }
  3951. // None never needs to be written to the DB.
  3952. if (in_array('$@NONE@$', $data)) {
  3953. unset($data[array_search('$@NONE@$', $data)]);
  3954. }
  3955. return parent::write_setting($data);
  3956. }
  3957. }
  3958. /**
  3959. * Special checkbox for calendar - resets SESSION vars.
  3960. *
  3961. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3962. */
  3963. class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
  3964. /**
  3965. * Calls the parent::__construct with default values
  3966. *
  3967. * name => calendar_adminseesall
  3968. * visiblename => get_string('adminseesall', 'admin')
  3969. * description => get_string('helpadminseesall', 'admin')
  3970. * defaultsetting => 0
  3971. */
  3972. public function __construct() {
  3973. parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
  3974. get_string('helpadminseesall', 'admin'), '0');
  3975. }
  3976. /**
  3977. * Stores the setting passed in $data
  3978. *
  3979. * @param mixed gets converted to string for comparison
  3980. * @return string empty string or error message
  3981. */
  3982. public function write_setting($data) {
  3983. global $SESSION;
  3984. return parent::write_setting($data);
  3985. }
  3986. }
  3987. /**
  3988. * Special select for settings that are altered in setup.php and can not be altered on the fly
  3989. *
  3990. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3991. */
  3992. class admin_setting_special_selectsetup extends admin_setting_configselect {
  3993. /**
  3994. * Reads the setting directly from the database
  3995. *
  3996. * @return mixed
  3997. */
  3998. public function get_setting() {
  3999. // read directly from db!
  4000. return get_config(NULL, $this->name);
  4001. }
  4002. /**
  4003. * Save the setting passed in $data
  4004. *
  4005. * @param string $data The setting to save
  4006. * @return string empty or error message
  4007. */
  4008. public function write_setting($data) {
  4009. global $CFG;
  4010. // do not change active CFG setting!
  4011. $current = $CFG->{$this->name};
  4012. $result = parent::write_setting($data);
  4013. $CFG->{$this->name} = $current;
  4014. return $result;
  4015. }
  4016. }
  4017. /**
  4018. * Special select for frontpage - stores data in course table
  4019. *
  4020. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4021. */
  4022. class admin_setting_sitesetselect extends admin_setting_configselect {
  4023. /**
  4024. * Returns the site name for the selected site
  4025. *
  4026. * @see get_site()
  4027. * @return string The site name of the selected site
  4028. */
  4029. public function get_setting() {
  4030. $site = course_get_format(get_site())->get_course();
  4031. return $site->{$this->name};
  4032. }
  4033. /**
  4034. * Updates the database and save the setting
  4035. *
  4036. * @param string data
  4037. * @return string empty or error message
  4038. */
  4039. public function write_setting($data) {
  4040. global $DB, $SITE, $COURSE;
  4041. if (!in_array($data, array_keys($this->choices))) {
  4042. return get_string('errorsetting', 'admin');
  4043. }
  4044. $record = new stdClass();
  4045. $record->id = SITEID;
  4046. $temp = $this->name;
  4047. $record->$temp = $data;
  4048. $record->timemodified = time();
  4049. course_get_format($SITE)->update_course_format_options($record);
  4050. $DB->update_record('course', $record);
  4051. // Reset caches.
  4052. $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
  4053. if ($SITE->id == $COURSE->id) {
  4054. $COURSE = $SITE;
  4055. }
  4056. core_courseformat\base::reset_course_cache($SITE->id);
  4057. return '';
  4058. }
  4059. }
  4060. /**
  4061. * Select for blog's bloglevel setting: if set to 0, will set blog_menu
  4062. * block to hidden.
  4063. *
  4064. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4065. */
  4066. class admin_setting_bloglevel extends admin_setting_configselect {
  4067. /**
  4068. * Updates the database and save the setting
  4069. *
  4070. * @param string data
  4071. * @return string empty or error message
  4072. */
  4073. public function write_setting($data) {
  4074. global $DB, $CFG;
  4075. if ($data == 0) {
  4076. $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
  4077. foreach ($blogblocks as $block) {
  4078. $DB->set_field('block', 'visible', 0, array('id' => $block->id));
  4079. }
  4080. } else {
  4081. // reenable all blocks only when switching from disabled blogs
  4082. if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
  4083. $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
  4084. foreach ($blogblocks as $block) {
  4085. $DB->set_field('block', 'visible', 1, array('id' => $block->id));
  4086. }
  4087. }
  4088. }
  4089. return parent::write_setting($data);
  4090. }
  4091. }
  4092. /**
  4093. * Special select - lists on the frontpage - hacky
  4094. *
  4095. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4096. */
  4097. class admin_setting_courselist_frontpage extends admin_setting {
  4098. /** @var array Array of choices value=>label */
  4099. public $choices;
  4100. /**
  4101. * Construct override, requires one param
  4102. *
  4103. * @param bool $loggedin Is the user logged in
  4104. */
  4105. public function __construct($loggedin) {
  4106. global $CFG;
  4107. require_once($CFG->dirroot.'/course/lib.php');
  4108. $name = 'frontpage'.($loggedin ? 'loggedin' : '');
  4109. $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
  4110. $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
  4111. $defaults = array(FRONTPAGEALLCOURSELIST);
  4112. parent::__construct($name, $visiblename, $description, $defaults);
  4113. }
  4114. /**
  4115. * Loads the choices available
  4116. *
  4117. * @return bool always returns true
  4118. */
  4119. public function load_choices() {
  4120. if (is_array($this->choices)) {
  4121. return true;
  4122. }
  4123. $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
  4124. FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
  4125. FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
  4126. FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
  4127. FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
  4128. FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
  4129. 'none' => get_string('none'));
  4130. if ($this->name === 'frontpage') {
  4131. unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
  4132. }
  4133. return true;
  4134. }
  4135. /**
  4136. * Returns the selected settings
  4137. *
  4138. * @param mixed array or setting or null
  4139. */
  4140. public function get_setting() {
  4141. $result = $this->config_read($this->name);
  4142. if (is_null($result)) {
  4143. return NULL;
  4144. }
  4145. if ($result === '') {
  4146. return array();
  4147. }
  4148. return explode(',', $result);
  4149. }
  4150. /**
  4151. * Save the selected options
  4152. *
  4153. * @param array $data
  4154. * @return mixed empty string (data is not an array) or bool true=success false=failure
  4155. */
  4156. public function write_setting($data) {
  4157. if (!is_array($data)) {
  4158. return '';
  4159. }
  4160. $this->load_choices();
  4161. $save = array();
  4162. foreach($data as $datum) {
  4163. if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
  4164. continue;
  4165. }
  4166. $save[$datum] = $datum; // no duplicates
  4167. }
  4168. return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
  4169. }
  4170. /**
  4171. * Return XHTML select field and wrapping div
  4172. *
  4173. * @todo Add vartype handling to make sure $data is an array
  4174. * @param array $data Array of elements to select by default
  4175. * @return string XHTML select field and wrapping div
  4176. */
  4177. public function output_html($data, $query='') {
  4178. global $OUTPUT;
  4179. $this->load_choices();
  4180. $currentsetting = array();
  4181. foreach ($data as $key) {
  4182. if ($key != 'none' and array_key_exists($key, $this->choices)) {
  4183. $currentsetting[] = $key; // already selected first
  4184. }
  4185. }
  4186. $context = (object) [
  4187. 'id' => $this->get_id(),
  4188. 'name' => $this->get_full_name(),
  4189. ];
  4190. $options = $this->choices;
  4191. $selects = [];
  4192. for ($i = 0; $i < count($this->choices) - 1; $i++) {
  4193. if (!array_key_exists($i, $currentsetting)) {
  4194. $currentsetting[$i] = 'none';
  4195. }
  4196. $selects[] = [
  4197. 'key' => $i,
  4198. 'options' => array_map(function($option) use ($options, $currentsetting, $i) {
  4199. return [
  4200. 'name' => $options[$option],
  4201. 'value' => $option,
  4202. 'selected' => $currentsetting[$i] == $option
  4203. ];
  4204. }, array_keys($options))
  4205. ];
  4206. }
  4207. $context->selects = $selects;
  4208. $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
  4209. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
  4210. }
  4211. }
  4212. /**
  4213. * Special checkbox for frontpage - stores data in course table
  4214. *
  4215. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4216. */
  4217. class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
  4218. /**
  4219. * Returns the current sites name
  4220. *
  4221. * @return string
  4222. */
  4223. public function get_setting() {
  4224. $site = course_get_format(get_site())->get_course();
  4225. return $site->{$this->name};
  4226. }
  4227. /**
  4228. * Save the selected setting
  4229. *
  4230. * @param string $data The selected site
  4231. * @return string empty string or error message
  4232. */
  4233. public function write_setting($data) {
  4234. global $DB, $SITE, $COURSE;
  4235. $record = new stdClass();
  4236. $record->id = $SITE->id;
  4237. $record->{$this->name} = ($data == '1' ? 1 : 0);
  4238. $record->timemodified = time();
  4239. course_get_format($SITE)->update_course_format_options($record);
  4240. $DB->update_record('course', $record);
  4241. // Reset caches.
  4242. $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
  4243. if ($SITE->id == $COURSE->id) {
  4244. $COURSE = $SITE;
  4245. }
  4246. core_courseformat\base::reset_course_cache($SITE->id);
  4247. return '';
  4248. }
  4249. }
  4250. /**
  4251. * Special text for frontpage - stores data in course table.
  4252. * Empty string means not set here. Manual setting is required.
  4253. *
  4254. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4255. */
  4256. class admin_setting_sitesettext extends admin_setting_configtext {
  4257. /**
  4258. * Constructor.
  4259. */
  4260. public function __construct() {
  4261. call_user_func_array(['parent', '__construct'], func_get_args());
  4262. $this->set_force_ltr(false);
  4263. }
  4264. /**
  4265. * Return the current setting
  4266. *
  4267. * @return mixed string or null
  4268. */
  4269. public function get_setting() {
  4270. $site = course_get_format(get_site())->get_course();
  4271. return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
  4272. }
  4273. /**
  4274. * Validate the selected data
  4275. *
  4276. * @param string $data The selected value to validate
  4277. * @return mixed true or message string
  4278. */
  4279. public function validate($data) {
  4280. global $DB, $SITE;
  4281. $cleaned = clean_param($data, PARAM_TEXT);
  4282. if ($cleaned === '') {
  4283. return get_string('required');
  4284. }
  4285. if ($this->name ==='shortname' &&
  4286. $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
  4287. return get_string('shortnametaken', 'error', $data);
  4288. }
  4289. if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
  4290. return true;
  4291. } else {
  4292. return get_string('validateerror', 'admin');
  4293. }
  4294. }
  4295. /**
  4296. * Save the selected setting
  4297. *
  4298. * @param string $data The selected value
  4299. * @return string empty or error message
  4300. */
  4301. public function write_setting($data) {
  4302. global $DB, $SITE, $COURSE;
  4303. $data = trim($data);
  4304. $validated = $this->validate($data);
  4305. if ($validated !== true) {
  4306. return $validated;
  4307. }
  4308. $record = new stdClass();
  4309. $record->id = $SITE->id;
  4310. $record->{$this->name} = $data;
  4311. $record->timemodified = time();
  4312. course_get_format($SITE)->update_course_format_options($record);
  4313. $DB->update_record('course', $record);
  4314. // Reset caches.
  4315. $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
  4316. if ($SITE->id == $COURSE->id) {
  4317. $COURSE = $SITE;
  4318. }
  4319. core_courseformat\base::reset_course_cache($SITE->id);
  4320. return '';
  4321. }
  4322. }
  4323. /**
  4324. * Special text editor for site description.
  4325. *
  4326. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4327. */
  4328. class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
  4329. /**
  4330. * Calls parent::__construct with specific arguments
  4331. */
  4332. public function __construct() {
  4333. parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
  4334. PARAM_RAW, 60, 15);
  4335. }
  4336. /**
  4337. * Return the current setting
  4338. * @return string The current setting
  4339. */
  4340. public function get_setting() {
  4341. $site = course_get_format(get_site())->get_course();
  4342. return $site->{$this->name};
  4343. }
  4344. /**
  4345. * Save the new setting
  4346. *
  4347. * @param string $data The new value to save
  4348. * @return string empty or error message
  4349. */
  4350. public function write_setting($data) {
  4351. global $DB, $SITE, $COURSE;
  4352. $record = new stdClass();
  4353. $record->id = $SITE->id;
  4354. $record->{$this->name} = $data;
  4355. $record->timemodified = time();
  4356. course_get_format($SITE)->update_course_format_options($record);
  4357. $DB->update_record('course', $record);
  4358. // Reset caches.
  4359. $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
  4360. if ($SITE->id == $COURSE->id) {
  4361. $COURSE = $SITE;
  4362. }
  4363. core_courseformat\base::reset_course_cache($SITE->id);
  4364. return '';
  4365. }
  4366. }
  4367. /**
  4368. * Administration interface for emoticon_manager settings.
  4369. *
  4370. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4371. */
  4372. class admin_setting_emoticons extends admin_setting {
  4373. /**
  4374. * Calls parent::__construct with specific args
  4375. */
  4376. public function __construct() {
  4377. global $CFG;
  4378. $manager = get_emoticon_manager();
  4379. $defaults = $this->prepare_form_data($manager->default_emoticons());
  4380. parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
  4381. }
  4382. /**
  4383. * Return the current setting(s)
  4384. *
  4385. * @return array Current settings array
  4386. */
  4387. public function get_setting() {
  4388. global $CFG;
  4389. $manager = get_emoticon_manager();
  4390. $config = $this->config_read($this->name);
  4391. if (is_null($config)) {
  4392. return null;
  4393. }
  4394. $config = $manager->decode_stored_config($config);
  4395. if (is_null($config)) {
  4396. return null;
  4397. }
  4398. return $this->prepare_form_data($config);
  4399. }
  4400. /**
  4401. * Save selected settings
  4402. *
  4403. * @param array $data Array of settings to save
  4404. * @return bool
  4405. */
  4406. public function write_setting($data) {
  4407. $manager = get_emoticon_manager();
  4408. $emoticons = $this->process_form_data($data);
  4409. if ($emoticons === false) {
  4410. return false;
  4411. }
  4412. if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
  4413. return ''; // success
  4414. } else {
  4415. return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
  4416. }
  4417. }
  4418. /**
  4419. * Return XHTML field(s) for options
  4420. *
  4421. * @param array $data Array of options to set in HTML
  4422. * @return string XHTML string for the fields and wrapping div(s)
  4423. */
  4424. public function output_html($data, $query='') {
  4425. global $OUTPUT;
  4426. $context = (object) [
  4427. 'name' => $this->get_full_name(),
  4428. 'emoticons' => [],
  4429. 'forceltr' => true,
  4430. ];
  4431. $i = 0;
  4432. foreach ($data as $field => $value) {
  4433. // When $i == 0: text.
  4434. // When $i == 1: imagename.
  4435. // When $i == 2: imagecomponent.
  4436. // When $i == 3: altidentifier.
  4437. // When $i == 4: altcomponent.
  4438. $fields[$i] = (object) [
  4439. 'field' => $field,
  4440. 'value' => $value,
  4441. 'index' => $i
  4442. ];
  4443. $i++;
  4444. if ($i > 4) {
  4445. $icon = null;
  4446. if (!empty($fields[1]->value)) {
  4447. if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
  4448. $alt = get_string($fields[3]->value, $fields[4]->value);
  4449. } else {
  4450. $alt = $fields[0]->value;
  4451. }
  4452. $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
  4453. }
  4454. $context->emoticons[] = [
  4455. 'fields' => $fields,
  4456. 'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
  4457. ];
  4458. $fields = [];
  4459. $i = 0;
  4460. }
  4461. }
  4462. $context->reseturl = new moodle_url('/admin/resetemoticons.php');
  4463. $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
  4464. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
  4465. }
  4466. /**
  4467. * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
  4468. *
  4469. * @see self::process_form_data()
  4470. * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
  4471. * @return array of form fields and their values
  4472. */
  4473. protected function prepare_form_data(array $emoticons) {
  4474. $form = array();
  4475. $i = 0;
  4476. foreach ($emoticons as $emoticon) {
  4477. $form['text'.$i] = $emoticon->text;
  4478. $form['imagename'.$i] = $emoticon->imagename;
  4479. $form['imagecomponent'.$i] = $emoticon->imagecomponent;
  4480. $form['altidentifier'.$i] = $emoticon->altidentifier;
  4481. $form['altcomponent'.$i] = $emoticon->altcomponent;
  4482. $i++;
  4483. }
  4484. // add one more blank field set for new object
  4485. $form['text'.$i] = '';
  4486. $form['imagename'.$i] = '';
  4487. $form['imagecomponent'.$i] = '';
  4488. $form['altidentifier'.$i] = '';
  4489. $form['altcomponent'.$i] = '';
  4490. return $form;
  4491. }
  4492. /**
  4493. * Converts the data from admin settings form into an array of emoticon objects
  4494. *
  4495. * @see self::prepare_form_data()
  4496. * @param array $data array of admin form fields and values
  4497. * @return false|array of emoticon objects
  4498. */
  4499. protected function process_form_data(array $form) {
  4500. $count = count($form); // number of form field values
  4501. if ($count % 5) {
  4502. // we must get five fields per emoticon object
  4503. return false;
  4504. }
  4505. $emoticons = array();
  4506. for ($i = 0; $i < $count / 5; $i++) {
  4507. $emoticon = new stdClass();
  4508. $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
  4509. $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
  4510. $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
  4511. $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
  4512. $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
  4513. if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
  4514. // prevent from breaking http://url.addresses by accident
  4515. $emoticon->text = '';
  4516. }
  4517. if (strlen($emoticon->text) < 2) {
  4518. // do not allow single character emoticons
  4519. $emoticon->text = '';
  4520. }
  4521. if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
  4522. // emoticon text must contain some non-alphanumeric character to prevent
  4523. // breaking HTML tags
  4524. $emoticon->text = '';
  4525. }
  4526. if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
  4527. $emoticons[] = $emoticon;
  4528. }
  4529. }
  4530. return $emoticons;
  4531. }
  4532. }
  4533. /**
  4534. * Special setting for limiting of the list of available languages.
  4535. *
  4536. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4537. */
  4538. class admin_setting_langlist extends admin_setting_configtext {
  4539. /**
  4540. * Calls parent::__construct with specific arguments
  4541. */
  4542. public function __construct() {
  4543. parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
  4544. }
  4545. /**
  4546. * Validate that each language identifier exists on the site
  4547. *
  4548. * @param string $data
  4549. * @return bool|string True if validation successful, otherwise error string
  4550. */
  4551. public function validate($data) {
  4552. $parentcheck = parent::validate($data);
  4553. if ($parentcheck !== true) {
  4554. return $parentcheck;
  4555. }
  4556. if ($data === '') {
  4557. return true;
  4558. }
  4559. // Normalize language identifiers.
  4560. $langcodes = array_map('trim', explode(',', $data));
  4561. foreach ($langcodes as $langcode) {
  4562. // If the langcode contains optional alias, split it out.
  4563. [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2);
  4564. if (!get_string_manager()->translation_exists($langcode)) {
  4565. return get_string('invalidlanguagecode', 'error', $langcode);
  4566. }
  4567. }
  4568. return true;
  4569. }
  4570. /**
  4571. * Save the new setting
  4572. *
  4573. * @param string $data The new setting
  4574. * @return bool
  4575. */
  4576. public function write_setting($data) {
  4577. $return = parent::write_setting($data);
  4578. get_string_manager()->reset_caches();
  4579. return $return;
  4580. }
  4581. }
  4582. /**
  4583. * Allows to specify comma separated list of known country codes.
  4584. *
  4585. * This is a simple subclass of the plain input text field with added validation so that all the codes are actually
  4586. * known codes.
  4587. *
  4588. * @package core
  4589. * @category admin
  4590. * @copyright 2020 David Mudrák <david@moodle.com>
  4591. * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4592. */
  4593. class admin_setting_countrycodes extends admin_setting_configtext {
  4594. /**
  4595. * Construct the instance of the setting.
  4596. *
  4597. * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'.
  4598. * @param lang_string|string $visiblename Language string with the field label text.
  4599. * @param lang_string|string $description Language string with the field description text.
  4600. * @param string $defaultsetting Default value of the setting.
  4601. * @param int $size Input text field size.
  4602. */
  4603. public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) {
  4604. parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size);
  4605. }
  4606. /**
  4607. * Validate the setting value before storing it.
  4608. *
  4609. * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or
  4610. * a comma separated list of such words.
  4611. *
  4612. * @param string $data Value inserted into the setting field.
  4613. * @return bool|string True if the value is OK, error string otherwise.
  4614. */
  4615. public function validate($data) {
  4616. $parentcheck = parent::validate($data);
  4617. if ($parentcheck !== true) {
  4618. return $parentcheck;
  4619. }
  4620. if ($data === '') {
  4621. return true;
  4622. }
  4623. $allcountries = get_string_manager()->get_list_of_countries(true);
  4624. foreach (explode(',', $data) as $code) {
  4625. if (!isset($allcountries[$code])) {
  4626. return get_string('invalidcountrycode', 'core_error', $code);
  4627. }
  4628. }
  4629. return true;
  4630. }
  4631. }
  4632. /**
  4633. * Selection of one of the recognised countries using the list
  4634. * returned by {@link get_list_of_countries()}.
  4635. *
  4636. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4637. */
  4638. class admin_settings_country_select extends admin_setting_configselect {
  4639. protected $includeall;
  4640. public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
  4641. $this->includeall = $includeall;
  4642. parent::__construct($name, $visiblename, $description, $defaultsetting, null);
  4643. }
  4644. /**
  4645. * Lazy-load the available choices for the select box
  4646. */
  4647. public function load_choices() {
  4648. global $CFG;
  4649. if (is_array($this->choices)) {
  4650. return true;
  4651. }
  4652. $this->choices = array_merge(
  4653. array('0' => get_string('choosedots')),
  4654. get_string_manager()->get_list_of_countries($this->includeall));
  4655. return true;
  4656. }
  4657. }
  4658. /**
  4659. * admin_setting_configselect for the default number of sections in a course,
  4660. * simply so we can lazy-load the choices.
  4661. *
  4662. * @copyright 2011 The Open University
  4663. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4664. */
  4665. class admin_settings_num_course_sections extends admin_setting_configselect {
  4666. public function __construct($name, $visiblename, $description, $defaultsetting) {
  4667. parent::__construct($name, $visiblename, $description, $defaultsetting, array());
  4668. }
  4669. /** Lazy-load the available choices for the select box */
  4670. public function load_choices() {
  4671. $max = get_config('moodlecourse', 'maxsections');
  4672. if (!isset($max) || !is_numeric($max)) {
  4673. $max = 52;
  4674. }
  4675. for ($i = 0; $i <= $max; $i++) {
  4676. $this->choices[$i] = "$i";
  4677. }
  4678. return true;
  4679. }
  4680. }
  4681. /**
  4682. * Course category selection
  4683. *
  4684. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4685. */
  4686. class admin_settings_coursecat_select extends admin_setting_configselect_autocomplete {
  4687. /**
  4688. * Calls parent::__construct with specific arguments
  4689. */
  4690. public function __construct($name, $visiblename, $description, $defaultsetting = 1) {
  4691. parent::__construct($name, $visiblename, $description, $defaultsetting, $choices = null);
  4692. }
  4693. /**
  4694. * Load the available choices for the select box
  4695. *
  4696. * @return bool
  4697. */
  4698. public function load_choices() {
  4699. if (is_array($this->choices)) {
  4700. return true;
  4701. }
  4702. $this->choices = core_course_category::make_categories_list('', 0, ' / ');
  4703. return true;
  4704. }
  4705. }
  4706. /**
  4707. * Special control for selecting days to backup
  4708. *
  4709. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4710. */
  4711. class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
  4712. /**
  4713. * Calls parent::__construct with specific arguments
  4714. */
  4715. public function __construct() {
  4716. parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
  4717. $this->plugin = 'backup';
  4718. }
  4719. /**
  4720. * Load the available choices for the select box
  4721. *
  4722. * @return bool Always returns true
  4723. */
  4724. public function load_choices() {
  4725. if (is_array($this->choices)) {
  4726. return true;
  4727. }
  4728. $this->choices = array();
  4729. $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
  4730. foreach ($days as $day) {
  4731. $this->choices[$day] = get_string($day, 'calendar');
  4732. }
  4733. return true;
  4734. }
  4735. }
  4736. /**
  4737. * Special setting for backup auto destination.
  4738. *
  4739. * @package core
  4740. * @subpackage admin
  4741. * @copyright 2014 Frédéric Massart - FMCorz.net
  4742. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4743. */
  4744. class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
  4745. /**
  4746. * Calls parent::__construct with specific arguments.
  4747. */
  4748. public function __construct() {
  4749. parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
  4750. }
  4751. /**
  4752. * Check if the directory must be set, depending on backup/backup_auto_storage.
  4753. *
  4754. * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
  4755. * there will be conflicts if this validation happens before the other one.
  4756. *
  4757. * @param string $data Form data.
  4758. * @return string Empty when no errors.
  4759. */
  4760. public function write_setting($data) {
  4761. $storage = (int) get_config('backup', 'backup_auto_storage');
  4762. if ($storage !== 0) {
  4763. if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
  4764. // The directory must exist and be writable.
  4765. return get_string('backuperrorinvaliddestination');
  4766. }
  4767. }
  4768. return parent::write_setting($data);
  4769. }
  4770. }
  4771. /**
  4772. * Special debug setting
  4773. *
  4774. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4775. */
  4776. class admin_setting_special_debug extends admin_setting_configselect {
  4777. /**
  4778. * Calls parent::__construct with specific arguments
  4779. */
  4780. public function __construct() {
  4781. parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
  4782. }
  4783. /**
  4784. * Load the available choices for the select box
  4785. *
  4786. * @return bool
  4787. */
  4788. public function load_choices() {
  4789. if (is_array($this->choices)) {
  4790. return true;
  4791. }
  4792. $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
  4793. DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
  4794. DEBUG_NORMAL => get_string('debugnormal', 'admin'),
  4795. DEBUG_ALL => get_string('debugall', 'admin'),
  4796. DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
  4797. return true;
  4798. }
  4799. }
  4800. /**
  4801. * Special admin control
  4802. *
  4803. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4804. */
  4805. class admin_setting_special_calendar_weekend extends admin_setting {
  4806. /**
  4807. * Calls parent::__construct with specific arguments
  4808. */
  4809. public function __construct() {
  4810. $name = 'calendar_weekend';
  4811. $visiblename = get_string('calendar_weekend', 'admin');
  4812. $description = get_string('helpweekenddays', 'admin');
  4813. $default = array ('0', '6'); // Saturdays and Sundays
  4814. parent::__construct($name, $visiblename, $description, $default);
  4815. }
  4816. /**
  4817. * Gets the current settings as an array
  4818. *
  4819. * @return mixed Null if none, else array of settings
  4820. */
  4821. public function get_setting() {
  4822. $result = $this->config_read($this->name);
  4823. if (is_null($result)) {
  4824. return NULL;
  4825. }
  4826. if ($result === '') {
  4827. return array();
  4828. }
  4829. $settings = array();
  4830. for ($i=0; $i<7; $i++) {
  4831. if ($result & (1 << $i)) {
  4832. $settings[] = $i;
  4833. }
  4834. }
  4835. return $settings;
  4836. }
  4837. /**
  4838. * Save the new settings
  4839. *
  4840. * @param array $data Array of new settings
  4841. * @return bool
  4842. */
  4843. public function write_setting($data) {
  4844. if (!is_array($data)) {
  4845. return '';
  4846. }
  4847. unset($data['xxxxx']);
  4848. $result = 0;
  4849. foreach($data as $index) {
  4850. $result |= 1 << $index;
  4851. }
  4852. return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
  4853. }
  4854. /**
  4855. * Return XHTML to display the control
  4856. *
  4857. * @param array $data array of selected days
  4858. * @param string $query
  4859. * @return string XHTML for display (field + wrapping div(s)
  4860. */
  4861. public function output_html($data, $query='') {
  4862. global $OUTPUT;
  4863. // The order matters very much because of the implied numeric keys.
  4864. $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
  4865. $context = (object) [
  4866. 'name' => $this->get_full_name(),
  4867. 'id' => $this->get_id(),
  4868. 'days' => array_map(function($index) use ($days, $data) {
  4869. return [
  4870. 'index' => $index,
  4871. 'label' => get_string($days[$index], 'calendar'),
  4872. 'checked' => in_array($index, $data)
  4873. ];
  4874. }, array_keys($days))
  4875. ];
  4876. $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
  4877. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
  4878. }
  4879. }
  4880. /**
  4881. * Admin setting that allows a user to pick a behaviour.
  4882. *
  4883. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4884. */
  4885. class admin_setting_question_behaviour extends admin_setting_configselect {
  4886. /**
  4887. * @param string $name name of config variable
  4888. * @param string $visiblename display name
  4889. * @param string $description description
  4890. * @param string $default default.
  4891. */
  4892. public function __construct($name, $visiblename, $description, $default) {
  4893. parent::__construct($name, $visiblename, $description, $default, null);
  4894. }
  4895. /**
  4896. * Load list of behaviours as choices
  4897. * @return bool true => success, false => error.
  4898. */
  4899. public function load_choices() {
  4900. global $CFG;
  4901. require_once($CFG->dirroot . '/question/engine/lib.php');
  4902. $this->choices = question_engine::get_behaviour_options('');
  4903. return true;
  4904. }
  4905. }
  4906. /**
  4907. * Admin setting that allows a user to pick appropriate roles for something.
  4908. *
  4909. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4910. */
  4911. class admin_setting_pickroles extends admin_setting_configmulticheckbox {
  4912. /** @var array Array of capabilities which identify roles */
  4913. private $types;
  4914. /**
  4915. * @param string $name Name of config variable
  4916. * @param string $visiblename Display name
  4917. * @param string $description Description
  4918. * @param array $types Array of archetypes which identify
  4919. * roles that will be enabled by default.
  4920. */
  4921. public function __construct($name, $visiblename, $description, $types) {
  4922. parent::__construct($name, $visiblename, $description, NULL, NULL);
  4923. $this->types = $types;
  4924. }
  4925. /**
  4926. * Load roles as choices
  4927. *
  4928. * @return bool true=>success, false=>error
  4929. */
  4930. public function load_choices() {
  4931. global $CFG, $DB;
  4932. if (during_initial_install()) {
  4933. return false;
  4934. }
  4935. if (is_array($this->choices)) {
  4936. return true;
  4937. }
  4938. if ($roles = get_all_roles()) {
  4939. $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
  4940. return true;
  4941. } else {
  4942. return false;
  4943. }
  4944. }
  4945. /**
  4946. * Return the default setting for this control
  4947. *
  4948. * @return array Array of default settings
  4949. */
  4950. public function get_defaultsetting() {
  4951. global $CFG;
  4952. if (during_initial_install()) {
  4953. return null;
  4954. }
  4955. $result = array();
  4956. foreach($this->types as $archetype) {
  4957. if ($caproles = get_archetype_roles($archetype)) {
  4958. foreach ($caproles as $caprole) {
  4959. $result[$caprole->id] = 1;
  4960. }
  4961. }
  4962. }
  4963. return $result;
  4964. }
  4965. }
  4966. /**
  4967. * Admin setting that is a list of installed filter plugins.
  4968. *
  4969. * @copyright 2015 The Open University
  4970. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4971. */
  4972. class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
  4973. /**
  4974. * Constructor
  4975. *
  4976. * @param string $name unique ascii name, either 'mysetting' for settings
  4977. * that in config, or 'myplugin/mysetting' for ones in config_plugins.
  4978. * @param string $visiblename localised name
  4979. * @param string $description localised long description
  4980. * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
  4981. */
  4982. public function __construct($name, $visiblename, $description, $default) {
  4983. if (empty($default)) {
  4984. $default = array();
  4985. }
  4986. $this->load_choices();
  4987. foreach ($default as $plugin) {
  4988. if (!isset($this->choices[$plugin])) {
  4989. unset($default[$plugin]);
  4990. }
  4991. }
  4992. parent::__construct($name, $visiblename, $description, $default, null);
  4993. }
  4994. public function load_choices() {
  4995. if (is_array($this->choices)) {
  4996. return true;
  4997. }
  4998. $this->choices = array();
  4999. foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
  5000. $this->choices[$plugin] = filter_get_name($plugin);
  5001. }
  5002. return true;
  5003. }
  5004. }
  5005. /**
  5006. * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
  5007. *
  5008. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5009. */
  5010. class admin_setting_configtext_with_advanced extends admin_setting_configtext {
  5011. /**
  5012. * Constructor
  5013. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  5014. * @param string $visiblename localised
  5015. * @param string $description long localised info
  5016. * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
  5017. * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
  5018. * @param int $size default field size
  5019. */
  5020. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
  5021. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
  5022. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  5023. }
  5024. }
  5025. /**
  5026. * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
  5027. *
  5028. * @copyright 2009 Petr Skoda (http://skodak.org)
  5029. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5030. */
  5031. class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
  5032. /**
  5033. * Constructor
  5034. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  5035. * @param string $visiblename localised
  5036. * @param string $description long localised info
  5037. * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
  5038. * @param string $yes value used when checked
  5039. * @param string $no value used when not checked
  5040. */
  5041. public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
  5042. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
  5043. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  5044. }
  5045. }
  5046. /**
  5047. * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
  5048. *
  5049. * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
  5050. *
  5051. * @copyright 2010 Sam Hemelryk
  5052. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5053. */
  5054. class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
  5055. /**
  5056. * Constructor
  5057. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  5058. * @param string $visiblename localised
  5059. * @param string $description long localised info
  5060. * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
  5061. * @param string $yes value used when checked
  5062. * @param string $no value used when not checked
  5063. */
  5064. public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
  5065. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
  5066. $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
  5067. }
  5068. }
  5069. /**
  5070. * Autocomplete as you type form element.
  5071. *
  5072. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5073. */
  5074. class admin_setting_configselect_autocomplete extends admin_setting_configselect {
  5075. /** @var boolean $tags Should we allow typing new entries to the field? */
  5076. protected $tags = false;
  5077. /** @var string $ajax Name of an AMD module to send/process ajax requests. */
  5078. protected $ajax = '';
  5079. /** @var string $placeholder Placeholder text for an empty list. */
  5080. protected $placeholder = '';
  5081. /** @var bool $casesensitive Whether the search has to be case-sensitive. */
  5082. protected $casesensitive = false;
  5083. /** @var bool $showsuggestions Show suggestions by default - but this can be turned off. */
  5084. protected $showsuggestions = true;
  5085. /** @var string $noselectionstring String that is shown when there are no selections. */
  5086. protected $noselectionstring = '';
  5087. /**
  5088. * Returns XHTML select field and wrapping div(s)
  5089. *
  5090. * @see output_select_html()
  5091. *
  5092. * @param string $data the option to show as selected
  5093. * @param string $query
  5094. * @return string XHTML field and wrapping div
  5095. */
  5096. public function output_html($data, $query='') {
  5097. global $PAGE;
  5098. $html = parent::output_html($data, $query);
  5099. if ($html === '') {
  5100. return $html;
  5101. }
  5102. $this->placeholder = get_string('search');
  5103. $params = array('#' . $this->get_id(), $this->tags, $this->ajax,
  5104. $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring);
  5105. // Load autocomplete wrapper for select2 library.
  5106. $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params);
  5107. return $html;
  5108. }
  5109. }
  5110. /**
  5111. * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
  5112. *
  5113. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5114. */
  5115. class admin_setting_configselect_with_advanced extends admin_setting_configselect {
  5116. /**
  5117. * Calls parent::__construct with specific arguments
  5118. */
  5119. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  5120. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
  5121. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  5122. }
  5123. }
  5124. /**
  5125. * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
  5126. *
  5127. * @copyright 2017 Marina Glancy
  5128. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5129. */
  5130. class admin_setting_configselect_with_lock extends admin_setting_configselect {
  5131. /**
  5132. * Constructor
  5133. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  5134. * or 'myplugin/mysetting' for ones in config_plugins.
  5135. * @param string $visiblename localised
  5136. * @param string $description long localised info
  5137. * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
  5138. * @param array $choices array of $value=>$label for each selection
  5139. */
  5140. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  5141. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
  5142. $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
  5143. }
  5144. }
  5145. /**
  5146. * Graded roles in gradebook
  5147. *
  5148. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5149. */
  5150. class admin_setting_special_gradebookroles extends admin_setting_pickroles {
  5151. /**
  5152. * Calls parent::__construct with specific arguments
  5153. */
  5154. public function __construct() {
  5155. parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
  5156. get_string('configgradebookroles', 'admin'),
  5157. array('student'));
  5158. }
  5159. }
  5160. /**
  5161. *
  5162. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5163. */
  5164. class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
  5165. /**
  5166. * Saves the new settings passed in $data
  5167. *
  5168. * @param string $data
  5169. * @return mixed string or Array
  5170. */
  5171. public function write_setting($data) {
  5172. global $CFG, $DB;
  5173. $oldvalue = $this->config_read($this->name);
  5174. $return = parent::write_setting($data);
  5175. $newvalue = $this->config_read($this->name);
  5176. if ($oldvalue !== $newvalue) {
  5177. // force full regrading
  5178. $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
  5179. }
  5180. return $return;
  5181. }
  5182. }
  5183. /**
  5184. * Which roles to show on course description page
  5185. *
  5186. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5187. */
  5188. class admin_setting_special_coursecontact extends admin_setting_pickroles {
  5189. /**
  5190. * Calls parent::__construct with specific arguments
  5191. */
  5192. public function __construct() {
  5193. parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
  5194. get_string('coursecontact_desc', 'admin'),
  5195. array('editingteacher'));
  5196. $this->set_updatedcallback(function (){
  5197. cache::make('core', 'coursecontacts')->purge();
  5198. });
  5199. }
  5200. }
  5201. /**
  5202. *
  5203. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5204. */
  5205. class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
  5206. /**
  5207. * Calls parent::__construct with specific arguments
  5208. */
  5209. public function __construct() {
  5210. parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
  5211. get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
  5212. }
  5213. /**
  5214. * Old syntax of class constructor. Deprecated in PHP7.
  5215. *
  5216. * @deprecated since Moodle 3.1
  5217. */
  5218. public function admin_setting_special_gradelimiting() {
  5219. debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
  5220. self::__construct();
  5221. }
  5222. /**
  5223. * Force site regrading
  5224. */
  5225. function regrade_all() {
  5226. global $CFG;
  5227. require_once("$CFG->libdir/gradelib.php");
  5228. grade_force_site_regrading();
  5229. }
  5230. /**
  5231. * Saves the new settings
  5232. *
  5233. * @param mixed $data
  5234. * @return string empty string or error message
  5235. */
  5236. function write_setting($data) {
  5237. $previous = $this->get_setting();
  5238. if ($previous === null) {
  5239. if ($data) {
  5240. $this->regrade_all();
  5241. }
  5242. } else {
  5243. if ($data != $previous) {
  5244. $this->regrade_all();
  5245. }
  5246. }
  5247. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  5248. }
  5249. }
  5250. /**
  5251. * Special setting for $CFG->grade_minmaxtouse.
  5252. *
  5253. * @package core
  5254. * @copyright 2015 Frédéric Massart - FMCorz.net
  5255. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5256. */
  5257. class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
  5258. /**
  5259. * Constructor.
  5260. */
  5261. public function __construct() {
  5262. parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
  5263. new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
  5264. array(
  5265. GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
  5266. GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
  5267. )
  5268. );
  5269. }
  5270. /**
  5271. * Saves the new setting.
  5272. *
  5273. * @param mixed $data
  5274. * @return string empty string or error message
  5275. */
  5276. function write_setting($data) {
  5277. global $CFG;
  5278. $previous = $this->get_setting();
  5279. $result = parent::write_setting($data);
  5280. // If saved and the value has changed.
  5281. if (empty($result) && $previous != $data) {
  5282. require_once($CFG->libdir . '/gradelib.php');
  5283. grade_force_site_regrading();
  5284. }
  5285. return $result;
  5286. }
  5287. }
  5288. /**
  5289. * Primary grade export plugin - has state tracking.
  5290. *
  5291. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5292. */
  5293. class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
  5294. /**
  5295. * Calls parent::__construct with specific arguments
  5296. */
  5297. public function __construct() {
  5298. parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
  5299. get_string('configgradeexport', 'admin'), array(), NULL);
  5300. }
  5301. /**
  5302. * Load the available choices for the multicheckbox
  5303. *
  5304. * @return bool always returns true
  5305. */
  5306. public function load_choices() {
  5307. if (is_array($this->choices)) {
  5308. return true;
  5309. }
  5310. $this->choices = array();
  5311. if ($plugins = core_component::get_plugin_list('gradeexport')) {
  5312. foreach($plugins as $plugin => $unused) {
  5313. $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
  5314. }
  5315. }
  5316. return true;
  5317. }
  5318. }
  5319. /**
  5320. * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
  5321. *
  5322. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5323. */
  5324. class admin_setting_special_gradepointdefault extends admin_setting_configtext {
  5325. /**
  5326. * Config gradepointmax constructor
  5327. *
  5328. * @param string $name Overidden by "gradepointmax"
  5329. * @param string $visiblename Overridden by "gradepointmax" language string.
  5330. * @param string $description Overridden by "gradepointmax_help" language string.
  5331. * @param string $defaultsetting Not used, overridden by 100.
  5332. * @param mixed $paramtype Overridden by PARAM_INT.
  5333. * @param int $size Overridden by 5.
  5334. */
  5335. public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
  5336. $name = 'gradepointdefault';
  5337. $visiblename = get_string('gradepointdefault', 'grades');
  5338. $description = get_string('gradepointdefault_help', 'grades');
  5339. $defaultsetting = 100;
  5340. $paramtype = PARAM_INT;
  5341. $size = 5;
  5342. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
  5343. }
  5344. /**
  5345. * Validate data before storage
  5346. * @param string $data The submitted data
  5347. * @return bool|string true if ok, string if error found
  5348. */
  5349. public function validate($data) {
  5350. global $CFG;
  5351. if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
  5352. return true;
  5353. } else {
  5354. return get_string('gradepointdefault_validateerror', 'grades');
  5355. }
  5356. }
  5357. }
  5358. /**
  5359. * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
  5360. *
  5361. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5362. */
  5363. class admin_setting_special_gradepointmax extends admin_setting_configtext {
  5364. /**
  5365. * Config gradepointmax constructor
  5366. *
  5367. * @param string $name Overidden by "gradepointmax"
  5368. * @param string $visiblename Overridden by "gradepointmax" language string.
  5369. * @param string $description Overridden by "gradepointmax_help" language string.
  5370. * @param string $defaultsetting Not used, overridden by 100.
  5371. * @param mixed $paramtype Overridden by PARAM_INT.
  5372. * @param int $size Overridden by 5.
  5373. */
  5374. public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
  5375. $name = 'gradepointmax';
  5376. $visiblename = get_string('gradepointmax', 'grades');
  5377. $description = get_string('gradepointmax_help', 'grades');
  5378. $defaultsetting = 100;
  5379. $paramtype = PARAM_INT;
  5380. $size = 5;
  5381. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
  5382. }
  5383. /**
  5384. * Save the selected setting
  5385. *
  5386. * @param string $data The selected site
  5387. * @return string empty string or error message
  5388. */
  5389. public function write_setting($data) {
  5390. if ($data === '') {
  5391. $data = (int)$this->defaultsetting;
  5392. } else {
  5393. $data = $data;
  5394. }
  5395. return parent::write_setting($data);
  5396. }
  5397. /**
  5398. * Validate data before storage
  5399. * @param string $data The submitted data
  5400. * @return bool|string true if ok, string if error found
  5401. */
  5402. public function validate($data) {
  5403. if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
  5404. return true;
  5405. } else {
  5406. return get_string('gradepointmax_validateerror', 'grades');
  5407. }
  5408. }
  5409. /**
  5410. * Return an XHTML string for the setting
  5411. * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
  5412. * @param string $query search query to be highlighted
  5413. * @return string XHTML to display control
  5414. */
  5415. public function output_html($data, $query = '') {
  5416. global $OUTPUT;
  5417. $default = $this->get_defaultsetting();
  5418. $context = (object) [
  5419. 'size' => $this->size,
  5420. 'id' => $this->get_id(),
  5421. 'name' => $this->get_full_name(),
  5422. 'value' => $data,
  5423. 'attributes' => [
  5424. 'maxlength' => 5
  5425. ],
  5426. 'forceltr' => $this->get_force_ltr()
  5427. ];
  5428. $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
  5429. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  5430. }
  5431. }
  5432. /**
  5433. * Grade category settings
  5434. *
  5435. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5436. */
  5437. class admin_setting_gradecat_combo extends admin_setting {
  5438. /** @var array Array of choices */
  5439. public $choices;
  5440. /**
  5441. * Sets choices and calls parent::__construct with passed arguments
  5442. * @param string $name
  5443. * @param string $visiblename
  5444. * @param string $description
  5445. * @param mixed $defaultsetting string or array depending on implementation
  5446. * @param array $choices An array of choices for the control
  5447. */
  5448. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  5449. $this->choices = $choices;
  5450. parent::__construct($name, $visiblename, $description, $defaultsetting);
  5451. }
  5452. /**
  5453. * Return the current setting(s) array
  5454. *
  5455. * @return array Array of value=>xx, forced=>xx, adv=>xx
  5456. */
  5457. public function get_setting() {
  5458. global $CFG;
  5459. $value = $this->config_read($this->name);
  5460. $flag = $this->config_read($this->name.'_flag');
  5461. if (is_null($value) or is_null($flag)) {
  5462. return NULL;
  5463. }
  5464. $flag = (int)$flag;
  5465. $forced = (boolean)(1 & $flag); // first bit
  5466. $adv = (boolean)(2 & $flag); // second bit
  5467. return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
  5468. }
  5469. /**
  5470. * Save the new settings passed in $data
  5471. *
  5472. * @todo Add vartype handling to ensure $data is array
  5473. * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
  5474. * @return string empty or error message
  5475. */
  5476. public function write_setting($data) {
  5477. global $CFG;
  5478. $value = $data['value'];
  5479. $forced = empty($data['forced']) ? 0 : 1;
  5480. $adv = empty($data['adv']) ? 0 : 2;
  5481. $flag = ($forced | $adv); //bitwise or
  5482. if (!in_array($value, array_keys($this->choices))) {
  5483. return 'Error setting ';
  5484. }
  5485. $oldvalue = $this->config_read($this->name);
  5486. $oldflag = (int)$this->config_read($this->name.'_flag');
  5487. $oldforced = (1 & $oldflag); // first bit
  5488. $result1 = $this->config_write($this->name, $value);
  5489. $result2 = $this->config_write($this->name.'_flag', $flag);
  5490. // force regrade if needed
  5491. if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
  5492. require_once($CFG->libdir.'/gradelib.php');
  5493. grade_category::updated_forced_settings();
  5494. }
  5495. if ($result1 and $result2) {
  5496. return '';
  5497. } else {
  5498. return get_string('errorsetting', 'admin');
  5499. }
  5500. }
  5501. /**
  5502. * Return XHTML to display the field and wrapping div
  5503. *
  5504. * @todo Add vartype handling to ensure $data is array
  5505. * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
  5506. * @param string $query
  5507. * @return string XHTML to display control
  5508. */
  5509. public function output_html($data, $query='') {
  5510. global $OUTPUT;
  5511. $value = $data['value'];
  5512. $default = $this->get_defaultsetting();
  5513. if (!is_null($default)) {
  5514. $defaultinfo = array();
  5515. if (isset($this->choices[$default['value']])) {
  5516. $defaultinfo[] = $this->choices[$default['value']];
  5517. }
  5518. if (!empty($default['forced'])) {
  5519. $defaultinfo[] = get_string('force');
  5520. }
  5521. if (!empty($default['adv'])) {
  5522. $defaultinfo[] = get_string('advanced');
  5523. }
  5524. $defaultinfo = implode(', ', $defaultinfo);
  5525. } else {
  5526. $defaultinfo = NULL;
  5527. }
  5528. $options = $this->choices;
  5529. $context = (object) [
  5530. 'id' => $this->get_id(),
  5531. 'name' => $this->get_full_name(),
  5532. 'forced' => !empty($data['forced']),
  5533. 'advanced' => !empty($data['adv']),
  5534. 'options' => array_map(function($option) use ($options, $value) {
  5535. return [
  5536. 'value' => $option,
  5537. 'name' => $options[$option],
  5538. 'selected' => $option == $value
  5539. ];
  5540. }, array_keys($options)),
  5541. ];
  5542. $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
  5543. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
  5544. }
  5545. }
  5546. /**
  5547. * Selection of grade report in user profiles
  5548. *
  5549. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5550. */
  5551. class admin_setting_grade_profilereport extends admin_setting_configselect {
  5552. /**
  5553. * Calls parent::__construct with specific arguments
  5554. */
  5555. public function __construct() {
  5556. parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
  5557. }
  5558. /**
  5559. * Loads an array of choices for the configselect control
  5560. *
  5561. * @return bool always return true
  5562. */
  5563. public function load_choices() {
  5564. if (is_array($this->choices)) {
  5565. return true;
  5566. }
  5567. $this->choices = array();
  5568. global $CFG;
  5569. require_once($CFG->libdir.'/gradelib.php');
  5570. foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
  5571. if (file_exists($plugindir.'/lib.php')) {
  5572. require_once($plugindir.'/lib.php');
  5573. $functionname = 'grade_report_'.$plugin.'_profilereport';
  5574. if (function_exists($functionname)) {
  5575. $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
  5576. }
  5577. }
  5578. }
  5579. return true;
  5580. }
  5581. }
  5582. /**
  5583. * Provides a selection of grade reports to be used for "grades".
  5584. *
  5585. * @copyright 2015 Adrian Greeve <adrian@moodle.com>
  5586. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5587. */
  5588. class admin_setting_my_grades_report extends admin_setting_configselect {
  5589. /**
  5590. * Calls parent::__construct with specific arguments.
  5591. */
  5592. public function __construct() {
  5593. parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
  5594. new lang_string('mygrades_desc', 'grades'), 'overview', null);
  5595. }
  5596. /**
  5597. * Loads an array of choices for the configselect control.
  5598. *
  5599. * @return bool always returns true.
  5600. */
  5601. public function load_choices() {
  5602. global $CFG; // Remove this line and behold the horror of behat test failures!
  5603. $this->choices = array();
  5604. foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
  5605. if (file_exists($plugindir . '/lib.php')) {
  5606. require_once($plugindir . '/lib.php');
  5607. // Check to see if the class exists. Check the correct plugin convention first.
  5608. if (class_exists('gradereport_' . $plugin)) {
  5609. $classname = 'gradereport_' . $plugin;
  5610. } else if (class_exists('grade_report_' . $plugin)) {
  5611. // We are using the old plugin naming convention.
  5612. $classname = 'grade_report_' . $plugin;
  5613. } else {
  5614. continue;
  5615. }
  5616. if ($classname::supports_mygrades()) {
  5617. $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
  5618. }
  5619. }
  5620. }
  5621. // Add an option to specify an external url.
  5622. $this->choices['external'] = get_string('externalurl', 'grades');
  5623. return true;
  5624. }
  5625. }
  5626. /**
  5627. * Special class for register auth selection
  5628. *
  5629. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5630. */
  5631. class admin_setting_special_registerauth extends admin_setting_configselect {
  5632. /**
  5633. * Calls parent::__construct with specific arguments
  5634. */
  5635. public function __construct() {
  5636. parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
  5637. }
  5638. /**
  5639. * Returns the default option
  5640. *
  5641. * @return string empty or default option
  5642. */
  5643. public function get_defaultsetting() {
  5644. $this->load_choices();
  5645. $defaultsetting = parent::get_defaultsetting();
  5646. if (array_key_exists($defaultsetting, $this->choices)) {
  5647. return $defaultsetting;
  5648. } else {
  5649. return '';
  5650. }
  5651. }
  5652. /**
  5653. * Loads the possible choices for the array
  5654. *
  5655. * @return bool always returns true
  5656. */
  5657. public function load_choices() {
  5658. global $CFG;
  5659. if (is_array($this->choices)) {
  5660. return true;
  5661. }
  5662. $this->choices = array();
  5663. $this->choices[''] = get_string('disable');
  5664. $authsenabled = get_enabled_auth_plugins();
  5665. foreach ($authsenabled as $auth) {
  5666. $authplugin = get_auth_plugin($auth);
  5667. if (!$authplugin->can_signup()) {
  5668. continue;
  5669. }
  5670. // Get the auth title (from core or own auth lang files)
  5671. $authtitle = $authplugin->get_title();
  5672. $this->choices[$auth] = $authtitle;
  5673. }
  5674. return true;
  5675. }
  5676. }
  5677. /**
  5678. * General plugins manager
  5679. */
  5680. class admin_page_pluginsoverview extends admin_externalpage {
  5681. /**
  5682. * Sets basic information about the external page
  5683. */
  5684. public function __construct() {
  5685. global $CFG;
  5686. parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
  5687. "$CFG->wwwroot/$CFG->admin/plugins.php");
  5688. }
  5689. }
  5690. /**
  5691. * Module manage page
  5692. *
  5693. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5694. */
  5695. class admin_page_managemods extends admin_externalpage {
  5696. /**
  5697. * Calls parent::__construct with specific arguments
  5698. */
  5699. public function __construct() {
  5700. global $CFG;
  5701. parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
  5702. }
  5703. /**
  5704. * Try to find the specified module
  5705. *
  5706. * @param string $query The module to search for
  5707. * @return array
  5708. */
  5709. public function search($query) {
  5710. global $CFG, $DB;
  5711. if ($result = parent::search($query)) {
  5712. return $result;
  5713. }
  5714. $found = false;
  5715. if ($modules = $DB->get_records('modules')) {
  5716. foreach ($modules as $module) {
  5717. if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
  5718. continue;
  5719. }
  5720. if (strpos($module->name, $query) !== false) {
  5721. $found = true;
  5722. break;
  5723. }
  5724. $strmodulename = get_string('modulename', $module->name);
  5725. if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
  5726. $found = true;
  5727. break;
  5728. }
  5729. }
  5730. }
  5731. if ($found) {
  5732. $result = new stdClass();
  5733. $result->page = $this;
  5734. $result->settings = array();
  5735. return array($this->name => $result);
  5736. } else {
  5737. return array();
  5738. }
  5739. }
  5740. }
  5741. /**
  5742. * Special class for enrol plugins management.
  5743. *
  5744. * @copyright 2010 Petr Skoda {@link http://skodak.org}
  5745. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5746. */
  5747. class admin_setting_manageenrols extends admin_setting {
  5748. /**
  5749. * Calls parent::__construct with specific arguments
  5750. */
  5751. public function __construct() {
  5752. $this->nosave = true;
  5753. parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
  5754. }
  5755. /**
  5756. * Always returns true, does nothing
  5757. *
  5758. * @return true
  5759. */
  5760. public function get_setting() {
  5761. return true;
  5762. }
  5763. /**
  5764. * Always returns true, does nothing
  5765. *
  5766. * @return true
  5767. */
  5768. public function get_defaultsetting() {
  5769. return true;
  5770. }
  5771. /**
  5772. * Always returns '', does not write anything
  5773. *
  5774. * @return string Always returns ''
  5775. */
  5776. public function write_setting($data) {
  5777. // do not write any setting
  5778. return '';
  5779. }
  5780. /**
  5781. * Checks if $query is one of the available enrol plugins
  5782. *
  5783. * @param string $query The string to search for
  5784. * @return bool Returns true if found, false if not
  5785. */
  5786. public function is_related($query) {
  5787. if (parent::is_related($query)) {
  5788. return true;
  5789. }
  5790. $query = core_text::strtolower($query);
  5791. $enrols = enrol_get_plugins(false);
  5792. foreach ($enrols as $name=>$enrol) {
  5793. $localised = get_string('pluginname', 'enrol_'.$name);
  5794. if (strpos(core_text::strtolower($name), $query) !== false) {
  5795. return true;
  5796. }
  5797. if (strpos(core_text::strtolower($localised), $query) !== false) {
  5798. return true;
  5799. }
  5800. }
  5801. return false;
  5802. }
  5803. /**
  5804. * Builds the XHTML to display the control
  5805. *
  5806. * @param string $data Unused
  5807. * @param string $query
  5808. * @return string
  5809. */
  5810. public function output_html($data, $query='') {
  5811. global $CFG, $OUTPUT, $DB, $PAGE;
  5812. // Display strings.
  5813. $strup = get_string('up');
  5814. $strdown = get_string('down');
  5815. $strsettings = get_string('settings');
  5816. $strenable = get_string('enable');
  5817. $strdisable = get_string('disable');
  5818. $struninstall = get_string('uninstallplugin', 'core_admin');
  5819. $strusage = get_string('enrolusage', 'enrol');
  5820. $strversion = get_string('version');
  5821. $strtest = get_string('testsettings', 'core_enrol');
  5822. $pluginmanager = core_plugin_manager::instance();
  5823. $enrols_available = enrol_get_plugins(false);
  5824. $active_enrols = enrol_get_plugins(true);
  5825. $allenrols = array();
  5826. foreach ($active_enrols as $key=>$enrol) {
  5827. $allenrols[$key] = true;
  5828. }
  5829. foreach ($enrols_available as $key=>$enrol) {
  5830. $allenrols[$key] = true;
  5831. }
  5832. // Now find all borked plugins and at least allow then to uninstall.
  5833. $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
  5834. foreach ($condidates as $candidate) {
  5835. if (empty($allenrols[$candidate])) {
  5836. $allenrols[$candidate] = true;
  5837. }
  5838. }
  5839. $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
  5840. $return .= $OUTPUT->box_start('generalbox enrolsui');
  5841. $table = new html_table();
  5842. $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
  5843. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  5844. $table->id = 'courseenrolmentplugins';
  5845. $table->attributes['class'] = 'admintable generaltable';
  5846. $table->data = array();
  5847. // Iterate through enrol plugins and add to the display table.
  5848. $updowncount = 1;
  5849. $enrolcount = count($active_enrols);
  5850. $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
  5851. $printed = array();
  5852. foreach($allenrols as $enrol => $unused) {
  5853. $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
  5854. $version = get_config('enrol_'.$enrol, 'version');
  5855. if ($version === false) {
  5856. $version = '';
  5857. }
  5858. if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
  5859. $name = get_string('pluginname', 'enrol_'.$enrol);
  5860. } else {
  5861. $name = $enrol;
  5862. }
  5863. // Usage.
  5864. $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
  5865. $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
  5866. $usage = "$ci / $cp";
  5867. // Hide/show links.
  5868. $class = '';
  5869. if (isset($active_enrols[$enrol])) {
  5870. $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
  5871. $hideshow = "<a href=\"$aurl\">";
  5872. $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
  5873. $enabled = true;
  5874. $displayname = $name;
  5875. } else if (isset($enrols_available[$enrol])) {
  5876. $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
  5877. $hideshow = "<a href=\"$aurl\">";
  5878. $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
  5879. $enabled = false;
  5880. $displayname = $name;
  5881. $class = 'dimmed_text';
  5882. } else {
  5883. $hideshow = '';
  5884. $enabled = false;
  5885. $displayname = '<span class="notifyproblem">'.$name.'</span>';
  5886. }
  5887. if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
  5888. $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
  5889. } else {
  5890. $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
  5891. }
  5892. // Up/down link (only if enrol is enabled).
  5893. $updown = '';
  5894. if ($enabled) {
  5895. if ($updowncount > 1) {
  5896. $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
  5897. $updown .= "<a href=\"$aurl\">";
  5898. $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
  5899. } else {
  5900. $updown .= $OUTPUT->spacer() . '&nbsp;';
  5901. }
  5902. if ($updowncount < $enrolcount) {
  5903. $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
  5904. $updown .= "<a href=\"$aurl\">";
  5905. $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
  5906. } else {
  5907. $updown .= $OUTPUT->spacer() . '&nbsp;';
  5908. }
  5909. ++$updowncount;
  5910. }
  5911. // Add settings link.
  5912. if (!$version) {
  5913. $settings = '';
  5914. } else if ($surl = $plugininfo->get_settings_url()) {
  5915. $settings = html_writer::link($surl, $strsettings);
  5916. } else {
  5917. $settings = '';
  5918. }
  5919. // Add uninstall info.
  5920. $uninstall = '';
  5921. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
  5922. $uninstall = html_writer::link($uninstallurl, $struninstall);
  5923. }
  5924. $test = '';
  5925. if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
  5926. $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey()));
  5927. $test = html_writer::link($testsettingsurl, $strtest);
  5928. }
  5929. // Add a row to the table.
  5930. $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
  5931. if ($class) {
  5932. $row->attributes['class'] = $class;
  5933. }
  5934. $table->data[] = $row;
  5935. $printed[$enrol] = true;
  5936. }
  5937. $return .= html_writer::table($table);
  5938. $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
  5939. $return .= $OUTPUT->box_end();
  5940. return highlight($query, $return);
  5941. }
  5942. }
  5943. /**
  5944. * Blocks manage page
  5945. *
  5946. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5947. */
  5948. class admin_page_manageblocks extends admin_externalpage {
  5949. /**
  5950. * Calls parent::__construct with specific arguments
  5951. */
  5952. public function __construct() {
  5953. global $CFG;
  5954. parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
  5955. }
  5956. /**
  5957. * Search for a specific block
  5958. *
  5959. * @param string $query The string to search for
  5960. * @return array
  5961. */
  5962. public function search($query) {
  5963. global $CFG, $DB;
  5964. if ($result = parent::search($query)) {
  5965. return $result;
  5966. }
  5967. $found = false;
  5968. if ($blocks = $DB->get_records('block')) {
  5969. foreach ($blocks as $block) {
  5970. if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
  5971. continue;
  5972. }
  5973. if (strpos($block->name, $query) !== false) {
  5974. $found = true;
  5975. break;
  5976. }
  5977. $strblockname = get_string('pluginname', 'block_'.$block->name);
  5978. if (strpos(core_text::strtolower($strblockname), $query) !== false) {
  5979. $found = true;
  5980. break;
  5981. }
  5982. }
  5983. }
  5984. if ($found) {
  5985. $result = new stdClass();
  5986. $result->page = $this;
  5987. $result->settings = array();
  5988. return array($this->name => $result);
  5989. } else {
  5990. return array();
  5991. }
  5992. }
  5993. }
  5994. /**
  5995. * Message outputs configuration
  5996. *
  5997. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5998. */
  5999. class admin_page_managemessageoutputs extends admin_externalpage {
  6000. /**
  6001. * Calls parent::__construct with specific arguments
  6002. */
  6003. public function __construct() {
  6004. global $CFG;
  6005. parent::__construct('managemessageoutputs',
  6006. get_string('defaultmessageoutputs', 'message'),
  6007. new moodle_url('/admin/message.php')
  6008. );
  6009. }
  6010. /**
  6011. * Search for a specific message processor
  6012. *
  6013. * @param string $query The string to search for
  6014. * @return array
  6015. */
  6016. public function search($query) {
  6017. global $CFG, $DB;
  6018. if ($result = parent::search($query)) {
  6019. return $result;
  6020. }
  6021. $found = false;
  6022. if ($processors = get_message_processors()) {
  6023. foreach ($processors as $processor) {
  6024. if (!$processor->available) {
  6025. continue;
  6026. }
  6027. if (strpos($processor->name, $query) !== false) {
  6028. $found = true;
  6029. break;
  6030. }
  6031. $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
  6032. if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
  6033. $found = true;
  6034. break;
  6035. }
  6036. }
  6037. }
  6038. if ($found) {
  6039. $result = new stdClass();
  6040. $result->page = $this;
  6041. $result->settings = array();
  6042. return array($this->name => $result);
  6043. } else {
  6044. return array();
  6045. }
  6046. }
  6047. }
  6048. /**
  6049. * Manage question behaviours page
  6050. *
  6051. * @copyright 2011 The Open University
  6052. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6053. */
  6054. class admin_page_manageqbehaviours extends admin_externalpage {
  6055. /**
  6056. * Constructor
  6057. */
  6058. public function __construct() {
  6059. global $CFG;
  6060. parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
  6061. new moodle_url('/admin/qbehaviours.php'));
  6062. }
  6063. /**
  6064. * Search question behaviours for the specified string
  6065. *
  6066. * @param string $query The string to search for in question behaviours
  6067. * @return array
  6068. */
  6069. public function search($query) {
  6070. global $CFG;
  6071. if ($result = parent::search($query)) {
  6072. return $result;
  6073. }
  6074. $found = false;
  6075. require_once($CFG->dirroot . '/question/engine/lib.php');
  6076. foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
  6077. if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
  6078. $query) !== false) {
  6079. $found = true;
  6080. break;
  6081. }
  6082. }
  6083. if ($found) {
  6084. $result = new stdClass();
  6085. $result->page = $this;
  6086. $result->settings = array();
  6087. return array($this->name => $result);
  6088. } else {
  6089. return array();
  6090. }
  6091. }
  6092. }
  6093. /**
  6094. * Question type manage page
  6095. *
  6096. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6097. */
  6098. class admin_page_manageqtypes extends admin_externalpage {
  6099. /**
  6100. * Calls parent::__construct with specific arguments
  6101. */
  6102. public function __construct() {
  6103. global $CFG;
  6104. parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
  6105. new moodle_url('/admin/qtypes.php'));
  6106. }
  6107. /**
  6108. * Search question types for the specified string
  6109. *
  6110. * @param string $query The string to search for in question types
  6111. * @return array
  6112. */
  6113. public function search($query) {
  6114. global $CFG;
  6115. if ($result = parent::search($query)) {
  6116. return $result;
  6117. }
  6118. $found = false;
  6119. require_once($CFG->dirroot . '/question/engine/bank.php');
  6120. foreach (question_bank::get_all_qtypes() as $qtype) {
  6121. if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
  6122. $found = true;
  6123. break;
  6124. }
  6125. }
  6126. if ($found) {
  6127. $result = new stdClass();
  6128. $result->page = $this;
  6129. $result->settings = array();
  6130. return array($this->name => $result);
  6131. } else {
  6132. return array();
  6133. }
  6134. }
  6135. }
  6136. class admin_page_manageportfolios extends admin_externalpage {
  6137. /**
  6138. * Calls parent::__construct with specific arguments
  6139. */
  6140. public function __construct() {
  6141. global $CFG;
  6142. parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
  6143. "$CFG->wwwroot/$CFG->admin/portfolio.php");
  6144. }
  6145. /**
  6146. * Searches page for the specified string.
  6147. * @param string $query The string to search for
  6148. * @return bool True if it is found on this page
  6149. */
  6150. public function search($query) {
  6151. global $CFG;
  6152. if ($result = parent::search($query)) {
  6153. return $result;
  6154. }
  6155. $found = false;
  6156. $portfolios = core_component::get_plugin_list('portfolio');
  6157. foreach ($portfolios as $p => $dir) {
  6158. if (strpos($p, $query) !== false) {
  6159. $found = true;
  6160. break;
  6161. }
  6162. }
  6163. if (!$found) {
  6164. foreach (portfolio_instances(false, false) as $instance) {
  6165. $title = $instance->get('name');
  6166. if (strpos(core_text::strtolower($title), $query) !== false) {
  6167. $found = true;
  6168. break;
  6169. }
  6170. }
  6171. }
  6172. if ($found) {
  6173. $result = new stdClass();
  6174. $result->page = $this;
  6175. $result->settings = array();
  6176. return array($this->name => $result);
  6177. } else {
  6178. return array();
  6179. }
  6180. }
  6181. }
  6182. class admin_page_managerepositories extends admin_externalpage {
  6183. /**
  6184. * Calls parent::__construct with specific arguments
  6185. */
  6186. public function __construct() {
  6187. global $CFG;
  6188. parent::__construct('managerepositories', get_string('manage',
  6189. 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
  6190. }
  6191. /**
  6192. * Searches page for the specified string.
  6193. * @param string $query The string to search for
  6194. * @return bool True if it is found on this page
  6195. */
  6196. public function search($query) {
  6197. global $CFG;
  6198. if ($result = parent::search($query)) {
  6199. return $result;
  6200. }
  6201. $found = false;
  6202. $repositories= core_component::get_plugin_list('repository');
  6203. foreach ($repositories as $p => $dir) {
  6204. if (strpos($p, $query) !== false) {
  6205. $found = true;
  6206. break;
  6207. }
  6208. }
  6209. if (!$found) {
  6210. foreach (repository::get_types() as $instance) {
  6211. $title = $instance->get_typename();
  6212. if (strpos(core_text::strtolower($title), $query) !== false) {
  6213. $found = true;
  6214. break;
  6215. }
  6216. }
  6217. }
  6218. if ($found) {
  6219. $result = new stdClass();
  6220. $result->page = $this;
  6221. $result->settings = array();
  6222. return array($this->name => $result);
  6223. } else {
  6224. return array();
  6225. }
  6226. }
  6227. }
  6228. /**
  6229. * Special class for authentication administration.
  6230. *
  6231. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6232. */
  6233. class admin_setting_manageauths extends admin_setting {
  6234. /**
  6235. * Calls parent::__construct with specific arguments
  6236. */
  6237. public function __construct() {
  6238. $this->nosave = true;
  6239. parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
  6240. }
  6241. /**
  6242. * Always returns true
  6243. *
  6244. * @return true
  6245. */
  6246. public function get_setting() {
  6247. return true;
  6248. }
  6249. /**
  6250. * Always returns true
  6251. *
  6252. * @return true
  6253. */
  6254. public function get_defaultsetting() {
  6255. return true;
  6256. }
  6257. /**
  6258. * Always returns '' and doesn't write anything
  6259. *
  6260. * @return string Always returns ''
  6261. */
  6262. public function write_setting($data) {
  6263. // do not write any setting
  6264. return '';
  6265. }
  6266. /**
  6267. * Search to find if Query is related to auth plugin
  6268. *
  6269. * @param string $query The string to search for
  6270. * @return bool true for related false for not
  6271. */
  6272. public function is_related($query) {
  6273. if (parent::is_related($query)) {
  6274. return true;
  6275. }
  6276. $authsavailable = core_component::get_plugin_list('auth');
  6277. foreach ($authsavailable as $auth => $dir) {
  6278. if (strpos($auth, $query) !== false) {
  6279. return true;
  6280. }
  6281. $authplugin = get_auth_plugin($auth);
  6282. $authtitle = $authplugin->get_title();
  6283. if (strpos(core_text::strtolower($authtitle), $query) !== false) {
  6284. return true;
  6285. }
  6286. }
  6287. return false;
  6288. }
  6289. /**
  6290. * Return XHTML to display control
  6291. *
  6292. * @param mixed $data Unused
  6293. * @param string $query
  6294. * @return string highlight
  6295. */
  6296. public function output_html($data, $query='') {
  6297. global $CFG, $OUTPUT, $DB;
  6298. // display strings
  6299. $txt = get_strings(array('authenticationplugins', 'users', 'administration',
  6300. 'settings', 'edit', 'name', 'enable', 'disable',
  6301. 'up', 'down', 'none', 'users'));
  6302. $txt->updown = "$txt->up/$txt->down";
  6303. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  6304. $txt->testsettings = get_string('testsettings', 'core_auth');
  6305. $authsavailable = core_component::get_plugin_list('auth');
  6306. get_enabled_auth_plugins(true); // fix the list of enabled auths
  6307. if (empty($CFG->auth)) {
  6308. $authsenabled = array();
  6309. } else {
  6310. $authsenabled = explode(',', $CFG->auth);
  6311. }
  6312. // construct the display array, with enabled auth plugins at the top, in order
  6313. $displayauths = array();
  6314. $registrationauths = array();
  6315. $registrationauths[''] = $txt->disable;
  6316. $authplugins = array();
  6317. foreach ($authsenabled as $auth) {
  6318. $authplugin = get_auth_plugin($auth);
  6319. $authplugins[$auth] = $authplugin;
  6320. /// Get the auth title (from core or own auth lang files)
  6321. $authtitle = $authplugin->get_title();
  6322. /// Apply titles
  6323. $displayauths[$auth] = $authtitle;
  6324. if ($authplugin->can_signup()) {
  6325. $registrationauths[$auth] = $authtitle;
  6326. }
  6327. }
  6328. foreach ($authsavailable as $auth => $dir) {
  6329. if (array_key_exists($auth, $displayauths)) {
  6330. continue; //already in the list
  6331. }
  6332. $authplugin = get_auth_plugin($auth);
  6333. $authplugins[$auth] = $authplugin;
  6334. /// Get the auth title (from core or own auth lang files)
  6335. $authtitle = $authplugin->get_title();
  6336. /// Apply titles
  6337. $displayauths[$auth] = $authtitle;
  6338. if ($authplugin->can_signup()) {
  6339. $registrationauths[$auth] = $authtitle;
  6340. }
  6341. }
  6342. $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
  6343. $return .= $OUTPUT->box_start('generalbox authsui');
  6344. $table = new html_table();
  6345. $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
  6346. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  6347. $table->data = array();
  6348. $table->attributes['class'] = 'admintable generaltable';
  6349. $table->id = 'manageauthtable';
  6350. //add always enabled plugins first
  6351. $displayname = $displayauths['manual'];
  6352. $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
  6353. $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
  6354. $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
  6355. $displayname = $displayauths['nologin'];
  6356. $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
  6357. $table->data[] = array($displayname, $usercount, '', '', '', '', '');
  6358. // iterate through auth plugins and add to the display table
  6359. $updowncount = 1;
  6360. $authcount = count($authsenabled);
  6361. $url = "auth.php?sesskey=" . sesskey();
  6362. foreach ($displayauths as $auth => $name) {
  6363. if ($auth == 'manual' or $auth == 'nologin') {
  6364. continue;
  6365. }
  6366. $class = '';
  6367. // hide/show link
  6368. if (in_array($auth, $authsenabled)) {
  6369. $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
  6370. $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
  6371. $enabled = true;
  6372. $displayname = $name;
  6373. }
  6374. else {
  6375. $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
  6376. $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
  6377. $enabled = false;
  6378. $displayname = $name;
  6379. $class = 'dimmed_text';
  6380. }
  6381. $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
  6382. // up/down link (only if auth is enabled)
  6383. $updown = '';
  6384. if ($enabled) {
  6385. if ($updowncount > 1) {
  6386. $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
  6387. $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
  6388. }
  6389. else {
  6390. $updown .= $OUTPUT->spacer() . '&nbsp;';
  6391. }
  6392. if ($updowncount < $authcount) {
  6393. $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
  6394. $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
  6395. }
  6396. else {
  6397. $updown .= $OUTPUT->spacer() . '&nbsp;';
  6398. }
  6399. ++ $updowncount;
  6400. }
  6401. // settings link
  6402. if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
  6403. $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
  6404. } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
  6405. $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
  6406. } else {
  6407. $settings = '';
  6408. }
  6409. // Uninstall link.
  6410. $uninstall = '';
  6411. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
  6412. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  6413. }
  6414. $test = '';
  6415. if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
  6416. $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey()));
  6417. $test = html_writer::link($testurl, $txt->testsettings);
  6418. }
  6419. // Add a row to the table.
  6420. $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
  6421. if ($class) {
  6422. $row->attributes['class'] = $class;
  6423. }
  6424. $table->data[] = $row;
  6425. }
  6426. $return .= html_writer::table($table);
  6427. $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
  6428. $return .= $OUTPUT->box_end();
  6429. return highlight($query, $return);
  6430. }
  6431. }
  6432. /**
  6433. * Special class for authentication administration.
  6434. *
  6435. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6436. */
  6437. class admin_setting_manageeditors extends admin_setting {
  6438. /**
  6439. * Calls parent::__construct with specific arguments
  6440. */
  6441. public function __construct() {
  6442. $this->nosave = true;
  6443. parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
  6444. }
  6445. /**
  6446. * Always returns true, does nothing
  6447. *
  6448. * @return true
  6449. */
  6450. public function get_setting() {
  6451. return true;
  6452. }
  6453. /**
  6454. * Always returns true, does nothing
  6455. *
  6456. * @return true
  6457. */
  6458. public function get_defaultsetting() {
  6459. return true;
  6460. }
  6461. /**
  6462. * Always returns '', does not write anything
  6463. *
  6464. * @return string Always returns ''
  6465. */
  6466. public function write_setting($data) {
  6467. // do not write any setting
  6468. return '';
  6469. }
  6470. /**
  6471. * Checks if $query is one of the available editors
  6472. *
  6473. * @param string $query The string to search for
  6474. * @return bool Returns true if found, false if not
  6475. */
  6476. public function is_related($query) {
  6477. if (parent::is_related($query)) {
  6478. return true;
  6479. }
  6480. $editors_available = editors_get_available();
  6481. foreach ($editors_available as $editor=>$editorstr) {
  6482. if (strpos($editor, $query) !== false) {
  6483. return true;
  6484. }
  6485. if (strpos(core_text::strtolower($editorstr), $query) !== false) {
  6486. return true;
  6487. }
  6488. }
  6489. return false;
  6490. }
  6491. /**
  6492. * Builds the XHTML to display the control
  6493. *
  6494. * @param string $data Unused
  6495. * @param string $query
  6496. * @return string
  6497. */
  6498. public function output_html($data, $query='') {
  6499. global $CFG, $OUTPUT;
  6500. // display strings
  6501. $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
  6502. 'up', 'down', 'none'));
  6503. $struninstall = get_string('uninstallplugin', 'core_admin');
  6504. $txt->updown = "$txt->up/$txt->down";
  6505. $editors_available = editors_get_available();
  6506. $active_editors = explode(',', $CFG->texteditors);
  6507. $active_editors = array_reverse($active_editors);
  6508. foreach ($active_editors as $key=>$editor) {
  6509. if (empty($editors_available[$editor])) {
  6510. unset($active_editors[$key]);
  6511. } else {
  6512. $name = $editors_available[$editor];
  6513. unset($editors_available[$editor]);
  6514. $editors_available[$editor] = $name;
  6515. }
  6516. }
  6517. if (empty($active_editors)) {
  6518. //$active_editors = array('textarea');
  6519. }
  6520. $editors_available = array_reverse($editors_available, true);
  6521. $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
  6522. $return .= $OUTPUT->box_start('generalbox editorsui');
  6523. $table = new html_table();
  6524. $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
  6525. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  6526. $table->id = 'editormanagement';
  6527. $table->attributes['class'] = 'admintable generaltable';
  6528. $table->data = array();
  6529. // iterate through auth plugins and add to the display table
  6530. $updowncount = 1;
  6531. $editorcount = count($active_editors);
  6532. $url = "editors.php?sesskey=" . sesskey();
  6533. foreach ($editors_available as $editor => $name) {
  6534. // hide/show link
  6535. $class = '';
  6536. if (in_array($editor, $active_editors)) {
  6537. $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
  6538. $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
  6539. $enabled = true;
  6540. $displayname = $name;
  6541. }
  6542. else {
  6543. $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
  6544. $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
  6545. $enabled = false;
  6546. $displayname = $name;
  6547. $class = 'dimmed_text';
  6548. }
  6549. // up/down link (only if auth is enabled)
  6550. $updown = '';
  6551. if ($enabled) {
  6552. if ($updowncount > 1) {
  6553. $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
  6554. $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
  6555. }
  6556. else {
  6557. $updown .= $OUTPUT->spacer() . '&nbsp;';
  6558. }
  6559. if ($updowncount < $editorcount) {
  6560. $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
  6561. $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
  6562. }
  6563. else {
  6564. $updown .= $OUTPUT->spacer() . '&nbsp;';
  6565. }
  6566. ++ $updowncount;
  6567. }
  6568. // settings link
  6569. if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
  6570. $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
  6571. $settings = "<a href='$eurl'>{$txt->settings}</a>";
  6572. } else {
  6573. $settings = '';
  6574. }
  6575. $uninstall = '';
  6576. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('editor_'.$editor, 'manage')) {
  6577. $uninstall = html_writer::link($uninstallurl, $struninstall);
  6578. }
  6579. // Add a row to the table.
  6580. $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
  6581. if ($class) {
  6582. $row->attributes['class'] = $class;
  6583. }
  6584. $table->data[] = $row;
  6585. }
  6586. $return .= html_writer::table($table);
  6587. $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
  6588. $return .= $OUTPUT->box_end();
  6589. return highlight($query, $return);
  6590. }
  6591. }
  6592. /**
  6593. * Special class for antiviruses administration.
  6594. *
  6595. * @copyright 2015 Ruslan Kabalin, Lancaster University.
  6596. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6597. */
  6598. class admin_setting_manageantiviruses extends admin_setting {
  6599. /**
  6600. * Calls parent::__construct with specific arguments
  6601. */
  6602. public function __construct() {
  6603. $this->nosave = true;
  6604. parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', '');
  6605. }
  6606. /**
  6607. * Always returns true, does nothing
  6608. *
  6609. * @return true
  6610. */
  6611. public function get_setting() {
  6612. return true;
  6613. }
  6614. /**
  6615. * Always returns true, does nothing
  6616. *
  6617. * @return true
  6618. */
  6619. public function get_defaultsetting() {
  6620. return true;
  6621. }
  6622. /**
  6623. * Always returns '', does not write anything
  6624. *
  6625. * @param string $data Unused
  6626. * @return string Always returns ''
  6627. */
  6628. public function write_setting($data) {
  6629. // Do not write any setting.
  6630. return '';
  6631. }
  6632. /**
  6633. * Checks if $query is one of the available editors
  6634. *
  6635. * @param string $query The string to search for
  6636. * @return bool Returns true if found, false if not
  6637. */
  6638. public function is_related($query) {
  6639. if (parent::is_related($query)) {
  6640. return true;
  6641. }
  6642. $antivirusesavailable = \core\antivirus\manager::get_available();
  6643. foreach ($antivirusesavailable as $antivirus => $antivirusstr) {
  6644. if (strpos($antivirus, $query) !== false) {
  6645. return true;
  6646. }
  6647. if (strpos(core_text::strtolower($antivirusstr), $query) !== false) {
  6648. return true;
  6649. }
  6650. }
  6651. return false;
  6652. }
  6653. /**
  6654. * Builds the XHTML to display the control
  6655. *
  6656. * @param string $data Unused
  6657. * @param string $query
  6658. * @return string
  6659. */
  6660. public function output_html($data, $query='') {
  6661. global $CFG, $OUTPUT;
  6662. // Display strings.
  6663. $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
  6664. 'up', 'down', 'none'));
  6665. $struninstall = get_string('uninstallplugin', 'core_admin');
  6666. $txt->updown = "$txt->up/$txt->down";
  6667. $antivirusesavailable = \core\antivirus\manager::get_available();
  6668. $activeantiviruses = explode(',', $CFG->antiviruses);
  6669. $activeantiviruses = array_reverse($activeantiviruses);
  6670. foreach ($activeantiviruses as $key => $antivirus) {
  6671. if (empty($antivirusesavailable[$antivirus])) {
  6672. unset($activeantiviruses[$key]);
  6673. } else {
  6674. $name = $antivirusesavailable[$antivirus];
  6675. unset($antivirusesavailable[$antivirus]);
  6676. $antivirusesavailable[$antivirus] = $name;
  6677. }
  6678. }
  6679. $antivirusesavailable = array_reverse($antivirusesavailable, true);
  6680. $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true);
  6681. $return .= $OUTPUT->box_start('generalbox antivirusesui');
  6682. $table = new html_table();
  6683. $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
  6684. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  6685. $table->id = 'antivirusmanagement';
  6686. $table->attributes['class'] = 'admintable generaltable';
  6687. $table->data = array();
  6688. // Iterate through auth plugins and add to the display table.
  6689. $updowncount = 1;
  6690. $antiviruscount = count($activeantiviruses);
  6691. $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey()));
  6692. foreach ($antivirusesavailable as $antivirus => $name) {
  6693. // Hide/show link.
  6694. $class = '';
  6695. if (in_array($antivirus, $activeantiviruses)) {
  6696. $hideshowurl = $baseurl;
  6697. $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus));
  6698. $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable'));
  6699. $hideshow = html_writer::link($hideshowurl, $hideshowimg);
  6700. $enabled = true;
  6701. $displayname = $name;
  6702. } else {
  6703. $hideshowurl = $baseurl;
  6704. $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus));
  6705. $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable'));
  6706. $hideshow = html_writer::link($hideshowurl, $hideshowimg);
  6707. $enabled = false;
  6708. $displayname = $name;
  6709. $class = 'dimmed_text';
  6710. }
  6711. // Up/down link.
  6712. $updown = '';
  6713. if ($enabled) {
  6714. if ($updowncount > 1) {
  6715. $updownurl = $baseurl;
  6716. $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus));
  6717. $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup'));
  6718. $updown = html_writer::link($updownurl, $updownimg);
  6719. } else {
  6720. $updownimg = $OUTPUT->spacer();
  6721. }
  6722. if ($updowncount < $antiviruscount) {
  6723. $updownurl = $baseurl;
  6724. $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus));
  6725. $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown'));
  6726. $updown = html_writer::link($updownurl, $updownimg);
  6727. } else {
  6728. $updownimg = $OUTPUT->spacer();
  6729. }
  6730. ++ $updowncount;
  6731. }
  6732. // Settings link.
  6733. if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) {
  6734. $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus));
  6735. $settings = html_writer::link($eurl, $txt->settings);
  6736. } else {
  6737. $settings = '';
  6738. }
  6739. $uninstall = '';
  6740. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) {
  6741. $uninstall = html_writer::link($uninstallurl, $struninstall);
  6742. }
  6743. // Add a row to the table.
  6744. $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
  6745. if ($class) {
  6746. $row->attributes['class'] = $class;
  6747. }
  6748. $table->data[] = $row;
  6749. }
  6750. $return .= html_writer::table($table);
  6751. $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin');
  6752. $return .= $OUTPUT->box_end();
  6753. return highlight($query, $return);
  6754. }
  6755. }
  6756. /**
  6757. * Special class for license administration.
  6758. *
  6759. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6760. * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
  6761. * @todo MDL-45184 This class will be deleted in Moodle 4.1.
  6762. */
  6763. class admin_setting_managelicenses extends admin_setting {
  6764. /**
  6765. * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
  6766. * @todo MDL-45184 This class will be deleted in Moodle 4.1
  6767. */
  6768. public function __construct() {
  6769. global $ADMIN;
  6770. debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
  6771. DEBUG_DEVELOPER);
  6772. // Replace admin setting load with new external page load for tool_licensemanager, if not loaded already.
  6773. if (!is_null($ADMIN->locate('licensemanager'))) {
  6774. $temp = new admin_externalpage('licensemanager',
  6775. get_string('licensemanager', 'tool_licensemanager'),
  6776. \tool_licensemanager\helper::get_licensemanager_url());
  6777. $ADMIN->add('license', $temp);
  6778. }
  6779. }
  6780. /**
  6781. * Always returns true, does nothing
  6782. *
  6783. * @deprecated since Moodle 3.9 MDL-45184.
  6784. * @todo MDL-45184 This method will be deleted in Moodle 4.1
  6785. *
  6786. * @return true
  6787. */
  6788. public function get_setting() {
  6789. debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
  6790. DEBUG_DEVELOPER);
  6791. return true;
  6792. }
  6793. /**
  6794. * Always returns true, does nothing
  6795. *
  6796. * @deprecated since Moodle 3.9 MDL-45184.
  6797. * @todo MDL-45184 This method will be deleted in Moodle 4.1
  6798. *
  6799. * @return true
  6800. */
  6801. public function get_defaultsetting() {
  6802. debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
  6803. DEBUG_DEVELOPER);
  6804. return true;
  6805. }
  6806. /**
  6807. * Always returns '', does not write anything
  6808. *
  6809. * @deprecated since Moodle 3.9 MDL-45184.
  6810. * @todo MDL-45184 This method will be deleted in Moodle 4.1
  6811. *
  6812. * @return string Always returns ''
  6813. */
  6814. public function write_setting($data) {
  6815. debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
  6816. DEBUG_DEVELOPER);
  6817. // do not write any setting
  6818. return '';
  6819. }
  6820. /**
  6821. * Builds the XHTML to display the control
  6822. *
  6823. * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
  6824. * @todo MDL-45184 This method will be deleted in Moodle 4.1
  6825. *
  6826. * @param string $data Unused
  6827. * @param string $query
  6828. * @return string
  6829. */
  6830. public function output_html($data, $query='') {
  6831. debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
  6832. DEBUG_DEVELOPER);
  6833. redirect(\tool_licensemanager\helper::get_licensemanager_url());
  6834. }
  6835. }
  6836. /**
  6837. * Course formats manager. Allows to enable/disable formats and jump to settings
  6838. */
  6839. class admin_setting_manageformats extends admin_setting {
  6840. /**
  6841. * Calls parent::__construct with specific arguments
  6842. */
  6843. public function __construct() {
  6844. $this->nosave = true;
  6845. parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
  6846. }
  6847. /**
  6848. * Always returns true
  6849. *
  6850. * @return true
  6851. */
  6852. public function get_setting() {
  6853. return true;
  6854. }
  6855. /**
  6856. * Always returns true
  6857. *
  6858. * @return true
  6859. */
  6860. public function get_defaultsetting() {
  6861. return true;
  6862. }
  6863. /**
  6864. * Always returns '' and doesn't write anything
  6865. *
  6866. * @param mixed $data string or array, must not be NULL
  6867. * @return string Always returns ''
  6868. */
  6869. public function write_setting($data) {
  6870. // do not write any setting
  6871. return '';
  6872. }
  6873. /**
  6874. * Search to find if Query is related to format plugin
  6875. *
  6876. * @param string $query The string to search for
  6877. * @return bool true for related false for not
  6878. */
  6879. public function is_related($query) {
  6880. if (parent::is_related($query)) {
  6881. return true;
  6882. }
  6883. $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
  6884. foreach ($formats as $format) {
  6885. if (strpos($format->component, $query) !== false ||
  6886. strpos(core_text::strtolower($format->displayname), $query) !== false) {
  6887. return true;
  6888. }
  6889. }
  6890. return false;
  6891. }
  6892. /**
  6893. * Return XHTML to display control
  6894. *
  6895. * @param mixed $data Unused
  6896. * @param string $query
  6897. * @return string highlight
  6898. */
  6899. public function output_html($data, $query='') {
  6900. global $CFG, $OUTPUT;
  6901. $return = '';
  6902. $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
  6903. $return .= $OUTPUT->box_start('generalbox formatsui');
  6904. $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
  6905. // display strings
  6906. $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
  6907. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  6908. $txt->updown = "$txt->up/$txt->down";
  6909. $table = new html_table();
  6910. $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
  6911. $table->align = array('left', 'center', 'center', 'center', 'center');
  6912. $table->attributes['class'] = 'manageformattable generaltable admintable';
  6913. $table->data = array();
  6914. $cnt = 0;
  6915. $defaultformat = get_config('moodlecourse', 'format');
  6916. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  6917. foreach ($formats as $format) {
  6918. $url = new moodle_url('/admin/courseformats.php',
  6919. array('sesskey' => sesskey(), 'format' => $format->name));
  6920. $isdefault = '';
  6921. $class = '';
  6922. if ($format->is_enabled()) {
  6923. $strformatname = $format->displayname;
  6924. if ($defaultformat === $format->name) {
  6925. $hideshow = $txt->default;
  6926. } else {
  6927. $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
  6928. $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
  6929. }
  6930. } else {
  6931. $strformatname = $format->displayname;
  6932. $class = 'dimmed_text';
  6933. $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
  6934. $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
  6935. }
  6936. $updown = '';
  6937. if ($cnt) {
  6938. $updown .= html_writer::link($url->out(false, array('action' => 'up')),
  6939. $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
  6940. } else {
  6941. $updown .= $spacer;
  6942. }
  6943. if ($cnt < count($formats) - 1) {
  6944. $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
  6945. $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
  6946. } else {
  6947. $updown .= $spacer;
  6948. }
  6949. $cnt++;
  6950. $settings = '';
  6951. if ($format->get_settings_url()) {
  6952. $settings = html_writer::link($format->get_settings_url(), $txt->settings);
  6953. }
  6954. $uninstall = '';
  6955. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
  6956. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  6957. }
  6958. $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
  6959. if ($class) {
  6960. $row->attributes['class'] = $class;
  6961. }
  6962. $table->data[] = $row;
  6963. }
  6964. $return .= html_writer::table($table);
  6965. $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
  6966. $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
  6967. $return .= $OUTPUT->box_end();
  6968. return highlight($query, $return);
  6969. }
  6970. }
  6971. /**
  6972. * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
  6973. *
  6974. * @package core
  6975. * @copyright 2018 Toni Barbera
  6976. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6977. */
  6978. class admin_setting_managecustomfields extends admin_setting {
  6979. /**
  6980. * Calls parent::__construct with specific arguments
  6981. */
  6982. public function __construct() {
  6983. $this->nosave = true;
  6984. parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
  6985. }
  6986. /**
  6987. * Always returns true
  6988. *
  6989. * @return true
  6990. */
  6991. public function get_setting() {
  6992. return true;
  6993. }
  6994. /**
  6995. * Always returns true
  6996. *
  6997. * @return true
  6998. */
  6999. public function get_defaultsetting() {
  7000. return true;
  7001. }
  7002. /**
  7003. * Always returns '' and doesn't write anything
  7004. *
  7005. * @param mixed $data string or array, must not be NULL
  7006. * @return string Always returns ''
  7007. */
  7008. public function write_setting($data) {
  7009. // Do not write any setting.
  7010. return '';
  7011. }
  7012. /**
  7013. * Search to find if Query is related to format plugin
  7014. *
  7015. * @param string $query The string to search for
  7016. * @return bool true for related false for not
  7017. */
  7018. public function is_related($query) {
  7019. if (parent::is_related($query)) {
  7020. return true;
  7021. }
  7022. $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
  7023. foreach ($formats as $format) {
  7024. if (strpos($format->component, $query) !== false ||
  7025. strpos(core_text::strtolower($format->displayname), $query) !== false) {
  7026. return true;
  7027. }
  7028. }
  7029. return false;
  7030. }
  7031. /**
  7032. * Return XHTML to display control
  7033. *
  7034. * @param mixed $data Unused
  7035. * @param string $query
  7036. * @return string highlight
  7037. */
  7038. public function output_html($data, $query='') {
  7039. global $CFG, $OUTPUT;
  7040. $return = '';
  7041. $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
  7042. $return .= $OUTPUT->box_start('generalbox customfieldsui');
  7043. $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
  7044. $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
  7045. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  7046. $txt->updown = "$txt->up/$txt->down";
  7047. $table = new html_table();
  7048. $table->head = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
  7049. $table->align = array('left', 'center', 'center', 'center');
  7050. $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
  7051. $table->data = array();
  7052. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  7053. foreach ($fields as $field) {
  7054. $url = new moodle_url('/admin/customfields.php',
  7055. array('sesskey' => sesskey(), 'field' => $field->name));
  7056. if ($field->is_enabled()) {
  7057. $strfieldname = $field->displayname;
  7058. $class = '';
  7059. $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
  7060. $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
  7061. } else {
  7062. $strfieldname = $field->displayname;
  7063. $class = 'dimmed_text';
  7064. $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
  7065. $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
  7066. }
  7067. $settings = '';
  7068. if ($field->get_settings_url()) {
  7069. $settings = html_writer::link($field->get_settings_url(), $txt->settings);
  7070. }
  7071. $uninstall = '';
  7072. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
  7073. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  7074. }
  7075. $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
  7076. $row->attributes['class'] = $class;
  7077. $table->data[] = $row;
  7078. }
  7079. $return .= html_writer::table($table);
  7080. $return .= $OUTPUT->box_end();
  7081. return highlight($query, $return);
  7082. }
  7083. }
  7084. /**
  7085. * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
  7086. *
  7087. * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net)
  7088. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7089. */
  7090. class admin_setting_managedataformats extends admin_setting {
  7091. /**
  7092. * Calls parent::__construct with specific arguments
  7093. */
  7094. public function __construct() {
  7095. $this->nosave = true;
  7096. parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
  7097. }
  7098. /**
  7099. * Always returns true
  7100. *
  7101. * @return true
  7102. */
  7103. public function get_setting() {
  7104. return true;
  7105. }
  7106. /**
  7107. * Always returns true
  7108. *
  7109. * @return true
  7110. */
  7111. public function get_defaultsetting() {
  7112. return true;
  7113. }
  7114. /**
  7115. * Always returns '' and doesn't write anything
  7116. *
  7117. * @param mixed $data string or array, must not be NULL
  7118. * @return string Always returns ''
  7119. */
  7120. public function write_setting($data) {
  7121. // Do not write any setting.
  7122. return '';
  7123. }
  7124. /**
  7125. * Search to find if Query is related to format plugin
  7126. *
  7127. * @param string $query The string to search for
  7128. * @return bool true for related false for not
  7129. */
  7130. public function is_related($query) {
  7131. if (parent::is_related($query)) {
  7132. return true;
  7133. }
  7134. $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
  7135. foreach ($formats as $format) {
  7136. if (strpos($format->component, $query) !== false ||
  7137. strpos(core_text::strtolower($format->displayname), $query) !== false) {
  7138. return true;
  7139. }
  7140. }
  7141. return false;
  7142. }
  7143. /**
  7144. * Return XHTML to display control
  7145. *
  7146. * @param mixed $data Unused
  7147. * @param string $query
  7148. * @return string highlight
  7149. */
  7150. public function output_html($data, $query='') {
  7151. global $CFG, $OUTPUT;
  7152. $return = '';
  7153. $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
  7154. $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
  7155. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  7156. $txt->updown = "$txt->up/$txt->down";
  7157. $table = new html_table();
  7158. $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
  7159. $table->align = array('left', 'center', 'center', 'center', 'center');
  7160. $table->attributes['class'] = 'manageformattable generaltable admintable';
  7161. $table->data = array();
  7162. $cnt = 0;
  7163. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  7164. $totalenabled = 0;
  7165. foreach ($formats as $format) {
  7166. if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
  7167. $totalenabled++;
  7168. }
  7169. }
  7170. foreach ($formats as $format) {
  7171. $status = $format->get_status();
  7172. $url = new moodle_url('/admin/dataformats.php',
  7173. array('sesskey' => sesskey(), 'name' => $format->name));
  7174. $class = '';
  7175. if ($format->is_enabled()) {
  7176. $strformatname = $format->displayname;
  7177. if ($totalenabled == 1&& $format->is_enabled()) {
  7178. $hideshow = '';
  7179. } else {
  7180. $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
  7181. $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
  7182. }
  7183. } else {
  7184. $class = 'dimmed_text';
  7185. $strformatname = $format->displayname;
  7186. $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
  7187. $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
  7188. }
  7189. $updown = '';
  7190. if ($cnt) {
  7191. $updown .= html_writer::link($url->out(false, array('action' => 'up')),
  7192. $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
  7193. } else {
  7194. $updown .= $spacer;
  7195. }
  7196. if ($cnt < count($formats) - 1) {
  7197. $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
  7198. $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
  7199. } else {
  7200. $updown .= $spacer;
  7201. }
  7202. $uninstall = '';
  7203. if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
  7204. $uninstall = get_string('status_missing', 'core_plugin');
  7205. } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
  7206. $uninstall = get_string('status_new', 'core_plugin');
  7207. } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
  7208. if ($totalenabled != 1 || !$format->is_enabled()) {
  7209. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  7210. }
  7211. }
  7212. $settings = '';
  7213. if ($format->get_settings_url()) {
  7214. $settings = html_writer::link($format->get_settings_url(), $txt->settings);
  7215. }
  7216. $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
  7217. if ($class) {
  7218. $row->attributes['class'] = $class;
  7219. }
  7220. $table->data[] = $row;
  7221. $cnt++;
  7222. }
  7223. $return .= html_writer::table($table);
  7224. return highlight($query, $return);
  7225. }
  7226. }
  7227. /**
  7228. * Special class for filter administration.
  7229. *
  7230. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7231. */
  7232. class admin_page_managefilters extends admin_externalpage {
  7233. /**
  7234. * Calls parent::__construct with specific arguments
  7235. */
  7236. public function __construct() {
  7237. global $CFG;
  7238. parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
  7239. }
  7240. /**
  7241. * Searches all installed filters for specified filter
  7242. *
  7243. * @param string $query The filter(string) to search for
  7244. * @param string $query
  7245. */
  7246. public function search($query) {
  7247. global $CFG;
  7248. if ($result = parent::search($query)) {
  7249. return $result;
  7250. }
  7251. $found = false;
  7252. $filternames = filter_get_all_installed();
  7253. foreach ($filternames as $path => $strfiltername) {
  7254. if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
  7255. $found = true;
  7256. break;
  7257. }
  7258. if (strpos($path, $query) !== false) {
  7259. $found = true;
  7260. break;
  7261. }
  7262. }
  7263. if ($found) {
  7264. $result = new stdClass;
  7265. $result->page = $this;
  7266. $result->settings = array();
  7267. return array($this->name => $result);
  7268. } else {
  7269. return array();
  7270. }
  7271. }
  7272. }
  7273. /**
  7274. * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
  7275. * Requires a get_rank method on the plugininfo class for sorting.
  7276. *
  7277. * @copyright 2017 Damyon Wiese
  7278. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7279. */
  7280. abstract class admin_setting_manage_plugins extends admin_setting {
  7281. /**
  7282. * Get the admin settings section name (just a unique string)
  7283. *
  7284. * @return string
  7285. */
  7286. public function get_section_name() {
  7287. return 'manage' . $this->get_plugin_type() . 'plugins';
  7288. }
  7289. /**
  7290. * Get the admin settings section title (use get_string).
  7291. *
  7292. * @return string
  7293. */
  7294. abstract public function get_section_title();
  7295. /**
  7296. * Get the type of plugin to manage.
  7297. *
  7298. * @return string
  7299. */
  7300. abstract public function get_plugin_type();
  7301. /**
  7302. * Get the name of the second column.
  7303. *
  7304. * @return string
  7305. */
  7306. public function get_info_column_name() {
  7307. return '';
  7308. }
  7309. /**
  7310. * Get the type of plugin to manage.
  7311. *
  7312. * @param plugininfo The plugin info class.
  7313. * @return string
  7314. */
  7315. abstract public function get_info_column($plugininfo);
  7316. /**
  7317. * Calls parent::__construct with specific arguments
  7318. */
  7319. public function __construct() {
  7320. $this->nosave = true;
  7321. parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
  7322. }
  7323. /**
  7324. * Always returns true, does nothing
  7325. *
  7326. * @return true
  7327. */
  7328. public function get_setting() {
  7329. return true;
  7330. }
  7331. /**
  7332. * Always returns true, does nothing
  7333. *
  7334. * @return true
  7335. */
  7336. public function get_defaultsetting() {
  7337. return true;
  7338. }
  7339. /**
  7340. * Always returns '', does not write anything
  7341. *
  7342. * @param mixed $data
  7343. * @return string Always returns ''
  7344. */
  7345. public function write_setting($data) {
  7346. // Do not write any setting.
  7347. return '';
  7348. }
  7349. /**
  7350. * Checks if $query is one of the available plugins of this type
  7351. *
  7352. * @param string $query The string to search for
  7353. * @return bool Returns true if found, false if not
  7354. */
  7355. public function is_related($query) {
  7356. if (parent::is_related($query)) {
  7357. return true;
  7358. }
  7359. $query = core_text::strtolower($query);
  7360. $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
  7361. foreach ($plugins as $name => $plugin) {
  7362. $localised = $plugin->displayname;
  7363. if (strpos(core_text::strtolower($name), $query) !== false) {
  7364. return true;
  7365. }
  7366. if (strpos(core_text::strtolower($localised), $query) !== false) {
  7367. return true;
  7368. }
  7369. }
  7370. return false;
  7371. }
  7372. /**
  7373. * The URL for the management page for this plugintype.
  7374. *
  7375. * @return moodle_url
  7376. */
  7377. protected function get_manage_url() {
  7378. return new moodle_url('/admin/updatesetting.php');
  7379. }
  7380. /**
  7381. * Builds the HTML to display the control.
  7382. *
  7383. * @param string $data Unused
  7384. * @param string $query
  7385. * @return string
  7386. */
  7387. public function output_html($data, $query = '') {
  7388. global $CFG, $OUTPUT, $DB, $PAGE;
  7389. $context = (object) [
  7390. 'manageurl' => new moodle_url($this->get_manage_url(), [
  7391. 'type' => $this->get_plugin_type(),
  7392. 'sesskey' => sesskey(),
  7393. ]),
  7394. 'infocolumnname' => $this->get_info_column_name(),
  7395. 'plugins' => [],
  7396. ];
  7397. $pluginmanager = core_plugin_manager::instance();
  7398. $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
  7399. $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
  7400. $plugins = array_merge($enabled, $allplugins);
  7401. foreach ($plugins as $key => $plugin) {
  7402. $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
  7403. $pluginkey = (object) [
  7404. 'plugin' => $plugin->displayname,
  7405. 'enabled' => $plugin->is_enabled(),
  7406. 'togglelink' => '',
  7407. 'moveuplink' => '',
  7408. 'movedownlink' => '',
  7409. 'settingslink' => $plugin->get_settings_url(),
  7410. 'uninstalllink' => '',
  7411. 'info' => '',
  7412. ];
  7413. // Enable/Disable link.
  7414. $togglelink = new moodle_url($pluginlink);
  7415. if ($plugin->is_enabled()) {
  7416. $toggletarget = false;
  7417. $togglelink->param('action', 'disable');
  7418. if (count($context->plugins)) {
  7419. // This is not the first plugin.
  7420. $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
  7421. }
  7422. if (count($enabled) > count($context->plugins) + 1) {
  7423. // This is not the last plugin.
  7424. $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
  7425. }
  7426. $pluginkey->info = $this->get_info_column($plugin);
  7427. } else {
  7428. $toggletarget = true;
  7429. $togglelink->param('action', 'enable');
  7430. }
  7431. $pluginkey->toggletarget = $toggletarget;
  7432. $pluginkey->togglelink = $togglelink;
  7433. $frankenstyle = $plugin->type . '_' . $plugin->name;
  7434. if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
  7435. // This plugin supports uninstallation.
  7436. $pluginkey->uninstalllink = $uninstalllink;
  7437. }
  7438. if (!empty($this->get_info_column_name())) {
  7439. // This plugintype has an info column.
  7440. $pluginkey->info = $this->get_info_column($plugin);
  7441. }
  7442. $context->plugins[] = $pluginkey;
  7443. }
  7444. $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
  7445. return highlight($query, $str);
  7446. }
  7447. }
  7448. /**
  7449. * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
  7450. * Requires a get_rank method on the plugininfo class for sorting.
  7451. *
  7452. * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
  7453. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7454. */
  7455. class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
  7456. public function get_section_title() {
  7457. return get_string('type_fileconverter_plural', 'plugin');
  7458. }
  7459. public function get_plugin_type() {
  7460. return 'fileconverter';
  7461. }
  7462. public function get_info_column_name() {
  7463. return get_string('supportedconversions', 'plugin');
  7464. }
  7465. public function get_info_column($plugininfo) {
  7466. return $plugininfo->get_supported_conversions();
  7467. }
  7468. }
  7469. /**
  7470. * Special class for media player plugins management.
  7471. *
  7472. * @copyright 2016 Marina Glancy
  7473. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7474. */
  7475. class admin_setting_managemediaplayers extends admin_setting {
  7476. /**
  7477. * Calls parent::__construct with specific arguments
  7478. */
  7479. public function __construct() {
  7480. $this->nosave = true;
  7481. parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
  7482. }
  7483. /**
  7484. * Always returns true, does nothing
  7485. *
  7486. * @return true
  7487. */
  7488. public function get_setting() {
  7489. return true;
  7490. }
  7491. /**
  7492. * Always returns true, does nothing
  7493. *
  7494. * @return true
  7495. */
  7496. public function get_defaultsetting() {
  7497. return true;
  7498. }
  7499. /**
  7500. * Always returns '', does not write anything
  7501. *
  7502. * @param mixed $data
  7503. * @return string Always returns ''
  7504. */
  7505. public function write_setting($data) {
  7506. // Do not write any setting.
  7507. return '';
  7508. }
  7509. /**
  7510. * Checks if $query is one of the available enrol plugins
  7511. *
  7512. * @param string $query The string to search for
  7513. * @return bool Returns true if found, false if not
  7514. */
  7515. public function is_related($query) {
  7516. if (parent::is_related($query)) {
  7517. return true;
  7518. }
  7519. $query = core_text::strtolower($query);
  7520. $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
  7521. foreach ($plugins as $name => $plugin) {
  7522. $localised = $plugin->displayname;
  7523. if (strpos(core_text::strtolower($name), $query) !== false) {
  7524. return true;
  7525. }
  7526. if (strpos(core_text::strtolower($localised), $query) !== false) {
  7527. return true;
  7528. }
  7529. }
  7530. return false;
  7531. }
  7532. /**
  7533. * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
  7534. * @return \core\plugininfo\media[]
  7535. */
  7536. protected function get_sorted_plugins() {
  7537. $pluginmanager = core_plugin_manager::instance();
  7538. $plugins = $pluginmanager->get_plugins_of_type('media');
  7539. $enabledplugins = $pluginmanager->get_enabled_plugins('media');
  7540. // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
  7541. \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
  7542. $order = array_values($enabledplugins);
  7543. $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
  7544. $sortedplugins = array();
  7545. foreach ($order as $name) {
  7546. $sortedplugins[$name] = $plugins[$name];
  7547. }
  7548. return $sortedplugins;
  7549. }
  7550. /**
  7551. * Builds the XHTML to display the control
  7552. *
  7553. * @param string $data Unused
  7554. * @param string $query
  7555. * @return string
  7556. */
  7557. public function output_html($data, $query='') {
  7558. global $CFG, $OUTPUT, $DB, $PAGE;
  7559. // Display strings.
  7560. $strup = get_string('up');
  7561. $strdown = get_string('down');
  7562. $strsettings = get_string('settings');
  7563. $strenable = get_string('enable');
  7564. $strdisable = get_string('disable');
  7565. $struninstall = get_string('uninstallplugin', 'core_admin');
  7566. $strversion = get_string('version');
  7567. $strname = get_string('name');
  7568. $strsupports = get_string('supports', 'core_media');
  7569. $pluginmanager = core_plugin_manager::instance();
  7570. $plugins = $this->get_sorted_plugins();
  7571. $enabledplugins = $pluginmanager->get_enabled_plugins('media');
  7572. $return = $OUTPUT->box_start('generalbox mediaplayersui');
  7573. $table = new html_table();
  7574. $table->head = array($strname, $strsupports, $strversion,
  7575. $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
  7576. $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
  7577. 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  7578. $table->id = 'mediaplayerplugins';
  7579. $table->attributes['class'] = 'admintable generaltable';
  7580. $table->data = array();
  7581. // Iterate through media plugins and add to the display table.
  7582. $updowncount = 1;
  7583. $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
  7584. $printed = array();
  7585. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  7586. $usedextensions = [];
  7587. foreach ($plugins as $name => $plugin) {
  7588. $url->param('media', $name);
  7589. $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
  7590. $version = $plugininfo->versiondb;
  7591. $supports = $plugininfo->supports($usedextensions);
  7592. // Hide/show links.
  7593. $class = '';
  7594. if (!$plugininfo->is_installed_and_upgraded()) {
  7595. $hideshow = '';
  7596. $enabled = false;
  7597. $displayname = '<span class="notifyproblem">'.$name.'</span>';
  7598. } else {
  7599. $enabled = $plugininfo->is_enabled();
  7600. if ($enabled) {
  7601. $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
  7602. $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
  7603. } else {
  7604. $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
  7605. $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
  7606. $class = 'dimmed_text';
  7607. }
  7608. $displayname = $plugin->displayname;
  7609. if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
  7610. $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
  7611. }
  7612. }
  7613. if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
  7614. $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
  7615. } else {
  7616. $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
  7617. }
  7618. // Up/down link (only if enrol is enabled).
  7619. $updown = '';
  7620. if ($enabled) {
  7621. if ($updowncount > 1) {
  7622. $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
  7623. $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
  7624. } else {
  7625. $updown = $spacer;
  7626. }
  7627. if ($updowncount < count($enabledplugins)) {
  7628. $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
  7629. $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
  7630. } else {
  7631. $updown .= $spacer;
  7632. }
  7633. ++$updowncount;
  7634. }
  7635. $uninstall = '';
  7636. $status = $plugininfo->get_status();
  7637. if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
  7638. $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
  7639. }
  7640. if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
  7641. $uninstall = get_string('status_new', 'core_plugin');
  7642. } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
  7643. $uninstall .= html_writer::link($uninstallurl, $struninstall);
  7644. }
  7645. $settings = '';
  7646. if ($plugininfo->get_settings_url()) {
  7647. $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
  7648. }
  7649. // Add a row to the table.
  7650. $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
  7651. if ($class) {
  7652. $row->attributes['class'] = $class;
  7653. }
  7654. $table->data[] = $row;
  7655. $printed[$name] = true;
  7656. }
  7657. $return .= html_writer::table($table);
  7658. $return .= $OUTPUT->box_end();
  7659. return highlight($query, $return);
  7660. }
  7661. }
  7662. /**
  7663. * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
  7664. *
  7665. * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
  7666. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7667. */
  7668. class admin_setting_managecontentbankcontenttypes extends admin_setting {
  7669. /**
  7670. * Calls parent::__construct with specific arguments
  7671. */
  7672. public function __construct() {
  7673. $this->nosave = true;
  7674. parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
  7675. }
  7676. /**
  7677. * Always returns true
  7678. *
  7679. * @return true
  7680. */
  7681. public function get_setting() {
  7682. return true;
  7683. }
  7684. /**
  7685. * Always returns true
  7686. *
  7687. * @return true
  7688. */
  7689. public function get_defaultsetting() {
  7690. return true;
  7691. }
  7692. /**
  7693. * Always returns '' and doesn't write anything
  7694. *
  7695. * @param mixed $data string or array, must not be NULL
  7696. * @return string Always returns ''
  7697. */
  7698. public function write_setting($data) {
  7699. // Do not write any setting.
  7700. return '';
  7701. }
  7702. /**
  7703. * Search to find if Query is related to content bank plugin
  7704. *
  7705. * @param string $query The string to search for
  7706. * @return bool true for related false for not
  7707. */
  7708. public function is_related($query) {
  7709. if (parent::is_related($query)) {
  7710. return true;
  7711. }
  7712. $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
  7713. foreach ($types as $type) {
  7714. if (strpos($type->component, $query) !== false ||
  7715. strpos(core_text::strtolower($type->displayname), $query) !== false) {
  7716. return true;
  7717. }
  7718. }
  7719. return false;
  7720. }
  7721. /**
  7722. * Return XHTML to display control
  7723. *
  7724. * @param mixed $data Unused
  7725. * @param string $query
  7726. * @return string highlight
  7727. */
  7728. public function output_html($data, $query='') {
  7729. global $CFG, $OUTPUT;
  7730. $return = '';
  7731. $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
  7732. $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
  7733. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  7734. $table = new html_table();
  7735. $table->head = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
  7736. $table->align = array('left', 'center', 'center', 'center', 'center');
  7737. $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
  7738. $table->data = array();
  7739. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  7740. $totalenabled = 0;
  7741. $count = 0;
  7742. foreach ($types as $type) {
  7743. if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
  7744. $totalenabled++;
  7745. }
  7746. }
  7747. foreach ($types as $type) {
  7748. $url = new moodle_url('/admin/contentbank.php',
  7749. array('sesskey' => sesskey(), 'name' => $type->name));
  7750. $class = '';
  7751. $strtypename = $type->displayname;
  7752. if ($type->is_enabled()) {
  7753. $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
  7754. $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
  7755. } else {
  7756. $class = 'dimmed_text';
  7757. $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
  7758. $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
  7759. }
  7760. $updown = '';
  7761. if ($count) {
  7762. $updown .= html_writer::link($url->out(false, array('action' => 'up')),
  7763. $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
  7764. } else {
  7765. $updown .= $spacer;
  7766. }
  7767. if ($count < count($types) - 1) {
  7768. $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
  7769. $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
  7770. } else {
  7771. $updown .= $spacer;
  7772. }
  7773. $settings = '';
  7774. if ($type->get_settings_url()) {
  7775. $settings = html_writer::link($type->get_settings_url(), $txt->settings);
  7776. }
  7777. $uninstall = '';
  7778. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
  7779. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  7780. }
  7781. $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
  7782. if ($class) {
  7783. $row->attributes['class'] = $class;
  7784. }
  7785. $table->data[] = $row;
  7786. $count++;
  7787. }
  7788. $return .= html_writer::table($table);
  7789. return highlight($query, $return);
  7790. }
  7791. }
  7792. /**
  7793. * Initialise admin page - this function does require login and permission
  7794. * checks specified in page definition.
  7795. *
  7796. * This function must be called on each admin page before other code.
  7797. *
  7798. * @global moodle_page $PAGE
  7799. *
  7800. * @param string $section name of page
  7801. * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
  7802. * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
  7803. * added to the turn blocks editing on/off form, so this page reloads correctly.
  7804. * @param string $actualurl if the actual page being viewed is not the normal one for this
  7805. * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
  7806. * @param array $options Additional options that can be specified for page setup.
  7807. * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
  7808. */
  7809. function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
  7810. global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
  7811. $PAGE->set_context(null); // hack - set context to something, by default to system context
  7812. $site = get_site();
  7813. require_login(null, false);
  7814. if (!empty($options['pagelayout'])) {
  7815. // A specific page layout has been requested.
  7816. $PAGE->set_pagelayout($options['pagelayout']);
  7817. } else if ($section === 'upgradesettings') {
  7818. $PAGE->set_pagelayout('maintenance');
  7819. } else {
  7820. $PAGE->set_pagelayout('admin');
  7821. }
  7822. $adminroot = admin_get_root(false, false); // settings not required for external pages
  7823. $extpage = $adminroot->locate($section, true);
  7824. if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
  7825. // The requested section isn't in the admin tree
  7826. // It could be because the user has inadequate capapbilities or because the section doesn't exist
  7827. if (!has_capability('moodle/site:config', context_system::instance())) {
  7828. // The requested section could depend on a different capability
  7829. // but most likely the user has inadequate capabilities
  7830. print_error('accessdenied', 'admin');
  7831. } else {
  7832. print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
  7833. }
  7834. }
  7835. // this eliminates our need to authenticate on the actual pages
  7836. if (!$extpage->check_access()) {
  7837. print_error('accessdenied', 'admin');
  7838. die;
  7839. }
  7840. navigation_node::require_admin_tree();
  7841. // $PAGE->set_extra_button($extrabutton); TODO
  7842. if (!$actualurl) {
  7843. $actualurl = $extpage->url;
  7844. }
  7845. $PAGE->set_url($actualurl, $extraurlparams);
  7846. if (strpos($PAGE->pagetype, 'admin-') !== 0) {
  7847. $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
  7848. }
  7849. if (empty($SITE->fullname) || empty($SITE->shortname)) {
  7850. // During initial install.
  7851. $strinstallation = get_string('installation', 'install');
  7852. $strsettings = get_string('settings');
  7853. $PAGE->navbar->add($strsettings);
  7854. $PAGE->set_title($strinstallation);
  7855. $PAGE->set_heading($strinstallation);
  7856. $PAGE->set_cacheable(false);
  7857. return;
  7858. }
  7859. // Locate the current item on the navigation and make it active when found.
  7860. $path = $extpage->path;
  7861. $node = $PAGE->settingsnav;
  7862. while ($node && count($path) > 0) {
  7863. $node = $node->get(array_pop($path));
  7864. }
  7865. if ($node) {
  7866. $node->make_active();
  7867. }
  7868. // Normal case.
  7869. $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
  7870. if ($PAGE->user_allowed_editing() && $adminediting != -1) {
  7871. $USER->editing = $adminediting;
  7872. }
  7873. $visiblepathtosection = array_reverse($extpage->visiblepath);
  7874. if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
  7875. if ($PAGE->user_is_editing()) {
  7876. $caption = get_string('blockseditoff');
  7877. $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
  7878. } else {
  7879. $caption = get_string('blocksediton');
  7880. $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
  7881. }
  7882. $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
  7883. }
  7884. $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
  7885. $PAGE->set_heading($SITE->fullname);
  7886. // prevent caching in nav block
  7887. $PAGE->navigation->clear_cache();
  7888. }
  7889. /**
  7890. * Returns the reference to admin tree root
  7891. *
  7892. * @return object admin_root object
  7893. */
  7894. function admin_get_root($reload=false, $requirefulltree=true) {
  7895. global $CFG, $DB, $OUTPUT, $ADMIN;
  7896. if (is_null($ADMIN)) {
  7897. // create the admin tree!
  7898. $ADMIN = new admin_root($requirefulltree);
  7899. }
  7900. if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
  7901. $ADMIN->purge_children($requirefulltree);
  7902. }
  7903. if (!$ADMIN->loaded) {
  7904. // we process this file first to create categories first and in correct order
  7905. require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
  7906. // now we process all other files in admin/settings to build the admin tree
  7907. foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
  7908. if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
  7909. continue;
  7910. }
  7911. if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
  7912. // plugins are loaded last - they may insert pages anywhere
  7913. continue;
  7914. }
  7915. require($file);
  7916. }
  7917. require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
  7918. $ADMIN->loaded = true;
  7919. }
  7920. return $ADMIN;
  7921. }
  7922. /// settings utility functions
  7923. /**
  7924. * This function applies default settings.
  7925. * Because setting the defaults of some settings can enable other settings,
  7926. * this function is called recursively until no more new settings are found.
  7927. *
  7928. * @param object $node, NULL means complete tree, null by default
  7929. * @param bool $unconditional if true overrides all values with defaults, true by default
  7930. * @param array $admindefaultsettings default admin settings to apply. Used recursively
  7931. * @param array $settingsoutput The names and values of the changed settings. Used recursively
  7932. * @return array $settingsoutput The names and values of the changed settings
  7933. */
  7934. function admin_apply_default_settings($node=null, $unconditional=true, $admindefaultsettings=array(), $settingsoutput=array()) {
  7935. $counter = 0;
  7936. if (is_null($node)) {
  7937. core_plugin_manager::reset_caches();
  7938. $node = admin_get_root(true, true);
  7939. $counter = count($settingsoutput);
  7940. }
  7941. if ($node instanceof admin_category) {
  7942. $entries = array_keys($node->children);
  7943. foreach ($entries as $entry) {
  7944. $settingsoutput = admin_apply_default_settings(
  7945. $node->children[$entry], $unconditional, $admindefaultsettings, $settingsoutput
  7946. );
  7947. }
  7948. } else if ($node instanceof admin_settingpage) {
  7949. foreach ($node->settings as $setting) {
  7950. if (!$unconditional && !is_null($setting->get_setting())) {
  7951. // Do not override existing defaults.
  7952. continue;
  7953. }
  7954. $defaultsetting = $setting->get_defaultsetting();
  7955. if (is_null($defaultsetting)) {
  7956. // No value yet - default maybe applied after admin user creation or in upgradesettings.
  7957. continue;
  7958. }
  7959. $settingname = $node->name . '_' . $setting->name; // Get a unique name for the setting.
  7960. if (!array_key_exists($settingname, $admindefaultsettings)) { // Only update a setting if not already processed.
  7961. $admindefaultsettings[$settingname] = $settingname;
  7962. $settingsoutput[$settingname] = $defaultsetting;
  7963. // Set the default for this setting.
  7964. $setting->write_setting($defaultsetting);
  7965. $setting->write_setting_flags(null);
  7966. } else {
  7967. unset($admindefaultsettings[$settingname]); // Remove processed settings.
  7968. }
  7969. }
  7970. }
  7971. // Call this function recursively until all settings are processed.
  7972. if (($node instanceof admin_root) && ($counter != count($settingsoutput))) {
  7973. $settingsoutput = admin_apply_default_settings(null, $unconditional, $admindefaultsettings, $settingsoutput);
  7974. }
  7975. // Just in case somebody modifies the list of active plugins directly.
  7976. core_plugin_manager::reset_caches();
  7977. return $settingsoutput;
  7978. }
  7979. /**
  7980. * Store changed settings, this function updates the errors variable in $ADMIN
  7981. *
  7982. * @param object $formdata from form
  7983. * @return int number of changed settings
  7984. */
  7985. function admin_write_settings($formdata) {
  7986. global $CFG, $SITE, $DB;
  7987. $olddbsessions = !empty($CFG->dbsessions);
  7988. $formdata = (array)$formdata;
  7989. $data = array();
  7990. foreach ($formdata as $fullname=>$value) {
  7991. if (strpos($fullname, 's_') !== 0) {
  7992. continue; // not a config value
  7993. }
  7994. $data[$fullname] = $value;
  7995. }
  7996. $adminroot = admin_get_root();
  7997. $settings = admin_find_write_settings($adminroot, $data);
  7998. $count = 0;
  7999. foreach ($settings as $fullname=>$setting) {
  8000. /** @var $setting admin_setting */
  8001. $original = $setting->get_setting();
  8002. $error = $setting->write_setting($data[$fullname]);
  8003. if ($error !== '') {
  8004. $adminroot->errors[$fullname] = new stdClass();
  8005. $adminroot->errors[$fullname]->data = $data[$fullname];
  8006. $adminroot->errors[$fullname]->id = $setting->get_id();
  8007. $adminroot->errors[$fullname]->error = $error;
  8008. } else {
  8009. $setting->write_setting_flags($data);
  8010. }
  8011. if ($setting->post_write_settings($original)) {
  8012. $count++;
  8013. }
  8014. }
  8015. if ($olddbsessions != !empty($CFG->dbsessions)) {
  8016. require_logout();
  8017. }
  8018. // Now update $SITE - just update the fields, in case other people have a
  8019. // a reference to it (e.g. $PAGE, $COURSE).
  8020. $newsite = $DB->get_record('course', array('id'=>$SITE->id));
  8021. foreach (get_object_vars($newsite) as $field => $value) {
  8022. $SITE->$field = $value;
  8023. }
  8024. // now reload all settings - some of them might depend on the changed
  8025. admin_get_root(true);
  8026. return $count;
  8027. }
  8028. /**
  8029. * Internal recursive function - finds all settings from submitted form
  8030. *
  8031. * @param object $node Instance of admin_category, or admin_settingpage
  8032. * @param array $data
  8033. * @return array
  8034. */
  8035. function admin_find_write_settings($node, $data) {
  8036. $return = array();
  8037. if (empty($data)) {
  8038. return $return;
  8039. }
  8040. if ($node instanceof admin_category) {
  8041. if ($node->check_access()) {
  8042. $entries = array_keys($node->children);
  8043. foreach ($entries as $entry) {
  8044. $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
  8045. }
  8046. }
  8047. } else if ($node instanceof admin_settingpage) {
  8048. if ($node->check_access()) {
  8049. foreach ($node->settings as $setting) {
  8050. $fullname = $setting->get_full_name();
  8051. if (array_key_exists($fullname, $data)) {
  8052. $return[$fullname] = $setting;
  8053. }
  8054. }
  8055. }
  8056. }
  8057. return $return;
  8058. }
  8059. /**
  8060. * Internal function - prints the search results
  8061. *
  8062. * @param string $query String to search for
  8063. * @return string empty or XHTML
  8064. */
  8065. function admin_search_settings_html($query) {
  8066. global $CFG, $OUTPUT, $PAGE;
  8067. if (core_text::strlen($query) < 2) {
  8068. return '';
  8069. }
  8070. $query = core_text::strtolower($query);
  8071. $adminroot = admin_get_root();
  8072. $findings = $adminroot->search($query);
  8073. $savebutton = false;
  8074. $tpldata = (object) [
  8075. 'actionurl' => $PAGE->url->out(false),
  8076. 'results' => [],
  8077. 'sesskey' => sesskey(),
  8078. ];
  8079. foreach ($findings as $found) {
  8080. $page = $found->page;
  8081. $settings = $found->settings;
  8082. if ($page->is_hidden()) {
  8083. // hidden pages are not displayed in search results
  8084. continue;
  8085. }
  8086. $heading = highlight($query, $page->visiblename);
  8087. $headingurl = null;
  8088. if ($page instanceof admin_externalpage) {
  8089. $headingurl = new moodle_url($page->url);
  8090. } else if ($page instanceof admin_settingpage) {
  8091. $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
  8092. } else {
  8093. continue;
  8094. }
  8095. // Locate the page in the admin root and populate its visiblepath attribute.
  8096. $path = array();
  8097. $located = $adminroot->locate($page->name, true);
  8098. if ($located) {
  8099. foreach ($located->visiblepath as $pathitem) {
  8100. array_unshift($path, (string) $pathitem);
  8101. }
  8102. }
  8103. $sectionsettings = [];
  8104. if (!empty($settings)) {
  8105. foreach ($settings as $setting) {
  8106. if (empty($setting->nosave)) {
  8107. $savebutton = true;
  8108. }
  8109. $fullname = $setting->get_full_name();
  8110. if (array_key_exists($fullname, $adminroot->errors)) {
  8111. $data = $adminroot->errors[$fullname]->data;
  8112. } else {
  8113. $data = $setting->get_setting();
  8114. // do not use defaults if settings not available - upgradesettings handles the defaults!
  8115. }
  8116. $sectionsettings[] = $setting->output_html($data, $query);
  8117. }
  8118. }
  8119. $tpldata->results[] = (object) [
  8120. 'title' => $heading,
  8121. 'path' => $path,
  8122. 'url' => $headingurl->out(false),
  8123. 'settings' => $sectionsettings
  8124. ];
  8125. }
  8126. $tpldata->showsave = $savebutton;
  8127. $tpldata->hasresults = !empty($tpldata->results);
  8128. return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
  8129. }
  8130. /**
  8131. * Internal function - returns arrays of html pages with uninitialised settings
  8132. *
  8133. * @param object $node Instance of admin_category or admin_settingpage
  8134. * @return array
  8135. */
  8136. function admin_output_new_settings_by_page($node) {
  8137. global $OUTPUT;
  8138. $return = array();
  8139. if ($node instanceof admin_category) {
  8140. $entries = array_keys($node->children);
  8141. foreach ($entries as $entry) {
  8142. $return += admin_output_new_settings_by_page($node->children[$entry]);
  8143. }
  8144. } else if ($node instanceof admin_settingpage) {
  8145. $newsettings = array();
  8146. foreach ($node->settings as $setting) {
  8147. if (is_null($setting->get_setting())) {
  8148. $newsettings[] = $setting;
  8149. }
  8150. }
  8151. if (count($newsettings) > 0) {
  8152. $adminroot = admin_get_root();
  8153. $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
  8154. $page .= '<fieldset class="adminsettings">'."\n";
  8155. foreach ($newsettings as $setting) {
  8156. $fullname = $setting->get_full_name();
  8157. if (array_key_exists($fullname, $adminroot->errors)) {
  8158. $data = $adminroot->errors[$fullname]->data;
  8159. } else {
  8160. $data = $setting->get_setting();
  8161. if (is_null($data)) {
  8162. $data = $setting->get_defaultsetting();
  8163. }
  8164. }
  8165. $page .= '<div class="clearer"><!-- --></div>'."\n";
  8166. $page .= $setting->output_html($data);
  8167. }
  8168. $page .= '</fieldset>';
  8169. $return[$node->name] = $page;
  8170. }
  8171. }
  8172. return $return;
  8173. }
  8174. /**
  8175. * Format admin settings
  8176. *
  8177. * @param object $setting
  8178. * @param string $title label element
  8179. * @param string $form form fragment, html code - not highlighted automatically
  8180. * @param string $description
  8181. * @param mixed $label link label to id, true by default or string being the label to connect it to
  8182. * @param string $warning warning text
  8183. * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
  8184. * @param string $query search query to be highlighted
  8185. * @return string XHTML
  8186. */
  8187. function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
  8188. global $CFG, $OUTPUT;
  8189. $context = (object) [
  8190. 'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
  8191. 'fullname' => $setting->get_full_name(),
  8192. ];
  8193. // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
  8194. if ($label === true) {
  8195. $context->labelfor = $setting->get_id();
  8196. } else if ($label === false) {
  8197. $context->labelfor = '';
  8198. } else {
  8199. $context->labelfor = $label;
  8200. }
  8201. $form .= $setting->output_setting_flags();
  8202. $context->warning = $warning;
  8203. $context->override = '';
  8204. if (empty($setting->plugin)) {
  8205. if (array_key_exists($setting->name, $CFG->config_php_settings)) {
  8206. $context->override = get_string('configoverride', 'admin');
  8207. }
  8208. } else {
  8209. if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
  8210. $context->override = get_string('configoverride', 'admin');
  8211. }
  8212. }
  8213. $defaults = array();
  8214. if (!is_null($defaultinfo)) {
  8215. if ($defaultinfo === '') {
  8216. $defaultinfo = get_string('emptysettingvalue', 'admin');
  8217. }
  8218. $defaults[] = $defaultinfo;
  8219. }
  8220. $context->default = null;
  8221. $setting->get_setting_flag_defaults($defaults);
  8222. if (!empty($defaults)) {
  8223. $defaultinfo = implode(', ', $defaults);
  8224. $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
  8225. $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
  8226. }
  8227. $context->error = '';
  8228. $adminroot = admin_get_root();
  8229. if (array_key_exists($context->fullname, $adminroot->errors)) {
  8230. $context->error = $adminroot->errors[$context->fullname]->error;
  8231. }
  8232. if ($dependenton = $setting->get_dependent_on()) {
  8233. $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
  8234. }
  8235. $context->id = 'admin-' . $setting->name;
  8236. $context->title = highlightfast($query, $title);
  8237. $context->name = highlightfast($query, $context->name);
  8238. $context->description = highlight($query, markdown_to_html($description));
  8239. $context->element = $form;
  8240. $context->forceltr = $setting->get_force_ltr();
  8241. $context->customcontrol = $setting->has_custom_form_control();
  8242. return $OUTPUT->render_from_template('core_admin/setting', $context);
  8243. }
  8244. /**
  8245. * Based on find_new_settings{@link ()} in upgradesettings.php
  8246. * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
  8247. *
  8248. * @param object $node Instance of admin_category, or admin_settingpage
  8249. * @return boolean true if any settings haven't been initialised, false if they all have
  8250. */
  8251. function any_new_admin_settings($node) {
  8252. if ($node instanceof admin_category) {
  8253. $entries = array_keys($node->children);
  8254. foreach ($entries as $entry) {
  8255. if (any_new_admin_settings($node->children[$entry])) {
  8256. return true;
  8257. }
  8258. }
  8259. } else if ($node instanceof admin_settingpage) {
  8260. foreach ($node->settings as $setting) {
  8261. if ($setting->get_setting() === NULL) {
  8262. return true;
  8263. }
  8264. }
  8265. }
  8266. return false;
  8267. }
  8268. /**
  8269. * Given a table and optionally a column name should replaces be done?
  8270. *
  8271. * @param string $table name
  8272. * @param string $column name
  8273. * @return bool success or fail
  8274. */
  8275. function db_should_replace($table, $column = '', $additionalskiptables = ''): bool {
  8276. // TODO: this is horrible hack, we should have a hook and each plugin should be responsible for proper replacing...
  8277. $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions',
  8278. 'events_queue', 'repository_instance_config', 'block_instances', 'files'];
  8279. // Additional skip tables.
  8280. if (!empty($additionalskiptables)) {
  8281. $skiptables = array_merge($skiptables, explode(',', str_replace(' ', '', $additionalskiptables)));
  8282. }
  8283. // Don't process these.
  8284. if (in_array($table, $skiptables)) {
  8285. return false;
  8286. }
  8287. // To be safe never replace inside a table that looks related to logging.
  8288. if (preg_match('/(^|_)logs?($|_)/', $table)) {
  8289. return false;
  8290. }
  8291. // Do column based exclusions.
  8292. if (!empty($column)) {
  8293. // Don't touch anything that looks like a hash.
  8294. if (preg_match('/hash$/', $column)) {
  8295. return false;
  8296. }
  8297. }
  8298. return true;
  8299. }
  8300. /**
  8301. * Moved from admin/replace.php so that we can use this in cron
  8302. *
  8303. * @param string $search string to look for
  8304. * @param string $replace string to replace
  8305. * @return bool success or fail
  8306. */
  8307. function db_replace($search, $replace, $additionalskiptables = '') {
  8308. global $DB, $CFG, $OUTPUT;
  8309. // Turn off time limits, sometimes upgrades can be slow.
  8310. core_php_time_limit::raise();
  8311. if (!$tables = $DB->get_tables() ) { // No tables yet at all.
  8312. return false;
  8313. }
  8314. foreach ($tables as $table) {
  8315. if (!db_should_replace($table, '', $additionalskiptables)) {
  8316. continue;
  8317. }
  8318. if ($columns = $DB->get_columns($table)) {
  8319. $DB->set_debug(true);
  8320. foreach ($columns as $column) {
  8321. if (!db_should_replace($table, $column->name)) {
  8322. continue;
  8323. }
  8324. $DB->replace_all_text($table, $column, $search, $replace);
  8325. }
  8326. $DB->set_debug(false);
  8327. }
  8328. }
  8329. // delete modinfo caches
  8330. rebuild_course_cache(0, true);
  8331. // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
  8332. $blocks = core_component::get_plugin_list('block');
  8333. foreach ($blocks as $blockname=>$fullblock) {
  8334. if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
  8335. continue;
  8336. }
  8337. if (!is_readable($fullblock.'/lib.php')) {
  8338. continue;
  8339. }
  8340. $function = 'block_'.$blockname.'_global_db_replace';
  8341. include_once($fullblock.'/lib.php');
  8342. if (!function_exists($function)) {
  8343. continue;
  8344. }
  8345. echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
  8346. $function($search, $replace);
  8347. echo $OUTPUT->notification("...finished", 'notifysuccess');
  8348. }
  8349. // Trigger an event.
  8350. $eventargs = [
  8351. 'context' => context_system::instance(),
  8352. 'other' => [
  8353. 'search' => $search,
  8354. 'replace' => $replace
  8355. ]
  8356. ];
  8357. $event = \core\event\database_text_field_content_replaced::create($eventargs);
  8358. $event->trigger();
  8359. purge_all_caches();
  8360. return true;
  8361. }
  8362. /**
  8363. * Manage repository settings
  8364. *
  8365. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  8366. */
  8367. class admin_setting_managerepository extends admin_setting {
  8368. /** @var string */
  8369. private $baseurl;
  8370. /**
  8371. * calls parent::__construct with specific arguments
  8372. */
  8373. public function __construct() {
  8374. global $CFG;
  8375. parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
  8376. $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
  8377. }
  8378. /**
  8379. * Always returns true, does nothing
  8380. *
  8381. * @return true
  8382. */
  8383. public function get_setting() {
  8384. return true;
  8385. }
  8386. /**
  8387. * Always returns true does nothing
  8388. *
  8389. * @return true
  8390. */
  8391. public function get_defaultsetting() {
  8392. return true;
  8393. }
  8394. /**
  8395. * Always returns s_managerepository
  8396. *
  8397. * @return string Always return 's_managerepository'
  8398. */
  8399. public function get_full_name() {
  8400. return 's_managerepository';
  8401. }
  8402. /**
  8403. * Always returns '' doesn't do anything
  8404. */
  8405. public function write_setting($data) {
  8406. $url = $this->baseurl . '&amp;new=' . $data;
  8407. return '';
  8408. // TODO
  8409. // Should not use redirect and exit here
  8410. // Find a better way to do this.
  8411. // redirect($url);
  8412. // exit;
  8413. }
  8414. /**
  8415. * Searches repository plugins for one that matches $query
  8416. *
  8417. * @param string $query The string to search for
  8418. * @return bool true if found, false if not
  8419. */
  8420. public function is_related($query) {
  8421. if (parent::is_related($query)) {
  8422. return true;
  8423. }
  8424. $repositories= core_component::get_plugin_list('repository');
  8425. foreach ($repositories as $p => $dir) {
  8426. if (strpos($p, $query) !== false) {
  8427. return true;
  8428. }
  8429. }
  8430. foreach (repository::get_types() as $instance) {
  8431. $title = $instance->get_typename();
  8432. if (strpos(core_text::strtolower($title), $query) !== false) {
  8433. return true;
  8434. }
  8435. }
  8436. return false;
  8437. }
  8438. /**
  8439. * Helper function that generates a moodle_url object
  8440. * relevant to the repository
  8441. */
  8442. function repository_action_url($repository) {
  8443. return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
  8444. }
  8445. /**
  8446. * Builds XHTML to display the control
  8447. *
  8448. * @param string $data Unused
  8449. * @param string $query
  8450. * @return string XHTML
  8451. */
  8452. public function output_html($data, $query='') {
  8453. global $CFG, $USER, $OUTPUT;
  8454. // Get strings that are used
  8455. $strshow = get_string('on', 'repository');
  8456. $strhide = get_string('off', 'repository');
  8457. $strdelete = get_string('disabled', 'repository');
  8458. $actionchoicesforexisting = array(
  8459. 'show' => $strshow,
  8460. 'hide' => $strhide,
  8461. 'delete' => $strdelete
  8462. );
  8463. $actionchoicesfornew = array(
  8464. 'newon' => $strshow,
  8465. 'newoff' => $strhide,
  8466. 'delete' => $strdelete
  8467. );
  8468. $return = '';
  8469. $return .= $OUTPUT->box_start('generalbox');
  8470. // Set strings that are used multiple times
  8471. $settingsstr = get_string('settings');
  8472. $disablestr = get_string('disable');
  8473. // Table to list plug-ins
  8474. $table = new html_table();
  8475. $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
  8476. $table->align = array('left', 'center', 'center', 'center', 'center');
  8477. $table->data = array();
  8478. // Get list of used plug-ins
  8479. $repositorytypes = repository::get_types();
  8480. if (!empty($repositorytypes)) {
  8481. // Array to store plugins being used
  8482. $alreadyplugins = array();
  8483. $totalrepositorytypes = count($repositorytypes);
  8484. $updowncount = 1;
  8485. foreach ($repositorytypes as $i) {
  8486. $settings = '';
  8487. $typename = $i->get_typename();
  8488. // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
  8489. $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
  8490. $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
  8491. if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
  8492. // Calculate number of instances in order to display them for the Moodle administrator
  8493. if (!empty($instanceoptionnames)) {
  8494. $params = array();
  8495. $params['context'] = array(context_system::instance());
  8496. $params['onlyvisible'] = false;
  8497. $params['type'] = $typename;
  8498. $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
  8499. // site instances
  8500. $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
  8501. $params['context'] = array();
  8502. $instances = repository::static_function($typename, 'get_instances', $params);
  8503. $courseinstances = array();
  8504. $userinstances = array();
  8505. foreach ($instances as $instance) {
  8506. $repocontext = context::instance_by_id($instance->instance->contextid);
  8507. if ($repocontext->contextlevel == CONTEXT_COURSE) {
  8508. $courseinstances[] = $instance;
  8509. } else if ($repocontext->contextlevel == CONTEXT_USER) {
  8510. $userinstances[] = $instance;
  8511. }
  8512. }
  8513. // course instances
  8514. $instancenumber = count($courseinstances);
  8515. $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
  8516. // user private instances
  8517. $instancenumber = count($userinstances);
  8518. $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
  8519. } else {
  8520. $admininstancenumbertext = "";
  8521. $courseinstancenumbertext = "";
  8522. $userinstancenumbertext = "";
  8523. }
  8524. $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
  8525. $settings .= $OUTPUT->container_start('mdl-left');
  8526. $settings .= '<br/>';
  8527. $settings .= $admininstancenumbertext;
  8528. $settings .= '<br/>';
  8529. $settings .= $courseinstancenumbertext;
  8530. $settings .= '<br/>';
  8531. $settings .= $userinstancenumbertext;
  8532. $settings .= $OUTPUT->container_end();
  8533. }
  8534. // Get the current visibility
  8535. if ($i->get_visible()) {
  8536. $currentaction = 'show';
  8537. } else {
  8538. $currentaction = 'hide';
  8539. }
  8540. $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
  8541. // Display up/down link
  8542. $updown = '';
  8543. // Should be done with CSS instead.
  8544. $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
  8545. if ($updowncount > 1) {
  8546. $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
  8547. $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
  8548. }
  8549. else {
  8550. $updown .= $spacer;
  8551. }
  8552. if ($updowncount < $totalrepositorytypes) {
  8553. $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
  8554. $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
  8555. }
  8556. else {
  8557. $updown .= $spacer;
  8558. }
  8559. $updowncount++;
  8560. $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
  8561. if (!in_array($typename, $alreadyplugins)) {
  8562. $alreadyplugins[] = $typename;
  8563. }
  8564. }
  8565. }
  8566. // Get all the plugins that exist on disk
  8567. $plugins = core_component::get_plugin_list('repository');
  8568. if (!empty($plugins)) {
  8569. foreach ($plugins as $plugin => $dir) {
  8570. // Check that it has not already been listed
  8571. if (!in_array($plugin, $alreadyplugins)) {
  8572. $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
  8573. $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
  8574. }
  8575. }
  8576. }
  8577. $return .= html_writer::table($table);
  8578. $return .= $OUTPUT->box_end();
  8579. return highlight($query, $return);
  8580. }
  8581. }
  8582. /**
  8583. * Special checkbox for enable mobile web service
  8584. * If enable then we store the service id of the mobile service into config table
  8585. * If disable then we unstore the service id from the config table
  8586. */
  8587. class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
  8588. /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
  8589. private $restuse;
  8590. /**
  8591. * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
  8592. *
  8593. * @return boolean
  8594. */
  8595. private function is_protocol_cap_allowed() {
  8596. global $DB, $CFG;
  8597. // If the $this->restuse variable is not set, it needs to be set.
  8598. if (empty($this->restuse) and $this->restuse!==false) {
  8599. $params = array();
  8600. $params['permission'] = CAP_ALLOW;
  8601. $params['roleid'] = $CFG->defaultuserroleid;
  8602. $params['capability'] = 'webservice/rest:use';
  8603. $this->restuse = $DB->record_exists('role_capabilities', $params);
  8604. }
  8605. return $this->restuse;
  8606. }
  8607. /**
  8608. * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
  8609. * @param type $status true to allow, false to not set
  8610. */
  8611. private function set_protocol_cap($status) {
  8612. global $CFG;
  8613. if ($status and !$this->is_protocol_cap_allowed()) {
  8614. //need to allow the cap
  8615. $permission = CAP_ALLOW;
  8616. $assign = true;
  8617. } else if (!$status and $this->is_protocol_cap_allowed()){
  8618. //need to disallow the cap
  8619. $permission = CAP_INHERIT;
  8620. $assign = true;
  8621. }
  8622. if (!empty($assign)) {
  8623. $systemcontext = context_system::instance();
  8624. assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
  8625. }
  8626. }
  8627. /**
  8628. * Builds XHTML to display the control.
  8629. * The main purpose of this overloading is to display a warning when https
  8630. * is not supported by the server
  8631. * @param string $data Unused
  8632. * @param string $query
  8633. * @return string XHTML
  8634. */
  8635. public function output_html($data, $query='') {
  8636. global $OUTPUT;
  8637. $html = parent::output_html($data, $query);
  8638. if ((string)$data === $this->yes) {
  8639. $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
  8640. foreach ($notifications as $notification) {
  8641. $message = get_string($notification[0], $notification[1]);
  8642. $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
  8643. }
  8644. }
  8645. return $html;
  8646. }
  8647. /**
  8648. * Retrieves the current setting using the objects name
  8649. *
  8650. * @return string
  8651. */
  8652. public function get_setting() {
  8653. global $CFG;
  8654. // First check if is not set.
  8655. $result = $this->config_read($this->name);
  8656. if (is_null($result)) {
  8657. return null;
  8658. }
  8659. // For install cli script, $CFG->defaultuserroleid is not set so return 0
  8660. // Or if web services aren't enabled this can't be,
  8661. if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
  8662. return 0;
  8663. }
  8664. require_once($CFG->dirroot . '/webservice/lib.php');
  8665. $webservicemanager = new webservice();
  8666. $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
  8667. if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
  8668. return $result;
  8669. } else {
  8670. return 0;
  8671. }
  8672. }
  8673. /**
  8674. * Save the selected setting
  8675. *
  8676. * @param string $data The selected site
  8677. * @return string empty string or error message
  8678. */
  8679. public function write_setting($data) {
  8680. global $DB, $CFG;
  8681. //for install cli script, $CFG->defaultuserroleid is not set so do nothing
  8682. if (empty($CFG->defaultuserroleid)) {
  8683. return '';
  8684. }
  8685. $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
  8686. require_once($CFG->dirroot . '/webservice/lib.php');
  8687. $webservicemanager = new webservice();
  8688. $updateprotocol = false;
  8689. if ((string)$data === $this->yes) {
  8690. //code run when enable mobile web service
  8691. //enable web service systeme if necessary
  8692. set_config('enablewebservices', true);
  8693. //enable mobile service
  8694. $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
  8695. $mobileservice->enabled = 1;
  8696. $webservicemanager->update_external_service($mobileservice);
  8697. // Enable REST server.
  8698. $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
  8699. if (!in_array('rest', $activeprotocols)) {
  8700. $activeprotocols[] = 'rest';
  8701. $updateprotocol = true;
  8702. }
  8703. if ($updateprotocol) {
  8704. set_config('webserviceprotocols', implode(',', $activeprotocols));
  8705. }
  8706. // Allow rest:use capability for authenticated user.
  8707. $this->set_protocol_cap(true);
  8708. } else {
  8709. //disable web service system if no other services are enabled
  8710. $otherenabledservices = $DB->get_records_select('external_services',
  8711. 'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
  8712. 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
  8713. if (empty($otherenabledservices)) {
  8714. set_config('enablewebservices', false);
  8715. // Also disable REST server.
  8716. $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
  8717. $protocolkey = array_search('rest', $activeprotocols);
  8718. if ($protocolkey !== false) {
  8719. unset($activeprotocols[$protocolkey]);
  8720. $updateprotocol = true;
  8721. }
  8722. if ($updateprotocol) {
  8723. set_config('webserviceprotocols', implode(',', $activeprotocols));
  8724. }
  8725. // Disallow rest:use capability for authenticated user.
  8726. $this->set_protocol_cap(false);
  8727. }
  8728. //disable the mobile service
  8729. $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
  8730. $mobileservice->enabled = 0;
  8731. $webservicemanager->update_external_service($mobileservice);
  8732. }
  8733. return (parent::write_setting($data));
  8734. }
  8735. }
  8736. /**
  8737. * Special class for management of external services
  8738. *
  8739. * @author Petr Skoda (skodak)
  8740. */
  8741. class admin_setting_manageexternalservices extends admin_setting {
  8742. /**
  8743. * Calls parent::__construct with specific arguments
  8744. */
  8745. public function __construct() {
  8746. $this->nosave = true;
  8747. parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
  8748. }
  8749. /**
  8750. * Always returns true, does nothing
  8751. *
  8752. * @return true
  8753. */
  8754. public function get_setting() {
  8755. return true;
  8756. }
  8757. /**
  8758. * Always returns true, does nothing
  8759. *
  8760. * @return true
  8761. */
  8762. public function get_defaultsetting() {
  8763. return true;
  8764. }
  8765. /**
  8766. * Always returns '', does not write anything
  8767. *
  8768. * @return string Always returns ''
  8769. */
  8770. public function write_setting($data) {
  8771. // do not write any setting
  8772. return '';
  8773. }
  8774. /**
  8775. * Checks if $query is one of the available external services
  8776. *
  8777. * @param string $query The string to search for
  8778. * @return bool Returns true if found, false if not
  8779. */
  8780. public function is_related($query) {
  8781. global $DB;
  8782. if (parent::is_related($query)) {
  8783. return true;
  8784. }
  8785. $services = $DB->get_records('external_services', array(), 'id, name');
  8786. foreach ($services as $service) {
  8787. if (strpos(core_text::strtolower($service->name), $query) !== false) {
  8788. return true;
  8789. }
  8790. }
  8791. return false;
  8792. }
  8793. /**
  8794. * Builds the XHTML to display the control
  8795. *
  8796. * @param string $data Unused
  8797. * @param string $query
  8798. * @return string
  8799. */
  8800. public function output_html($data, $query='') {
  8801. global $CFG, $OUTPUT, $DB;
  8802. // display strings
  8803. $stradministration = get_string('administration');
  8804. $stredit = get_string('edit');
  8805. $strservice = get_string('externalservice', 'webservice');
  8806. $strdelete = get_string('delete');
  8807. $strplugin = get_string('plugin', 'admin');
  8808. $stradd = get_string('add');
  8809. $strfunctions = get_string('functions', 'webservice');
  8810. $strusers = get_string('users');
  8811. $strserviceusers = get_string('serviceusers', 'webservice');
  8812. $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
  8813. $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
  8814. $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
  8815. // built in services
  8816. $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
  8817. $return = "";
  8818. if (!empty($services)) {
  8819. $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
  8820. $table = new html_table();
  8821. $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
  8822. $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
  8823. $table->id = 'builtinservices';
  8824. $table->attributes['class'] = 'admintable externalservices generaltable';
  8825. $table->data = array();
  8826. // iterate through auth plugins and add to the display table
  8827. foreach ($services as $service) {
  8828. $name = $service->name;
  8829. // hide/show link
  8830. if ($service->enabled) {
  8831. $displayname = "<span>$name</span>";
  8832. } else {
  8833. $displayname = "<span class=\"dimmed_text\">$name</span>";
  8834. }
  8835. $plugin = $service->component;
  8836. $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
  8837. if ($service->restrictedusers) {
  8838. $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
  8839. } else {
  8840. $users = get_string('allusers', 'webservice');
  8841. }
  8842. $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
  8843. // add a row to the table
  8844. $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
  8845. }
  8846. $return .= html_writer::table($table);
  8847. }
  8848. // Custom services
  8849. $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
  8850. $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
  8851. $table = new html_table();
  8852. $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
  8853. $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
  8854. $table->id = 'customservices';
  8855. $table->attributes['class'] = 'admintable externalservices generaltable';
  8856. $table->data = array();
  8857. // iterate through auth plugins and add to the display table
  8858. foreach ($services as $service) {
  8859. $name = $service->name;
  8860. // hide/show link
  8861. if ($service->enabled) {
  8862. $displayname = "<span>$name</span>";
  8863. } else {
  8864. $displayname = "<span class=\"dimmed_text\">$name</span>";
  8865. }
  8866. // delete link
  8867. $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
  8868. $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
  8869. if ($service->restrictedusers) {
  8870. $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
  8871. } else {
  8872. $users = get_string('allusers', 'webservice');
  8873. }
  8874. $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
  8875. // add a row to the table
  8876. $table->data[] = array($displayname, $delete, $functions, $users, $edit);
  8877. }
  8878. // add new custom service option
  8879. $return .= html_writer::table($table);
  8880. $return .= '<br />';
  8881. // add a token to the table
  8882. $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
  8883. return highlight($query, $return);
  8884. }
  8885. }
  8886. /**
  8887. * Special class for overview of external services
  8888. *
  8889. * @author Jerome Mouneyrac
  8890. */
  8891. class admin_setting_webservicesoverview extends admin_setting {
  8892. /**
  8893. * Calls parent::__construct with specific arguments
  8894. */
  8895. public function __construct() {
  8896. $this->nosave = true;
  8897. parent::__construct('webservicesoverviewui',
  8898. get_string('webservicesoverview', 'webservice'), '', '');
  8899. }
  8900. /**
  8901. * Always returns true, does nothing
  8902. *
  8903. * @return true
  8904. */
  8905. public function get_setting() {
  8906. return true;
  8907. }
  8908. /**
  8909. * Always returns true, does nothing
  8910. *
  8911. * @return true
  8912. */
  8913. public function get_defaultsetting() {
  8914. return true;
  8915. }
  8916. /**
  8917. * Always returns '', does not write anything
  8918. *
  8919. * @return string Always returns ''
  8920. */
  8921. public function write_setting($data) {
  8922. // do not write any setting
  8923. return '';
  8924. }
  8925. /**
  8926. * Builds the XHTML to display the control
  8927. *
  8928. * @param string $data Unused
  8929. * @param string $query
  8930. * @return string
  8931. */
  8932. public function output_html($data, $query='') {
  8933. global $CFG, $OUTPUT;
  8934. $return = "";
  8935. $brtag = html_writer::empty_tag('br');
  8936. /// One system controlling Moodle with Token
  8937. $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
  8938. $table = new html_table();
  8939. $table->head = array(get_string('step', 'webservice'), get_string('status'),
  8940. get_string('description'));
  8941. $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
  8942. $table->id = 'onesystemcontrol';
  8943. $table->attributes['class'] = 'admintable wsoverview generaltable';
  8944. $table->data = array();
  8945. $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
  8946. . $brtag . $brtag;
  8947. /// 1. Enable Web Services
  8948. $row = array();
  8949. $url = new moodle_url("/admin/search.php?query=enablewebservices");
  8950. $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
  8951. array('href' => $url));
  8952. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  8953. if ($CFG->enablewebservices) {
  8954. $status = get_string('yes');
  8955. }
  8956. $row[1] = $status;
  8957. $row[2] = get_string('enablewsdescription', 'webservice');
  8958. $table->data[] = $row;
  8959. /// 2. Enable protocols
  8960. $row = array();
  8961. $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
  8962. $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
  8963. array('href' => $url));
  8964. $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
  8965. //retrieve activated protocol
  8966. $active_protocols = empty($CFG->webserviceprotocols) ?
  8967. array() : explode(',', $CFG->webserviceprotocols);
  8968. if (!empty($active_protocols)) {
  8969. $status = "";
  8970. foreach ($active_protocols as $protocol) {
  8971. $status .= $protocol . $brtag;
  8972. }
  8973. }
  8974. $row[1] = $status;
  8975. $row[2] = get_string('enableprotocolsdescription', 'webservice');
  8976. $table->data[] = $row;
  8977. /// 3. Create user account
  8978. $row = array();
  8979. $url = new moodle_url("/user/editadvanced.php?id=-1");
  8980. $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
  8981. array('href' => $url));
  8982. $row[1] = "";
  8983. $row[2] = get_string('createuserdescription', 'webservice');
  8984. $table->data[] = $row;
  8985. /// 4. Add capability to users
  8986. $row = array();
  8987. $url = new moodle_url("/admin/roles/check.php?contextid=1");
  8988. $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
  8989. array('href' => $url));
  8990. $row[1] = "";
  8991. $row[2] = get_string('checkusercapabilitydescription', 'webservice');
  8992. $table->data[] = $row;
  8993. /// 5. Select a web service
  8994. $row = array();
  8995. $url = new moodle_url("/admin/settings.php?section=externalservices");
  8996. $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
  8997. array('href' => $url));
  8998. $row[1] = "";
  8999. $row[2] = get_string('createservicedescription', 'webservice');
  9000. $table->data[] = $row;
  9001. /// 6. Add functions
  9002. $row = array();
  9003. $url = new moodle_url("/admin/settings.php?section=externalservices");
  9004. $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
  9005. array('href' => $url));
  9006. $row[1] = "";
  9007. $row[2] = get_string('addfunctionsdescription', 'webservice');
  9008. $table->data[] = $row;
  9009. /// 7. Add the specific user
  9010. $row = array();
  9011. $url = new moodle_url("/admin/settings.php?section=externalservices");
  9012. $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
  9013. array('href' => $url));
  9014. $row[1] = "";
  9015. $row[2] = get_string('selectspecificuserdescription', 'webservice');
  9016. $table->data[] = $row;
  9017. /// 8. Create token for the specific user
  9018. $row = array();
  9019. $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
  9020. $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
  9021. array('href' => $url));
  9022. $row[1] = "";
  9023. $row[2] = get_string('createtokenforuserdescription', 'webservice');
  9024. $table->data[] = $row;
  9025. /// 9. Enable the documentation
  9026. $row = array();
  9027. $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
  9028. $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
  9029. array('href' => $url));
  9030. $status = '<span class="warning">' . get_string('no') . '</span>';
  9031. if ($CFG->enablewsdocumentation) {
  9032. $status = get_string('yes');
  9033. }
  9034. $row[1] = $status;
  9035. $row[2] = get_string('enabledocumentationdescription', 'webservice');
  9036. $table->data[] = $row;
  9037. /// 10. Test the service
  9038. $row = array();
  9039. $url = new moodle_url("/admin/webservice/testclient.php");
  9040. $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
  9041. array('href' => $url));
  9042. $row[1] = "";
  9043. $row[2] = get_string('testwithtestclientdescription', 'webservice');
  9044. $table->data[] = $row;
  9045. $return .= html_writer::table($table);
  9046. /// Users as clients with token
  9047. $return .= $brtag . $brtag . $brtag;
  9048. $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
  9049. $table = new html_table();
  9050. $table->head = array(get_string('step', 'webservice'), get_string('status'),
  9051. get_string('description'));
  9052. $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
  9053. $table->id = 'userasclients';
  9054. $table->attributes['class'] = 'admintable wsoverview generaltable';
  9055. $table->data = array();
  9056. $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
  9057. $brtag . $brtag;
  9058. /// 1. Enable Web Services
  9059. $row = array();
  9060. $url = new moodle_url("/admin/search.php?query=enablewebservices");
  9061. $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
  9062. array('href' => $url));
  9063. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9064. if ($CFG->enablewebservices) {
  9065. $status = get_string('yes');
  9066. }
  9067. $row[1] = $status;
  9068. $row[2] = get_string('enablewsdescription', 'webservice');
  9069. $table->data[] = $row;
  9070. /// 2. Enable protocols
  9071. $row = array();
  9072. $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
  9073. $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
  9074. array('href' => $url));
  9075. $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
  9076. //retrieve activated protocol
  9077. $active_protocols = empty($CFG->webserviceprotocols) ?
  9078. array() : explode(',', $CFG->webserviceprotocols);
  9079. if (!empty($active_protocols)) {
  9080. $status = "";
  9081. foreach ($active_protocols as $protocol) {
  9082. $status .= $protocol . $brtag;
  9083. }
  9084. }
  9085. $row[1] = $status;
  9086. $row[2] = get_string('enableprotocolsdescription', 'webservice');
  9087. $table->data[] = $row;
  9088. /// 3. Select a web service
  9089. $row = array();
  9090. $url = new moodle_url("/admin/settings.php?section=externalservices");
  9091. $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
  9092. array('href' => $url));
  9093. $row[1] = "";
  9094. $row[2] = get_string('createserviceforusersdescription', 'webservice');
  9095. $table->data[] = $row;
  9096. /// 4. Add functions
  9097. $row = array();
  9098. $url = new moodle_url("/admin/settings.php?section=externalservices");
  9099. $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
  9100. array('href' => $url));
  9101. $row[1] = "";
  9102. $row[2] = get_string('addfunctionsdescription', 'webservice');
  9103. $table->data[] = $row;
  9104. /// 5. Add capability to users
  9105. $row = array();
  9106. $url = new moodle_url("/admin/roles/check.php?contextid=1");
  9107. $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
  9108. array('href' => $url));
  9109. $row[1] = "";
  9110. $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
  9111. $table->data[] = $row;
  9112. /// 6. Test the service
  9113. $row = array();
  9114. $url = new moodle_url("/admin/webservice/testclient.php");
  9115. $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
  9116. array('href' => $url));
  9117. $row[1] = "";
  9118. $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
  9119. $table->data[] = $row;
  9120. $return .= html_writer::table($table);
  9121. return highlight($query, $return);
  9122. }
  9123. }
  9124. /**
  9125. * Special class for web service protocol administration.
  9126. *
  9127. * @author Petr Skoda (skodak)
  9128. */
  9129. class admin_setting_managewebserviceprotocols extends admin_setting {
  9130. /**
  9131. * Calls parent::__construct with specific arguments
  9132. */
  9133. public function __construct() {
  9134. $this->nosave = true;
  9135. parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
  9136. }
  9137. /**
  9138. * Always returns true, does nothing
  9139. *
  9140. * @return true
  9141. */
  9142. public function get_setting() {
  9143. return true;
  9144. }
  9145. /**
  9146. * Always returns true, does nothing
  9147. *
  9148. * @return true
  9149. */
  9150. public function get_defaultsetting() {
  9151. return true;
  9152. }
  9153. /**
  9154. * Always returns '', does not write anything
  9155. *
  9156. * @return string Always returns ''
  9157. */
  9158. public function write_setting($data) {
  9159. // do not write any setting
  9160. return '';
  9161. }
  9162. /**
  9163. * Checks if $query is one of the available webservices
  9164. *
  9165. * @param string $query The string to search for
  9166. * @return bool Returns true if found, false if not
  9167. */
  9168. public function is_related($query) {
  9169. if (parent::is_related($query)) {
  9170. return true;
  9171. }
  9172. $protocols = core_component::get_plugin_list('webservice');
  9173. foreach ($protocols as $protocol=>$location) {
  9174. if (strpos($protocol, $query) !== false) {
  9175. return true;
  9176. }
  9177. $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
  9178. if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
  9179. return true;
  9180. }
  9181. }
  9182. return false;
  9183. }
  9184. /**
  9185. * Builds the XHTML to display the control
  9186. *
  9187. * @param string $data Unused
  9188. * @param string $query
  9189. * @return string
  9190. */
  9191. public function output_html($data, $query='') {
  9192. global $CFG, $OUTPUT;
  9193. // display strings
  9194. $stradministration = get_string('administration');
  9195. $strsettings = get_string('settings');
  9196. $stredit = get_string('edit');
  9197. $strprotocol = get_string('protocol', 'webservice');
  9198. $strenable = get_string('enable');
  9199. $strdisable = get_string('disable');
  9200. $strversion = get_string('version');
  9201. $protocols_available = core_component::get_plugin_list('webservice');
  9202. $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
  9203. ksort($protocols_available);
  9204. foreach ($active_protocols as $key=>$protocol) {
  9205. if (empty($protocols_available[$protocol])) {
  9206. unset($active_protocols[$key]);
  9207. }
  9208. }
  9209. $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
  9210. $return .= $OUTPUT->box_start('generalbox webservicesui');
  9211. $table = new html_table();
  9212. $table->head = array($strprotocol, $strversion, $strenable, $strsettings);
  9213. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  9214. $table->id = 'webserviceprotocols';
  9215. $table->attributes['class'] = 'admintable generaltable';
  9216. $table->data = array();
  9217. // iterate through auth plugins and add to the display table
  9218. $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
  9219. foreach ($protocols_available as $protocol => $location) {
  9220. $name = get_string('pluginname', 'webservice_'.$protocol);
  9221. $plugin = new stdClass();
  9222. if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
  9223. include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
  9224. }
  9225. $version = isset($plugin->version) ? $plugin->version : '';
  9226. // hide/show link
  9227. if (in_array($protocol, $active_protocols)) {
  9228. $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
  9229. $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
  9230. $displayname = "<span>$name</span>";
  9231. } else {
  9232. $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
  9233. $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
  9234. $displayname = "<span class=\"dimmed_text\">$name</span>";
  9235. }
  9236. // settings link
  9237. if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
  9238. $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
  9239. } else {
  9240. $settings = '';
  9241. }
  9242. // add a row to the table
  9243. $table->data[] = array($displayname, $version, $hideshow, $settings);
  9244. }
  9245. $return .= html_writer::table($table);
  9246. $return .= get_string('configwebserviceplugins', 'webservice');
  9247. $return .= $OUTPUT->box_end();
  9248. return highlight($query, $return);
  9249. }
  9250. }
  9251. /**
  9252. * Colour picker
  9253. *
  9254. * @copyright 2010 Sam Hemelryk
  9255. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9256. */
  9257. class admin_setting_configcolourpicker extends admin_setting {
  9258. /**
  9259. * Information for previewing the colour
  9260. *
  9261. * @var array|null
  9262. */
  9263. protected $previewconfig = null;
  9264. /**
  9265. * Use default when empty.
  9266. */
  9267. protected $usedefaultwhenempty = true;
  9268. /**
  9269. *
  9270. * @param string $name
  9271. * @param string $visiblename
  9272. * @param string $description
  9273. * @param string $defaultsetting
  9274. * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
  9275. */
  9276. public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null,
  9277. $usedefaultwhenempty = true) {
  9278. $this->previewconfig = $previewconfig;
  9279. $this->usedefaultwhenempty = $usedefaultwhenempty;
  9280. parent::__construct($name, $visiblename, $description, $defaultsetting);
  9281. $this->set_force_ltr(true);
  9282. }
  9283. /**
  9284. * Return the setting
  9285. *
  9286. * @return mixed returns config if successful else null
  9287. */
  9288. public function get_setting() {
  9289. return $this->config_read($this->name);
  9290. }
  9291. /**
  9292. * Saves the setting
  9293. *
  9294. * @param string $data
  9295. * @return bool
  9296. */
  9297. public function write_setting($data) {
  9298. $data = $this->validate($data);
  9299. if ($data === false) {
  9300. return get_string('validateerror', 'admin');
  9301. }
  9302. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  9303. }
  9304. /**
  9305. * Validates the colour that was entered by the user
  9306. *
  9307. * @param string $data
  9308. * @return string|false
  9309. */
  9310. protected function validate($data) {
  9311. /**
  9312. * List of valid HTML colour names
  9313. *
  9314. * @var array
  9315. */
  9316. $colornames = array(
  9317. 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
  9318. 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
  9319. 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
  9320. 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
  9321. 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
  9322. 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
  9323. 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
  9324. 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
  9325. 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
  9326. 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
  9327. 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
  9328. 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
  9329. 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
  9330. 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
  9331. 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
  9332. 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
  9333. 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
  9334. 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
  9335. 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
  9336. 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
  9337. 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
  9338. 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
  9339. 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
  9340. 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
  9341. 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
  9342. 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
  9343. 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
  9344. 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
  9345. 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
  9346. 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
  9347. 'whitesmoke', 'yellow', 'yellowgreen'
  9348. );
  9349. if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
  9350. if (strpos($data, '#')!==0) {
  9351. $data = '#'.$data;
  9352. }
  9353. return $data;
  9354. } else if (in_array(strtolower($data), $colornames)) {
  9355. return $data;
  9356. } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
  9357. return $data;
  9358. } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
  9359. return $data;
  9360. } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
  9361. return $data;
  9362. } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
  9363. return $data;
  9364. } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
  9365. return $data;
  9366. } else if (empty($data)) {
  9367. if ($this->usedefaultwhenempty){
  9368. return $this->defaultsetting;
  9369. } else {
  9370. return '';
  9371. }
  9372. } else {
  9373. return false;
  9374. }
  9375. }
  9376. /**
  9377. * Generates the HTML for the setting
  9378. *
  9379. * @global moodle_page $PAGE
  9380. * @global core_renderer $OUTPUT
  9381. * @param string $data
  9382. * @param string $query
  9383. */
  9384. public function output_html($data, $query = '') {
  9385. global $PAGE, $OUTPUT;
  9386. $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
  9387. $context = (object) [
  9388. 'id' => $this->get_id(),
  9389. 'name' => $this->get_full_name(),
  9390. 'value' => $data,
  9391. 'icon' => $icon->export_for_template($OUTPUT),
  9392. 'haspreviewconfig' => !empty($this->previewconfig),
  9393. 'forceltr' => $this->get_force_ltr(),
  9394. 'readonly' => $this->is_readonly(),
  9395. ];
  9396. $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
  9397. $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
  9398. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
  9399. $this->get_defaultsetting(), $query);
  9400. }
  9401. }
  9402. /**
  9403. * Class used for uploading of one file into file storage,
  9404. * the file name is stored in config table.
  9405. *
  9406. * Please note you need to implement your own '_pluginfile' callback function,
  9407. * this setting only stores the file, it does not deal with file serving.
  9408. *
  9409. * @copyright 2013 Petr Skoda {@link http://skodak.org}
  9410. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9411. */
  9412. class admin_setting_configstoredfile extends admin_setting {
  9413. /** @var array file area options - should be one file only */
  9414. protected $options;
  9415. /** @var string name of the file area */
  9416. protected $filearea;
  9417. /** @var int intemid */
  9418. protected $itemid;
  9419. /** @var string used for detection of changes */
  9420. protected $oldhashes;
  9421. /**
  9422. * Create new stored file setting.
  9423. *
  9424. * @param string $name low level setting name
  9425. * @param string $visiblename human readable setting name
  9426. * @param string $description description of setting
  9427. * @param mixed $filearea file area for file storage
  9428. * @param int $itemid itemid for file storage
  9429. * @param array $options file area options
  9430. */
  9431. public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
  9432. parent::__construct($name, $visiblename, $description, '');
  9433. $this->filearea = $filearea;
  9434. $this->itemid = $itemid;
  9435. $this->options = (array)$options;
  9436. $this->customcontrol = true;
  9437. }
  9438. /**
  9439. * Applies defaults and returns all options.
  9440. * @return array
  9441. */
  9442. protected function get_options() {
  9443. global $CFG;
  9444. require_once("$CFG->libdir/filelib.php");
  9445. require_once("$CFG->dirroot/repository/lib.php");
  9446. $defaults = array(
  9447. 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
  9448. 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
  9449. 'context' => context_system::instance());
  9450. foreach($this->options as $k => $v) {
  9451. $defaults[$k] = $v;
  9452. }
  9453. return $defaults;
  9454. }
  9455. public function get_setting() {
  9456. return $this->config_read($this->name);
  9457. }
  9458. public function write_setting($data) {
  9459. global $USER;
  9460. // Let's not deal with validation here, this is for admins only.
  9461. $current = $this->get_setting();
  9462. if (empty($data) && $current === null) {
  9463. // This will be the case when applying default settings (installation).
  9464. return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
  9465. } else if (!is_number($data)) {
  9466. // Draft item id is expected here!
  9467. return get_string('errorsetting', 'admin');
  9468. }
  9469. $options = $this->get_options();
  9470. $fs = get_file_storage();
  9471. $component = is_null($this->plugin) ? 'core' : $this->plugin;
  9472. $this->oldhashes = null;
  9473. if ($current) {
  9474. $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
  9475. if ($file = $fs->get_file_by_hash($hash)) {
  9476. $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
  9477. }
  9478. unset($file);
  9479. }
  9480. if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
  9481. // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
  9482. // But we can safely ignore that if the destination area is empty, so that the user is not prompt
  9483. // with an error because the draft area does not exist, as he did not use it.
  9484. $usercontext = context_user::instance($USER->id);
  9485. if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
  9486. return get_string('errorsetting', 'admin');
  9487. }
  9488. }
  9489. file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
  9490. $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
  9491. $filepath = '';
  9492. if ($files) {
  9493. /** @var stored_file $file */
  9494. $file = reset($files);
  9495. $filepath = $file->get_filepath().$file->get_filename();
  9496. }
  9497. return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
  9498. }
  9499. public function post_write_settings($original) {
  9500. $options = $this->get_options();
  9501. $fs = get_file_storage();
  9502. $component = is_null($this->plugin) ? 'core' : $this->plugin;
  9503. $current = $this->get_setting();
  9504. $newhashes = null;
  9505. if ($current) {
  9506. $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
  9507. if ($file = $fs->get_file_by_hash($hash)) {
  9508. $newhashes = $file->get_contenthash().$file->get_pathnamehash();
  9509. }
  9510. unset($file);
  9511. }
  9512. if ($this->oldhashes === $newhashes) {
  9513. $this->oldhashes = null;
  9514. return false;
  9515. }
  9516. $this->oldhashes = null;
  9517. $callbackfunction = $this->updatedcallback;
  9518. if (!empty($callbackfunction) and function_exists($callbackfunction)) {
  9519. $callbackfunction($this->get_full_name());
  9520. }
  9521. return true;
  9522. }
  9523. public function output_html($data, $query = '') {
  9524. global $PAGE, $CFG;
  9525. $options = $this->get_options();
  9526. $id = $this->get_id();
  9527. $elname = $this->get_full_name();
  9528. $draftitemid = file_get_submitted_draft_itemid($elname);
  9529. $component = is_null($this->plugin) ? 'core' : $this->plugin;
  9530. file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
  9531. // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
  9532. require_once("$CFG->dirroot/lib/form/filemanager.php");
  9533. $fmoptions = new stdClass();
  9534. $fmoptions->mainfile = $options['mainfile'];
  9535. $fmoptions->maxbytes = $options['maxbytes'];
  9536. $fmoptions->maxfiles = $options['maxfiles'];
  9537. $fmoptions->client_id = uniqid();
  9538. $fmoptions->itemid = $draftitemid;
  9539. $fmoptions->subdirs = $options['subdirs'];
  9540. $fmoptions->target = $id;
  9541. $fmoptions->accepted_types = $options['accepted_types'];
  9542. $fmoptions->return_types = $options['return_types'];
  9543. $fmoptions->context = $options['context'];
  9544. $fmoptions->areamaxbytes = $options['areamaxbytes'];
  9545. $fm = new form_filemanager($fmoptions);
  9546. $output = $PAGE->get_renderer('core', 'files');
  9547. $html = $output->render($fm);
  9548. $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
  9549. $html .= '<input value="" id="'.$id.'" type="hidden" />';
  9550. return format_admin_setting($this, $this->visiblename,
  9551. '<div class="form-filemanager" data-fieldtype="filemanager">'.$html.'</div>',
  9552. $this->description, true, '', '', $query);
  9553. }
  9554. }
  9555. /**
  9556. * Administration interface for user specified regular expressions for device detection.
  9557. *
  9558. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9559. */
  9560. class admin_setting_devicedetectregex extends admin_setting {
  9561. /**
  9562. * Calls parent::__construct with specific args
  9563. *
  9564. * @param string $name
  9565. * @param string $visiblename
  9566. * @param string $description
  9567. * @param mixed $defaultsetting
  9568. */
  9569. public function __construct($name, $visiblename, $description, $defaultsetting = '') {
  9570. global $CFG;
  9571. parent::__construct($name, $visiblename, $description, $defaultsetting);
  9572. }
  9573. /**
  9574. * Return the current setting(s)
  9575. *
  9576. * @return array Current settings array
  9577. */
  9578. public function get_setting() {
  9579. global $CFG;
  9580. $config = $this->config_read($this->name);
  9581. if (is_null($config)) {
  9582. return null;
  9583. }
  9584. return $this->prepare_form_data($config);
  9585. }
  9586. /**
  9587. * Save selected settings
  9588. *
  9589. * @param array $data Array of settings to save
  9590. * @return bool
  9591. */
  9592. public function write_setting($data) {
  9593. if (empty($data)) {
  9594. $data = array();
  9595. }
  9596. if ($this->config_write($this->name, $this->process_form_data($data))) {
  9597. return ''; // success
  9598. } else {
  9599. return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
  9600. }
  9601. }
  9602. /**
  9603. * Return XHTML field(s) for regexes
  9604. *
  9605. * @param array $data Array of options to set in HTML
  9606. * @return string XHTML string for the fields and wrapping div(s)
  9607. */
  9608. public function output_html($data, $query='') {
  9609. global $OUTPUT;
  9610. $context = (object) [
  9611. 'expressions' => [],
  9612. 'name' => $this->get_full_name()
  9613. ];
  9614. if (empty($data)) {
  9615. $looplimit = 1;
  9616. } else {
  9617. $looplimit = (count($data)/2)+1;
  9618. }
  9619. for ($i=0; $i<$looplimit; $i++) {
  9620. $expressionname = 'expression'.$i;
  9621. if (!empty($data[$expressionname])){
  9622. $expression = $data[$expressionname];
  9623. } else {
  9624. $expression = '';
  9625. }
  9626. $valuename = 'value'.$i;
  9627. if (!empty($data[$valuename])){
  9628. $value = $data[$valuename];
  9629. } else {
  9630. $value= '';
  9631. }
  9632. $context->expressions[] = [
  9633. 'index' => $i,
  9634. 'expression' => $expression,
  9635. 'value' => $value
  9636. ];
  9637. }
  9638. $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
  9639. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
  9640. }
  9641. /**
  9642. * Converts the string of regexes
  9643. *
  9644. * @see self::process_form_data()
  9645. * @param $regexes string of regexes
  9646. * @return array of form fields and their values
  9647. */
  9648. protected function prepare_form_data($regexes) {
  9649. $regexes = json_decode($regexes);
  9650. $form = array();
  9651. $i = 0;
  9652. foreach ($regexes as $value => $regex) {
  9653. $expressionname = 'expression'.$i;
  9654. $valuename = 'value'.$i;
  9655. $form[$expressionname] = $regex;
  9656. $form[$valuename] = $value;
  9657. $i++;
  9658. }
  9659. return $form;
  9660. }
  9661. /**
  9662. * Converts the data from admin settings form into a string of regexes
  9663. *
  9664. * @see self::prepare_form_data()
  9665. * @param array $data array of admin form fields and values
  9666. * @return false|string of regexes
  9667. */
  9668. protected function process_form_data(array $form) {
  9669. $count = count($form); // number of form field values
  9670. if ($count % 2) {
  9671. // we must get five fields per expression
  9672. return false;
  9673. }
  9674. $regexes = array();
  9675. for ($i = 0; $i < $count / 2; $i++) {
  9676. $expressionname = "expression".$i;
  9677. $valuename = "value".$i;
  9678. $expression = trim($form['expression'.$i]);
  9679. $value = trim($form['value'.$i]);
  9680. if (empty($expression)){
  9681. continue;
  9682. }
  9683. $regexes[$value] = $expression;
  9684. }
  9685. $regexes = json_encode($regexes);
  9686. return $regexes;
  9687. }
  9688. }
  9689. /**
  9690. * Multiselect for current modules
  9691. *
  9692. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9693. */
  9694. class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
  9695. private $excludesystem;
  9696. /**
  9697. * Calls parent::__construct - note array $choices is not required
  9698. *
  9699. * @param string $name setting name
  9700. * @param string $visiblename localised setting name
  9701. * @param string $description setting description
  9702. * @param array $defaultsetting a plain array of default module ids
  9703. * @param bool $excludesystem If true, excludes modules with 'system' archetype
  9704. */
  9705. public function __construct($name, $visiblename, $description, $defaultsetting = array(),
  9706. $excludesystem = true) {
  9707. parent::__construct($name, $visiblename, $description, $defaultsetting, null);
  9708. $this->excludesystem = $excludesystem;
  9709. }
  9710. /**
  9711. * Loads an array of current module choices
  9712. *
  9713. * @return bool always return true
  9714. */
  9715. public function load_choices() {
  9716. if (is_array($this->choices)) {
  9717. return true;
  9718. }
  9719. $this->choices = array();
  9720. global $CFG, $DB;
  9721. $records = $DB->get_records('modules', array('visible'=>1), 'name');
  9722. foreach ($records as $record) {
  9723. // Exclude modules if the code doesn't exist
  9724. if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
  9725. // Also exclude system modules (if specified)
  9726. if (!($this->excludesystem &&
  9727. plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
  9728. MOD_ARCHETYPE_SYSTEM)) {
  9729. $this->choices[$record->id] = $record->name;
  9730. }
  9731. }
  9732. }
  9733. return true;
  9734. }
  9735. }
  9736. /**
  9737. * Admin setting to show if a php extension is enabled or not.
  9738. *
  9739. * @copyright 2013 Damyon Wiese
  9740. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9741. */
  9742. class admin_setting_php_extension_enabled extends admin_setting {
  9743. /** @var string The name of the extension to check for */
  9744. private $extension;
  9745. /**
  9746. * Calls parent::__construct with specific arguments
  9747. */
  9748. public function __construct($name, $visiblename, $description, $extension) {
  9749. $this->extension = $extension;
  9750. $this->nosave = true;
  9751. parent::__construct($name, $visiblename, $description, '');
  9752. }
  9753. /**
  9754. * Always returns true, does nothing
  9755. *
  9756. * @return true
  9757. */
  9758. public function get_setting() {
  9759. return true;
  9760. }
  9761. /**
  9762. * Always returns true, does nothing
  9763. *
  9764. * @return true
  9765. */
  9766. public function get_defaultsetting() {
  9767. return true;
  9768. }
  9769. /**
  9770. * Always returns '', does not write anything
  9771. *
  9772. * @return string Always returns ''
  9773. */
  9774. public function write_setting($data) {
  9775. // Do not write any setting.
  9776. return '';
  9777. }
  9778. /**
  9779. * Outputs the html for this setting.
  9780. * @return string Returns an XHTML string
  9781. */
  9782. public function output_html($data, $query='') {
  9783. global $OUTPUT;
  9784. $o = '';
  9785. if (!extension_loaded($this->extension)) {
  9786. $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description;
  9787. $o .= format_admin_setting($this, $this->visiblename, $warning);
  9788. }
  9789. return $o;
  9790. }
  9791. }
  9792. /**
  9793. * Server timezone setting.
  9794. *
  9795. * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
  9796. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9797. * @author Petr Skoda <petr.skoda@totaralms.com>
  9798. */
  9799. class admin_setting_servertimezone extends admin_setting_configselect {
  9800. /**
  9801. * Constructor.
  9802. */
  9803. public function __construct() {
  9804. $default = core_date::get_default_php_timezone();
  9805. if ($default === 'UTC') {
  9806. // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
  9807. $default = 'Europe/London';
  9808. }
  9809. parent::__construct('timezone',
  9810. new lang_string('timezone', 'core_admin'),
  9811. new lang_string('configtimezone', 'core_admin'), $default, null);
  9812. }
  9813. /**
  9814. * Lazy load timezone options.
  9815. * @return bool true if loaded, false if error
  9816. */
  9817. public function load_choices() {
  9818. global $CFG;
  9819. if (is_array($this->choices)) {
  9820. return true;
  9821. }
  9822. $current = isset($CFG->timezone) ? $CFG->timezone : null;
  9823. $this->choices = core_date::get_list_of_timezones($current, false);
  9824. if ($current == 99) {
  9825. // Do not show 99 unless it is current value, we want to get rid of it over time.
  9826. $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
  9827. core_date::get_default_php_timezone());
  9828. }
  9829. return true;
  9830. }
  9831. }
  9832. /**
  9833. * Forced user timezone setting.
  9834. *
  9835. * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
  9836. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9837. * @author Petr Skoda <petr.skoda@totaralms.com>
  9838. */
  9839. class admin_setting_forcetimezone extends admin_setting_configselect {
  9840. /**
  9841. * Constructor.
  9842. */
  9843. public function __construct() {
  9844. parent::__construct('forcetimezone',
  9845. new lang_string('forcetimezone', 'core_admin'),
  9846. new lang_string('helpforcetimezone', 'core_admin'), '99', null);
  9847. }
  9848. /**
  9849. * Lazy load timezone options.
  9850. * @return bool true if loaded, false if error
  9851. */
  9852. public function load_choices() {
  9853. global $CFG;
  9854. if (is_array($this->choices)) {
  9855. return true;
  9856. }
  9857. $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
  9858. $this->choices = core_date::get_list_of_timezones($current, true);
  9859. $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
  9860. return true;
  9861. }
  9862. }
  9863. /**
  9864. * Search setup steps info.
  9865. *
  9866. * @package core
  9867. * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
  9868. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9869. */
  9870. class admin_setting_searchsetupinfo extends admin_setting {
  9871. /**
  9872. * Calls parent::__construct with specific arguments
  9873. */
  9874. public function __construct() {
  9875. $this->nosave = true;
  9876. parent::__construct('searchsetupinfo', '', '', '');
  9877. }
  9878. /**
  9879. * Always returns true, does nothing
  9880. *
  9881. * @return true
  9882. */
  9883. public function get_setting() {
  9884. return true;
  9885. }
  9886. /**
  9887. * Always returns true, does nothing
  9888. *
  9889. * @return true
  9890. */
  9891. public function get_defaultsetting() {
  9892. return true;
  9893. }
  9894. /**
  9895. * Always returns '', does not write anything
  9896. *
  9897. * @param array $data
  9898. * @return string Always returns ''
  9899. */
  9900. public function write_setting($data) {
  9901. // Do not write any setting.
  9902. return '';
  9903. }
  9904. /**
  9905. * Builds the HTML to display the control
  9906. *
  9907. * @param string $data Unused
  9908. * @param string $query
  9909. * @return string
  9910. */
  9911. public function output_html($data, $query='') {
  9912. global $CFG, $OUTPUT, $ADMIN;
  9913. $return = '';
  9914. $brtag = html_writer::empty_tag('br');
  9915. $searchareas = \core_search\manager::get_search_areas_list();
  9916. $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
  9917. $anyindexed = false;
  9918. foreach ($searchareas as $areaid => $searcharea) {
  9919. list($componentname, $varname) = $searcharea->get_config_var_name();
  9920. if (get_config($componentname, $varname . '_indexingstart')) {
  9921. $anyindexed = true;
  9922. break;
  9923. }
  9924. }
  9925. $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
  9926. $table = new html_table();
  9927. $table->head = array(get_string('step', 'search'), get_string('status'));
  9928. $table->colclasses = array('leftalign step', 'leftalign status');
  9929. $table->id = 'searchsetup';
  9930. $table->attributes['class'] = 'admintable generaltable';
  9931. $table->data = array();
  9932. $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
  9933. // Select a search engine.
  9934. $row = array();
  9935. $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
  9936. $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
  9937. array('href' => $url));
  9938. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9939. if (!empty($CFG->searchengine)) {
  9940. $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
  9941. array('class' => 'badge badge-success'));
  9942. }
  9943. $row[1] = $status;
  9944. $table->data[] = $row;
  9945. // Available areas.
  9946. $row = array();
  9947. $url = new moodle_url('/admin/searchareas.php');
  9948. $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
  9949. array('href' => $url));
  9950. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9951. if ($anyenabled) {
  9952. $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
  9953. }
  9954. $row[1] = $status;
  9955. $table->data[] = $row;
  9956. // Setup search engine.
  9957. $row = array();
  9958. if (empty($CFG->searchengine)) {
  9959. $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
  9960. $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9961. } else {
  9962. if ($ADMIN->locate('search' . $CFG->searchengine)) {
  9963. $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
  9964. $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin'));
  9965. } else {
  9966. $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin');
  9967. }
  9968. // Check the engine status.
  9969. $searchengine = \core_search\manager::search_engine_instance();
  9970. try {
  9971. $serverstatus = $searchengine->is_server_ready();
  9972. } catch (\moodle_exception $e) {
  9973. $serverstatus = $e->getMessage();
  9974. }
  9975. if ($serverstatus === true) {
  9976. $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
  9977. } else {
  9978. $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger'));
  9979. }
  9980. $row[1] = $status;
  9981. }
  9982. $table->data[] = $row;
  9983. // Indexed data.
  9984. $row = array();
  9985. $url = new moodle_url('/admin/searchareas.php');
  9986. $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
  9987. if ($anyindexed) {
  9988. $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
  9989. } else {
  9990. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9991. }
  9992. $row[1] = $status;
  9993. $table->data[] = $row;
  9994. // Enable global search.
  9995. $row = array();
  9996. $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
  9997. $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
  9998. array('href' => $url));
  9999. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  10000. if (\core_search\manager::is_global_search_enabled()) {
  10001. $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
  10002. }
  10003. $row[1] = $status;
  10004. $table->data[] = $row;
  10005. $return .= html_writer::table($table);
  10006. return highlight($query, $return);
  10007. }
  10008. }
  10009. /**
  10010. * Used to validate the contents of SCSS code and ensuring they are parsable.
  10011. *
  10012. * It does not attempt to detect undefined SCSS variables because it is designed
  10013. * to be used without knowledge of other config/scss included.
  10014. *
  10015. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  10016. * @copyright 2016 Dan Poltawski <dan@moodle.com>
  10017. */
  10018. class admin_setting_scsscode extends admin_setting_configtextarea {
  10019. /**
  10020. * Validate the contents of the SCSS to ensure its parsable. Does not
  10021. * attempt to detect undefined scss variables.
  10022. *
  10023. * @param string $data The scss code from text field.
  10024. * @return mixed bool true for success or string:error on failure.
  10025. */
  10026. public function validate($data) {
  10027. if (empty($data)) {
  10028. return true;
  10029. }
  10030. $scss = new core_scss();
  10031. try {
  10032. $scss->compile($data);
  10033. } catch (ScssPhp\ScssPhp\Exception\ParserException $e) {
  10034. return get_string('scssinvalid', 'admin', $e->getMessage());
  10035. } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) {
  10036. // Silently ignore this - it could be a scss variable defined from somewhere
  10037. // else which we are not examining here.
  10038. return true;
  10039. }
  10040. return true;
  10041. }
  10042. }
  10043. /**
  10044. * Administration setting to define a list of file types.
  10045. *
  10046. * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
  10047. * @copyright 2017 David Mudrák <david@moodle.com>
  10048. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  10049. */
  10050. class admin_setting_filetypes extends admin_setting_configtext {
  10051. /** @var array Allow selection from these file types only. */
  10052. protected $onlytypes = [];
  10053. /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
  10054. protected $allowall = true;
  10055. /** @var core_form\filetypes_util instance to use as a helper. */
  10056. protected $util = null;
  10057. /**
  10058. * Constructor.
  10059. *
  10060. * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
  10061. * @param string $visiblename Localised label of the setting
  10062. * @param string $description Localised description of the setting
  10063. * @param string $defaultsetting Default setting value.
  10064. * @param array $options Setting widget options, an array with optional keys:
  10065. * 'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
  10066. * 'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
  10067. */
  10068. public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
  10069. parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
  10070. if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
  10071. $this->onlytypes = $options['onlytypes'];
  10072. }
  10073. if (!$this->onlytypes && array_key_exists('allowall', $options)) {
  10074. $this->allowall = (bool)$options['allowall'];
  10075. }
  10076. $this->util = new \core_form\filetypes_util();
  10077. }
  10078. /**
  10079. * Normalize the user's input and write it to the database as comma separated list.
  10080. *
  10081. * Comma separated list as a text representation of the array was chosen to
  10082. * make this compatible with how the $CFG->courseoverviewfilesext values are stored.
  10083. *
  10084. * @param string $data Value submitted by the admin.
  10085. * @return string Epty string if all good, error message otherwise.
  10086. */
  10087. public function write_setting($data) {
  10088. return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
  10089. }
  10090. /**
  10091. * Validate data before storage
  10092. *
  10093. * @param string $data The setting values provided by the admin
  10094. * @return bool|string True if ok, the string if error found
  10095. */
  10096. public function validate($data) {
  10097. $parentcheck = parent::validate($data);
  10098. if ($parentcheck !== true) {
  10099. return $parentcheck;
  10100. }
  10101. // Check for unknown file types.
  10102. if ($unknown = $this->util->get_unknown_file_types($data)) {
  10103. return get_string('filetypesunknown', 'core_form', implode(', ', $unknown));
  10104. }
  10105. // Check for disallowed file types.
  10106. if ($notlisted = $this->util->get_not_listed($data, $this->onlytypes)) {
  10107. return get_string('filetypesnotallowed', 'core_form', implode(', ', $notlisted));
  10108. }
  10109. return true;
  10110. }
  10111. /**
  10112. * Return an HTML string for the setting element.
  10113. *
  10114. * @param string $data The current setting value
  10115. * @param string $query Admin search query to be highlighted
  10116. * @return string HTML to be displayed
  10117. */
  10118. public function output_html($data, $query='') {
  10119. global $OUTPUT, $PAGE;
  10120. $default = $this->get_defaultsetting();
  10121. $context = (object) [
  10122. 'id' => $this->get_id(),
  10123. 'name' => $this->get_full_name(),
  10124. 'value' => $data,
  10125. 'descriptions' => $this->util->describe_file_types($data),
  10126. ];
  10127. $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
  10128. $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
  10129. $this->get_id(),
  10130. $this->visiblename->out(),
  10131. $this->onlytypes,
  10132. $this->allowall,
  10133. ]);
  10134. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  10135. }
  10136. /**
  10137. * Should the values be always displayed in LTR mode?
  10138. *
  10139. * We always return true here because these values are not RTL compatible.
  10140. *
  10141. * @return bool True because these values are not RTL compatible.
  10142. */
  10143. public function get_force_ltr() {
  10144. return true;
  10145. }
  10146. }
  10147. /**
  10148. * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
  10149. *
  10150. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  10151. * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
  10152. */
  10153. class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
  10154. /**
  10155. * Constructor.
  10156. *
  10157. * @param string $name
  10158. * @param string $visiblename
  10159. * @param string $description
  10160. * @param mixed $defaultsetting string or array
  10161. * @param mixed $paramtype
  10162. * @param string $cols
  10163. * @param string $rows
  10164. */
  10165. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
  10166. $cols = '60', $rows = '8') {
  10167. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
  10168. // Pre-set force LTR to false.
  10169. $this->set_force_ltr(false);
  10170. }
  10171. /**
  10172. * Validate the content and format of the age of digital consent map to ensure it is parsable.
  10173. *
  10174. * @param string $data The age of digital consent map from text field.
  10175. * @return mixed bool true for success or string:error on failure.
  10176. */
  10177. public function validate($data) {
  10178. if (empty($data)) {
  10179. return true;
  10180. }
  10181. try {
  10182. \core_auth\digital_consent::parse_age_digital_consent_map($data);
  10183. } catch (\moodle_exception $e) {
  10184. return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
  10185. }
  10186. return true;
  10187. }
  10188. }
  10189. /**
  10190. * Selection of plugins that can work as site policy handlers
  10191. *
  10192. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  10193. * @copyright 2018 Marina Glancy
  10194. */
  10195. class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
  10196. /**
  10197. * Constructor
  10198. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
  10199. * for ones in config_plugins.
  10200. * @param string $visiblename localised
  10201. * @param string $description long localised info
  10202. * @param string $defaultsetting
  10203. */
  10204. public function __construct($name, $visiblename, $description, $defaultsetting = '') {
  10205. parent::__construct($name, $visiblename, $description, $defaultsetting, null);
  10206. }
  10207. /**
  10208. * Lazy-load the available choices for the select box
  10209. */
  10210. public function load_choices() {
  10211. if (during_initial_install()) {
  10212. return false;
  10213. }
  10214. if (is_array($this->choices)) {
  10215. return true;
  10216. }
  10217. $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
  10218. $manager = new \core_privacy\local\sitepolicy\manager();
  10219. $plugins = $manager->get_all_handlers();
  10220. foreach ($plugins as $pname => $unused) {
  10221. $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
  10222. ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
  10223. }
  10224. return true;
  10225. }
  10226. }
  10227. /**
  10228. * Used to validate theme presets code and ensuring they compile well.
  10229. *
  10230. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  10231. * @copyright 2019 Bas Brands <bas@moodle.com>
  10232. */
  10233. class admin_setting_configthemepreset extends admin_setting_configselect {
  10234. /** @var string The name of the theme to check for */
  10235. private $themename;
  10236. /**
  10237. * Constructor
  10238. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  10239. * or 'myplugin/mysetting' for ones in config_plugins.
  10240. * @param string $visiblename localised
  10241. * @param string $description long localised info
  10242. * @param string|int $defaultsetting
  10243. * @param array $choices array of $value=>$label for each selection
  10244. * @param string $themename name of theme to check presets for.
  10245. */
  10246. public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
  10247. $this->themename = $themename;
  10248. parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
  10249. }
  10250. /**
  10251. * Write settings if validated
  10252. *
  10253. * @param string $data
  10254. * @return string
  10255. */
  10256. public function write_setting($data) {
  10257. $validated = $this->validate($data);
  10258. if ($validated !== true) {
  10259. return $validated;
  10260. }
  10261. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  10262. }
  10263. /**
  10264. * Validate the preset file to ensure its parsable.
  10265. *
  10266. * @param string $data The preset file chosen.
  10267. * @return mixed bool true for success or string:error on failure.
  10268. */
  10269. public function validate($data) {
  10270. if (in_array($data, ['default.scss', 'plain.scss'])) {
  10271. return true;
  10272. }
  10273. $fs = get_file_storage();
  10274. $theme = theme_config::load($this->themename);
  10275. $context = context_system::instance();
  10276. // If the preset has not changed there is no need to validate it.
  10277. if ($theme->settings->preset == $data) {
  10278. return true;
  10279. }
  10280. if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
  10281. // This operation uses a lot of resources.
  10282. raise_memory_limit(MEMORY_EXTRA);
  10283. core_php_time_limit::raise(300);
  10284. // TODO: MDL-62757 When changing anything in this method please do not forget to check
  10285. // if the get_css_content_from_scss() method in class theme_config needs updating too.
  10286. $compiler = new core_scss();
  10287. $compiler->prepend_raw_scss($theme->get_pre_scss_code());
  10288. $compiler->append_raw_scss($presetfile->get_content());
  10289. if ($scssproperties = $theme->get_scss_property()) {
  10290. $compiler->setImportPaths($scssproperties[0]);
  10291. }
  10292. $compiler->append_raw_scss($theme->get_extra_scss_code());
  10293. try {
  10294. $compiler->to_css();
  10295. } catch (Exception $e) {
  10296. return get_string('invalidthemepreset', 'admin', $e->getMessage());
  10297. }
  10298. // Try to save memory.
  10299. $compiler = null;
  10300. unset($compiler);
  10301. }
  10302. return true;
  10303. }
  10304. }
  10305. /**
  10306. * Selection of plugins that can work as H5P libraries handlers
  10307. *
  10308. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  10309. * @copyright 2020 Sara Arjona <sara@moodle.com>
  10310. */
  10311. class admin_settings_h5plib_handler_select extends admin_setting_configselect {
  10312. /**
  10313. * Constructor
  10314. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
  10315. * for ones in config_plugins.
  10316. * @param string $visiblename localised
  10317. * @param string $description long localised info
  10318. * @param string $defaultsetting
  10319. */
  10320. public function __construct($name, $visiblename, $description, $defaultsetting = '') {
  10321. parent::__construct($name, $visiblename, $description, $defaultsetting, null);
  10322. }
  10323. /**
  10324. * Lazy-load the available choices for the select box
  10325. */
  10326. public function load_choices() {
  10327. if (during_initial_install()) {
  10328. return false;
  10329. }
  10330. if (is_array($this->choices)) {
  10331. return true;
  10332. }
  10333. $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
  10334. foreach ($this->choices as $name => $class) {
  10335. $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
  10336. ['name' => new lang_string('pluginname', $name), 'component' => $name]);
  10337. }
  10338. return true;
  10339. }
  10340. }