PageRenderTime 54ms CodeModel.GetById 16ms RepoModel.GetById 1ms app.codeStats 2ms

/lib/adminlib.php

http://github.com/moodle/moodle
PHP | 11409 lines | 6370 code | 1335 blank | 3704 comment | 974 complexity | 1ecf6e7c44876bcc34cd9175bd18b7f9 MD5 | raw file
Possible License(s): MIT, AGPL-3.0, MPL-2.0-no-copyleft-exception, LGPL-3.0, GPL-3.0, Apache-2.0, LGPL-2.1, BSD-3-Clause
  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. defined('MOODLE_INTERNAL') || die();
  104. /// Add libraries
  105. require_once($CFG->libdir.'/ddllib.php');
  106. require_once($CFG->libdir.'/xmlize.php');
  107. require_once($CFG->libdir.'/messagelib.php');
  108. define('INSECURE_DATAROOT_WARNING', 1);
  109. define('INSECURE_DATAROOT_ERROR', 2);
  110. /**
  111. * Automatically clean-up all plugin data and remove the plugin DB tables
  112. *
  113. * NOTE: do not call directly, use new /admin/plugins.php?uninstall=component instead!
  114. *
  115. * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
  116. * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
  117. * @uses global $OUTPUT to produce notices and other messages
  118. * @return void
  119. */
  120. function uninstall_plugin($type, $name) {
  121. global $CFG, $DB, $OUTPUT;
  122. // This may take a long time.
  123. core_php_time_limit::raise();
  124. // Recursively uninstall all subplugins first.
  125. $subplugintypes = core_component::get_plugin_types_with_subplugins();
  126. if (isset($subplugintypes[$type])) {
  127. $base = core_component::get_plugin_directory($type, $name);
  128. $subpluginsfile = "{$base}/db/subplugins.json";
  129. if (file_exists($subpluginsfile)) {
  130. $subplugins = (array) json_decode(file_get_contents($subpluginsfile))->plugintypes;
  131. } else if (file_exists("{$base}/db/subplugins.php")) {
  132. debugging('Use of subplugins.php has been deprecated. ' .
  133. 'Please update your plugin to provide a subplugins.json file instead.',
  134. DEBUG_DEVELOPER);
  135. $subplugins = [];
  136. include("{$base}/db/subplugins.php");
  137. }
  138. if (!empty($subplugins)) {
  139. foreach (array_keys($subplugins) as $subplugintype) {
  140. $instances = core_component::get_plugin_list($subplugintype);
  141. foreach ($instances as $subpluginname => $notusedpluginpath) {
  142. uninstall_plugin($subplugintype, $subpluginname);
  143. }
  144. }
  145. }
  146. }
  147. $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
  148. if ($type === 'mod') {
  149. $pluginname = $name; // eg. 'forum'
  150. if (get_string_manager()->string_exists('modulename', $component)) {
  151. $strpluginname = get_string('modulename', $component);
  152. } else {
  153. $strpluginname = $component;
  154. }
  155. } else {
  156. $pluginname = $component;
  157. if (get_string_manager()->string_exists('pluginname', $component)) {
  158. $strpluginname = get_string('pluginname', $component);
  159. } else {
  160. $strpluginname = $component;
  161. }
  162. }
  163. echo $OUTPUT->heading($pluginname);
  164. // Delete all tag areas, collections and instances associated with this plugin.
  165. core_tag_area::uninstall($component);
  166. // Custom plugin uninstall.
  167. $plugindirectory = core_component::get_plugin_directory($type, $name);
  168. $uninstalllib = $plugindirectory . '/db/uninstall.php';
  169. if (file_exists($uninstalllib)) {
  170. require_once($uninstalllib);
  171. $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
  172. if (function_exists($uninstallfunction)) {
  173. // Do not verify result, let plugin complain if necessary.
  174. $uninstallfunction();
  175. }
  176. }
  177. // Specific plugin type cleanup.
  178. $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
  179. if ($plugininfo) {
  180. $plugininfo->uninstall_cleanup();
  181. core_plugin_manager::reset_caches();
  182. }
  183. $plugininfo = null;
  184. // perform clean-up task common for all the plugin/subplugin types
  185. //delete the web service functions and pre-built services
  186. require_once($CFG->dirroot.'/lib/externallib.php');
  187. external_delete_descriptions($component);
  188. // delete calendar events
  189. $DB->delete_records('event', array('modulename' => $pluginname));
  190. // Delete scheduled tasks.
  191. $DB->delete_records('task_scheduled', array('component' => $component));
  192. // Delete Inbound Message datakeys.
  193. $DB->delete_records_select('messageinbound_datakeys',
  194. 'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
  195. // Delete Inbound Message handlers.
  196. $DB->delete_records('messageinbound_handlers', array('component' => $component));
  197. // delete all the logs
  198. $DB->delete_records('log', array('module' => $pluginname));
  199. // delete log_display information
  200. $DB->delete_records('log_display', array('component' => $component));
  201. // delete the module configuration records
  202. unset_all_config_for_plugin($component);
  203. if ($type === 'mod') {
  204. unset_all_config_for_plugin($pluginname);
  205. }
  206. // delete message provider
  207. message_provider_uninstall($component);
  208. // delete the plugin tables
  209. $xmldbfilepath = $plugindirectory . '/db/install.xml';
  210. drop_plugin_tables($component, $xmldbfilepath, false);
  211. if ($type === 'mod' or $type === 'block') {
  212. // non-frankenstyle table prefixes
  213. drop_plugin_tables($name, $xmldbfilepath, false);
  214. }
  215. // delete the capabilities that were defined by this module
  216. capabilities_cleanup($component);
  217. // Delete all remaining files in the filepool owned by the component.
  218. $fs = get_file_storage();
  219. $fs->delete_component_files($component);
  220. // Finally purge all caches.
  221. purge_all_caches();
  222. // Invalidate the hash used for upgrade detections.
  223. set_config('allversionshash', '');
  224. echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
  225. }
  226. /**
  227. * Returns the version of installed component
  228. *
  229. * @param string $component component name
  230. * @param string $source either 'disk' or 'installed' - where to get the version information from
  231. * @return string|bool version number or false if the component is not found
  232. */
  233. function get_component_version($component, $source='installed') {
  234. global $CFG, $DB;
  235. list($type, $name) = core_component::normalize_component($component);
  236. // moodle core or a core subsystem
  237. if ($type === 'core') {
  238. if ($source === 'installed') {
  239. if (empty($CFG->version)) {
  240. return false;
  241. } else {
  242. return $CFG->version;
  243. }
  244. } else {
  245. if (!is_readable($CFG->dirroot.'/version.php')) {
  246. return false;
  247. } else {
  248. $version = null; //initialize variable for IDEs
  249. include($CFG->dirroot.'/version.php');
  250. return $version;
  251. }
  252. }
  253. }
  254. // activity module
  255. if ($type === 'mod') {
  256. if ($source === 'installed') {
  257. if ($CFG->version < 2013092001.02) {
  258. return $DB->get_field('modules', 'version', array('name'=>$name));
  259. } else {
  260. return get_config('mod_'.$name, 'version');
  261. }
  262. } else {
  263. $mods = core_component::get_plugin_list('mod');
  264. if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
  265. return false;
  266. } else {
  267. $plugin = new stdClass();
  268. $plugin->version = null;
  269. $module = $plugin;
  270. include($mods[$name].'/version.php');
  271. return $plugin->version;
  272. }
  273. }
  274. }
  275. // block
  276. if ($type === 'block') {
  277. if ($source === 'installed') {
  278. if ($CFG->version < 2013092001.02) {
  279. return $DB->get_field('block', 'version', array('name'=>$name));
  280. } else {
  281. return get_config('block_'.$name, 'version');
  282. }
  283. } else {
  284. $blocks = core_component::get_plugin_list('block');
  285. if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
  286. return false;
  287. } else {
  288. $plugin = new stdclass();
  289. include($blocks[$name].'/version.php');
  290. return $plugin->version;
  291. }
  292. }
  293. }
  294. // all other plugin types
  295. if ($source === 'installed') {
  296. return get_config($type.'_'.$name, 'version');
  297. } else {
  298. $plugins = core_component::get_plugin_list($type);
  299. if (empty($plugins[$name])) {
  300. return false;
  301. } else {
  302. $plugin = new stdclass();
  303. include($plugins[$name].'/version.php');
  304. return $plugin->version;
  305. }
  306. }
  307. }
  308. /**
  309. * Delete all plugin tables
  310. *
  311. * @param string $name Name of plugin, used as table prefix
  312. * @param string $file Path to install.xml file
  313. * @param bool $feedback defaults to true
  314. * @return bool Always returns true
  315. */
  316. function drop_plugin_tables($name, $file, $feedback=true) {
  317. global $CFG, $DB;
  318. // first try normal delete
  319. if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
  320. return true;
  321. }
  322. // then try to find all tables that start with name and are not in any xml file
  323. $used_tables = get_used_table_names();
  324. $tables = $DB->get_tables();
  325. /// Iterate over, fixing id fields as necessary
  326. foreach ($tables as $table) {
  327. if (in_array($table, $used_tables)) {
  328. continue;
  329. }
  330. if (strpos($table, $name) !== 0) {
  331. continue;
  332. }
  333. // found orphan table --> delete it
  334. if ($DB->get_manager()->table_exists($table)) {
  335. $xmldb_table = new xmldb_table($table);
  336. $DB->get_manager()->drop_table($xmldb_table);
  337. }
  338. }
  339. return true;
  340. }
  341. /**
  342. * Returns names of all known tables == tables that moodle knows about.
  343. *
  344. * @return array Array of lowercase table names
  345. */
  346. function get_used_table_names() {
  347. $table_names = array();
  348. $dbdirs = get_db_directories();
  349. foreach ($dbdirs as $dbdir) {
  350. $file = $dbdir.'/install.xml';
  351. $xmldb_file = new xmldb_file($file);
  352. if (!$xmldb_file->fileExists()) {
  353. continue;
  354. }
  355. $loaded = $xmldb_file->loadXMLStructure();
  356. $structure = $xmldb_file->getStructure();
  357. if ($loaded and $tables = $structure->getTables()) {
  358. foreach($tables as $table) {
  359. $table_names[] = strtolower($table->getName());
  360. }
  361. }
  362. }
  363. return $table_names;
  364. }
  365. /**
  366. * Returns list of all directories where we expect install.xml files
  367. * @return array Array of paths
  368. */
  369. function get_db_directories() {
  370. global $CFG;
  371. $dbdirs = array();
  372. /// First, the main one (lib/db)
  373. $dbdirs[] = $CFG->libdir.'/db';
  374. /// Then, all the ones defined by core_component::get_plugin_types()
  375. $plugintypes = core_component::get_plugin_types();
  376. foreach ($plugintypes as $plugintype => $pluginbasedir) {
  377. if ($plugins = core_component::get_plugin_list($plugintype)) {
  378. foreach ($plugins as $plugin => $plugindir) {
  379. $dbdirs[] = $plugindir.'/db';
  380. }
  381. }
  382. }
  383. return $dbdirs;
  384. }
  385. /**
  386. * Try to obtain or release the cron lock.
  387. * @param string $name name of lock
  388. * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
  389. * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
  390. * @return bool true if lock obtained
  391. */
  392. function set_cron_lock($name, $until, $ignorecurrent=false) {
  393. global $DB;
  394. if (empty($name)) {
  395. debugging("Tried to get a cron lock for a null fieldname");
  396. return false;
  397. }
  398. // remove lock by force == remove from config table
  399. if (is_null($until)) {
  400. set_config($name, null);
  401. return true;
  402. }
  403. if (!$ignorecurrent) {
  404. // read value from db - other processes might have changed it
  405. $value = $DB->get_field('config', 'value', array('name'=>$name));
  406. if ($value and $value > time()) {
  407. //lock active
  408. return false;
  409. }
  410. }
  411. set_config($name, $until);
  412. return true;
  413. }
  414. /**
  415. * Test if and critical warnings are present
  416. * @return bool
  417. */
  418. function admin_critical_warnings_present() {
  419. global $SESSION;
  420. if (!has_capability('moodle/site:config', context_system::instance())) {
  421. return 0;
  422. }
  423. if (!isset($SESSION->admin_critical_warning)) {
  424. $SESSION->admin_critical_warning = 0;
  425. if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
  426. $SESSION->admin_critical_warning = 1;
  427. }
  428. }
  429. return $SESSION->admin_critical_warning;
  430. }
  431. /**
  432. * Detects if float supports at least 10 decimal digits
  433. *
  434. * Detects if float supports at least 10 decimal digits
  435. * and also if float-->string conversion works as expected.
  436. *
  437. * @return bool true if problem found
  438. */
  439. function is_float_problem() {
  440. $num1 = 2009010200.01;
  441. $num2 = 2009010200.02;
  442. return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
  443. }
  444. /**
  445. * Try to verify that dataroot is not accessible from web.
  446. *
  447. * Try to verify that dataroot is not accessible from web.
  448. * It is not 100% correct but might help to reduce number of vulnerable sites.
  449. * Protection from httpd.conf and .htaccess is not detected properly.
  450. *
  451. * @uses INSECURE_DATAROOT_WARNING
  452. * @uses INSECURE_DATAROOT_ERROR
  453. * @param bool $fetchtest try to test public access by fetching file, default false
  454. * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
  455. */
  456. function is_dataroot_insecure($fetchtest=false) {
  457. global $CFG;
  458. $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
  459. $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
  460. $rp = strrev(trim($rp, '/'));
  461. $rp = explode('/', $rp);
  462. foreach($rp as $r) {
  463. if (strpos($siteroot, '/'.$r.'/') === 0) {
  464. $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
  465. } else {
  466. break; // probably alias root
  467. }
  468. }
  469. $siteroot = strrev($siteroot);
  470. $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
  471. if (strpos($dataroot, $siteroot) !== 0) {
  472. return false;
  473. }
  474. if (!$fetchtest) {
  475. return INSECURE_DATAROOT_WARNING;
  476. }
  477. // now try all methods to fetch a test file using http protocol
  478. $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
  479. preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
  480. $httpdocroot = $matches[1];
  481. $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
  482. make_upload_directory('diag');
  483. $testfile = $CFG->dataroot.'/diag/public.txt';
  484. if (!file_exists($testfile)) {
  485. file_put_contents($testfile, 'test file, do not delete');
  486. @chmod($testfile, $CFG->filepermissions);
  487. }
  488. $teststr = trim(file_get_contents($testfile));
  489. if (empty($teststr)) {
  490. // hmm, strange
  491. return INSECURE_DATAROOT_WARNING;
  492. }
  493. $testurl = $datarooturl.'/diag/public.txt';
  494. if (extension_loaded('curl') and
  495. !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
  496. !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
  497. ($ch = @curl_init($testurl)) !== false) {
  498. curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
  499. curl_setopt($ch, CURLOPT_HEADER, false);
  500. $data = curl_exec($ch);
  501. if (!curl_errno($ch)) {
  502. $data = trim($data);
  503. if ($data === $teststr) {
  504. curl_close($ch);
  505. return INSECURE_DATAROOT_ERROR;
  506. }
  507. }
  508. curl_close($ch);
  509. }
  510. if ($data = @file_get_contents($testurl)) {
  511. $data = trim($data);
  512. if ($data === $teststr) {
  513. return INSECURE_DATAROOT_ERROR;
  514. }
  515. }
  516. preg_match('|https?://([^/]+)|i', $testurl, $matches);
  517. $sitename = $matches[1];
  518. $error = 0;
  519. if ($fp = @fsockopen($sitename, 80, $error)) {
  520. preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
  521. $localurl = $matches[1];
  522. $out = "GET $localurl HTTP/1.1\r\n";
  523. $out .= "Host: $sitename\r\n";
  524. $out .= "Connection: Close\r\n\r\n";
  525. fwrite($fp, $out);
  526. $data = '';
  527. $incoming = false;
  528. while (!feof($fp)) {
  529. if ($incoming) {
  530. $data .= fgets($fp, 1024);
  531. } else if (@fgets($fp, 1024) === "\r\n") {
  532. $incoming = true;
  533. }
  534. }
  535. fclose($fp);
  536. $data = trim($data);
  537. if ($data === $teststr) {
  538. return INSECURE_DATAROOT_ERROR;
  539. }
  540. }
  541. return INSECURE_DATAROOT_WARNING;
  542. }
  543. /**
  544. * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
  545. */
  546. function enable_cli_maintenance_mode() {
  547. global $CFG;
  548. if (file_exists("$CFG->dataroot/climaintenance.html")) {
  549. unlink("$CFG->dataroot/climaintenance.html");
  550. }
  551. if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
  552. $data = $CFG->maintenance_message;
  553. $data = bootstrap_renderer::early_error_content($data, null, null, null);
  554. $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
  555. } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
  556. $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
  557. } else {
  558. $data = get_string('sitemaintenance', 'admin');
  559. $data = bootstrap_renderer::early_error_content($data, null, null, null);
  560. $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
  561. }
  562. file_put_contents("$CFG->dataroot/climaintenance.html", $data);
  563. chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
  564. }
  565. /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
  566. /**
  567. * Interface for anything appearing in the admin tree
  568. *
  569. * The interface that is implemented by anything that appears in the admin tree
  570. * block. It forces inheriting classes to define a method for checking user permissions
  571. * and methods for finding something in the admin tree.
  572. *
  573. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  574. */
  575. interface part_of_admin_tree {
  576. /**
  577. * Finds a named part_of_admin_tree.
  578. *
  579. * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
  580. * and not parentable_part_of_admin_tree, then this function should only check if
  581. * $this->name matches $name. If it does, it should return a reference to $this,
  582. * otherwise, it should return a reference to NULL.
  583. *
  584. * If a class inherits parentable_part_of_admin_tree, this method should be called
  585. * recursively on all child objects (assuming, of course, the parent object's name
  586. * doesn't match the search criterion).
  587. *
  588. * @param string $name The internal name of the part_of_admin_tree we're searching for.
  589. * @return mixed An object reference or a NULL reference.
  590. */
  591. public function locate($name);
  592. /**
  593. * Removes named part_of_admin_tree.
  594. *
  595. * @param string $name The internal name of the part_of_admin_tree we want to remove.
  596. * @return bool success.
  597. */
  598. public function prune($name);
  599. /**
  600. * Search using query
  601. * @param string $query
  602. * @return mixed array-object structure of found settings and pages
  603. */
  604. public function search($query);
  605. /**
  606. * Verifies current user's access to this part_of_admin_tree.
  607. *
  608. * Used to check if the current user has access to this part of the admin tree or
  609. * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
  610. * then this method is usually just a call to has_capability() in the site context.
  611. *
  612. * If a class inherits parentable_part_of_admin_tree, this method should return the
  613. * logical OR of the return of check_access() on all child objects.
  614. *
  615. * @return bool True if the user has access, false if she doesn't.
  616. */
  617. public function check_access();
  618. /**
  619. * Mostly useful for removing of some parts of the tree in admin tree block.
  620. *
  621. * @return True is hidden from normal list view
  622. */
  623. public function is_hidden();
  624. /**
  625. * Show we display Save button at the page bottom?
  626. * @return bool
  627. */
  628. public function show_save();
  629. }
  630. /**
  631. * Interface implemented by any part_of_admin_tree that has children.
  632. *
  633. * The interface implemented by any part_of_admin_tree that can be a parent
  634. * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
  635. * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
  636. * include an add method for adding other part_of_admin_tree objects as children.
  637. *
  638. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  639. */
  640. interface parentable_part_of_admin_tree extends part_of_admin_tree {
  641. /**
  642. * Adds a part_of_admin_tree object to the admin tree.
  643. *
  644. * Used to add a part_of_admin_tree object to this object or a child of this
  645. * object. $something should only be added if $destinationname matches
  646. * $this->name. If it doesn't, add should be called on child objects that are
  647. * also parentable_part_of_admin_tree's.
  648. *
  649. * $something should be appended as the last child in the $destinationname. If the
  650. * $beforesibling is specified, $something should be prepended to it. If the given
  651. * sibling is not found, $something should be appended to the end of $destinationname
  652. * and a developer debugging message should be displayed.
  653. *
  654. * @param string $destinationname The internal name of the new parent for $something.
  655. * @param part_of_admin_tree $something The object to be added.
  656. * @return bool True on success, false on failure.
  657. */
  658. public function add($destinationname, $something, $beforesibling = null);
  659. }
  660. /**
  661. * The object used to represent folders (a.k.a. categories) in the admin tree block.
  662. *
  663. * Each admin_category object contains a number of part_of_admin_tree objects.
  664. *
  665. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  666. */
  667. class admin_category implements parentable_part_of_admin_tree {
  668. /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
  669. protected $children;
  670. /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
  671. public $name;
  672. /** @var string The displayed name for this category. Usually obtained through get_string() */
  673. public $visiblename;
  674. /** @var bool Should this category be hidden in admin tree block? */
  675. public $hidden;
  676. /** @var mixed Either a string or an array or strings */
  677. public $path;
  678. /** @var mixed Either a string or an array or strings */
  679. public $visiblepath;
  680. /** @var array fast lookup category cache, all categories of one tree point to one cache */
  681. protected $category_cache;
  682. /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
  683. protected $sort = false;
  684. /** @var bool If set to true children will be sorted in ascending order. */
  685. protected $sortasc = true;
  686. /** @var bool If set to true sub categories and pages will be split and then sorted.. */
  687. protected $sortsplit = true;
  688. /** @var bool $sorted True if the children have been sorted and don't need resorting */
  689. protected $sorted = false;
  690. /**
  691. * Constructor for an empty admin category
  692. *
  693. * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
  694. * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
  695. * @param bool $hidden hide category in admin tree block, defaults to false
  696. */
  697. public function __construct($name, $visiblename, $hidden=false) {
  698. $this->children = array();
  699. $this->name = $name;
  700. $this->visiblename = $visiblename;
  701. $this->hidden = $hidden;
  702. }
  703. /**
  704. * Returns a reference to the part_of_admin_tree object with internal name $name.
  705. *
  706. * @param string $name The internal name of the object we want.
  707. * @param bool $findpath initialize path and visiblepath arrays
  708. * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
  709. * defaults to false
  710. */
  711. public function locate($name, $findpath=false) {
  712. if (!isset($this->category_cache[$this->name])) {
  713. // somebody much have purged the cache
  714. $this->category_cache[$this->name] = $this;
  715. }
  716. if ($this->name == $name) {
  717. if ($findpath) {
  718. $this->visiblepath[] = $this->visiblename;
  719. $this->path[] = $this->name;
  720. }
  721. return $this;
  722. }
  723. // quick category lookup
  724. if (!$findpath and isset($this->category_cache[$name])) {
  725. return $this->category_cache[$name];
  726. }
  727. $return = NULL;
  728. foreach($this->children as $childid=>$unused) {
  729. if ($return = $this->children[$childid]->locate($name, $findpath)) {
  730. break;
  731. }
  732. }
  733. if (!is_null($return) and $findpath) {
  734. $return->visiblepath[] = $this->visiblename;
  735. $return->path[] = $this->name;
  736. }
  737. return $return;
  738. }
  739. /**
  740. * Search using query
  741. *
  742. * @param string query
  743. * @return mixed array-object structure of found settings and pages
  744. */
  745. public function search($query) {
  746. $result = array();
  747. foreach ($this->get_children() as $child) {
  748. $subsearch = $child->search($query);
  749. if (!is_array($subsearch)) {
  750. debugging('Incorrect search result from '.$child->name);
  751. continue;
  752. }
  753. $result = array_merge($result, $subsearch);
  754. }
  755. return $result;
  756. }
  757. /**
  758. * Removes part_of_admin_tree object with internal name $name.
  759. *
  760. * @param string $name The internal name of the object we want to remove.
  761. * @return bool success
  762. */
  763. public function prune($name) {
  764. if ($this->name == $name) {
  765. return false; //can not remove itself
  766. }
  767. foreach($this->children as $precedence => $child) {
  768. if ($child->name == $name) {
  769. // clear cache and delete self
  770. while($this->category_cache) {
  771. // delete the cache, but keep the original array address
  772. array_pop($this->category_cache);
  773. }
  774. unset($this->children[$precedence]);
  775. return true;
  776. } else if ($this->children[$precedence]->prune($name)) {
  777. return true;
  778. }
  779. }
  780. return false;
  781. }
  782. /**
  783. * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
  784. *
  785. * By default the new part of the tree is appended as the last child of the parent. You
  786. * can specify a sibling node that the new part should be prepended to. If the given
  787. * sibling is not found, the part is appended to the end (as it would be by default) and
  788. * a developer debugging message is displayed.
  789. *
  790. * @throws coding_exception if the $beforesibling is empty string or is not string at all.
  791. * @param string $destinationame The internal name of the immediate parent that we want for $something.
  792. * @param mixed $something A part_of_admin_tree or setting instance to be added.
  793. * @param string $beforesibling The name of the parent's child the $something should be prepended to.
  794. * @return bool True if successfully added, false if $something can not be added.
  795. */
  796. public function add($parentname, $something, $beforesibling = null) {
  797. global $CFG;
  798. $parent = $this->locate($parentname);
  799. if (is_null($parent)) {
  800. debugging('parent does not exist!');
  801. return false;
  802. }
  803. if ($something instanceof part_of_admin_tree) {
  804. if (!($parent instanceof parentable_part_of_admin_tree)) {
  805. debugging('error - parts of tree can be inserted only into parentable parts');
  806. return false;
  807. }
  808. if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
  809. // The name of the node is already used, simply warn the developer that this should not happen.
  810. // It is intentional to check for the debug level before performing the check.
  811. debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
  812. }
  813. if (is_null($beforesibling)) {
  814. // Append $something as the parent's last child.
  815. $parent->children[] = $something;
  816. } else {
  817. if (!is_string($beforesibling) or trim($beforesibling) === '') {
  818. throw new coding_exception('Unexpected value of the beforesibling parameter');
  819. }
  820. // Try to find the position of the sibling.
  821. $siblingposition = null;
  822. foreach ($parent->children as $childposition => $child) {
  823. if ($child->name === $beforesibling) {
  824. $siblingposition = $childposition;
  825. break;
  826. }
  827. }
  828. if (is_null($siblingposition)) {
  829. debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
  830. $parent->children[] = $something;
  831. } else {
  832. $parent->children = array_merge(
  833. array_slice($parent->children, 0, $siblingposition),
  834. array($something),
  835. array_slice($parent->children, $siblingposition)
  836. );
  837. }
  838. }
  839. if ($something instanceof admin_category) {
  840. if (isset($this->category_cache[$something->name])) {
  841. debugging('Duplicate admin category name: '.$something->name);
  842. } else {
  843. $this->category_cache[$something->name] = $something;
  844. $something->category_cache =& $this->category_cache;
  845. foreach ($something->children as $child) {
  846. // just in case somebody already added subcategories
  847. if ($child instanceof admin_category) {
  848. if (isset($this->category_cache[$child->name])) {
  849. debugging('Duplicate admin category name: '.$child->name);
  850. } else {
  851. $this->category_cache[$child->name] = $child;
  852. $child->category_cache =& $this->category_cache;
  853. }
  854. }
  855. }
  856. }
  857. }
  858. return true;
  859. } else {
  860. debugging('error - can not add this element');
  861. return false;
  862. }
  863. }
  864. /**
  865. * Checks if the user has access to anything in this category.
  866. *
  867. * @return bool True if the user has access to at least one child in this category, false otherwise.
  868. */
  869. public function check_access() {
  870. foreach ($this->children as $child) {
  871. if ($child->check_access()) {
  872. return true;
  873. }
  874. }
  875. return false;
  876. }
  877. /**
  878. * Is this category hidden in admin tree block?
  879. *
  880. * @return bool True if hidden
  881. */
  882. public function is_hidden() {
  883. return $this->hidden;
  884. }
  885. /**
  886. * Show we display Save button at the page bottom?
  887. * @return bool
  888. */
  889. public function show_save() {
  890. foreach ($this->children as $child) {
  891. if ($child->show_save()) {
  892. return true;
  893. }
  894. }
  895. return false;
  896. }
  897. /**
  898. * Sets sorting on this category.
  899. *
  900. * Please note this function doesn't actually do the sorting.
  901. * It can be called anytime.
  902. * Sorting occurs when the user calls get_children.
  903. * Code using the children array directly won't see the sorted results.
  904. *
  905. * @param bool $sort If set to true children will be sorted, if false they won't be.
  906. * @param bool $asc If true sorting will be ascending, otherwise descending.
  907. * @param bool $split If true we sort pages and sub categories separately.
  908. */
  909. public function set_sorting($sort, $asc = true, $split = true) {
  910. $this->sort = (bool)$sort;
  911. $this->sortasc = (bool)$asc;
  912. $this->sortsplit = (bool)$split;
  913. }
  914. /**
  915. * Returns the children associated with this category.
  916. *
  917. * @return part_of_admin_tree[]
  918. */
  919. public function get_children() {
  920. // If we should sort and it hasn't already been sorted.
  921. if ($this->sort && !$this->sorted) {
  922. if ($this->sortsplit) {
  923. $categories = array();
  924. $pages = array();
  925. foreach ($this->children as $child) {
  926. if ($child instanceof admin_category) {
  927. $categories[] = $child;
  928. } else {
  929. $pages[] = $child;
  930. }
  931. }
  932. core_collator::asort_objects_by_property($categories, 'visiblename');
  933. core_collator::asort_objects_by_property($pages, 'visiblename');
  934. if (!$this->sortasc) {
  935. $categories = array_reverse($categories);
  936. $pages = array_reverse($pages);
  937. }
  938. $this->children = array_merge($pages, $categories);
  939. } else {
  940. core_collator::asort_objects_by_property($this->children, 'visiblename');
  941. if (!$this->sortasc) {
  942. $this->children = array_reverse($this->children);
  943. }
  944. }
  945. $this->sorted = true;
  946. }
  947. return $this->children;
  948. }
  949. /**
  950. * Magically gets a property from this object.
  951. *
  952. * @param $property
  953. * @return part_of_admin_tree[]
  954. * @throws coding_exception
  955. */
  956. public function __get($property) {
  957. if ($property === 'children') {
  958. return $this->get_children();
  959. }
  960. throw new coding_exception('Invalid property requested.');
  961. }
  962. /**
  963. * Magically sets a property against this object.
  964. *
  965. * @param string $property
  966. * @param mixed $value
  967. * @throws coding_exception
  968. */
  969. public function __set($property, $value) {
  970. if ($property === 'children') {
  971. $this->sorted = false;
  972. $this->children = $value;
  973. } else {
  974. throw new coding_exception('Invalid property requested.');
  975. }
  976. }
  977. /**
  978. * Checks if an inaccessible property is set.
  979. *
  980. * @param string $property
  981. * @return bool
  982. * @throws coding_exception
  983. */
  984. public function __isset($property) {
  985. if ($property === 'children') {
  986. return isset($this->children);
  987. }
  988. throw new coding_exception('Invalid property requested.');
  989. }
  990. }
  991. /**
  992. * Root of admin settings tree, does not have any parent.
  993. *
  994. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  995. */
  996. class admin_root extends admin_category {
  997. /** @var array List of errors */
  998. public $errors;
  999. /** @var string search query */
  1000. public $search;
  1001. /** @var bool full tree flag - true means all settings required, false only pages required */
  1002. public $fulltree;
  1003. /** @var bool flag indicating loaded tree */
  1004. public $loaded;
  1005. /** @var mixed site custom defaults overriding defaults in settings files*/
  1006. public $custom_defaults;
  1007. /**
  1008. * @param bool $fulltree true means all settings required,
  1009. * false only pages required
  1010. */
  1011. public function __construct($fulltree) {
  1012. global $CFG;
  1013. parent::__construct('root', get_string('administration'), false);
  1014. $this->errors = array();
  1015. $this->search = '';
  1016. $this->fulltree = $fulltree;
  1017. $this->loaded = false;
  1018. $this->category_cache = array();
  1019. // load custom defaults if found
  1020. $this->custom_defaults = null;
  1021. $defaultsfile = "$CFG->dirroot/local/defaults.php";
  1022. if (is_readable($defaultsfile)) {
  1023. $defaults = array();
  1024. include($defaultsfile);
  1025. if (is_array($defaults) and count($defaults)) {
  1026. $this->custom_defaults = $defaults;
  1027. }
  1028. }
  1029. }
  1030. /**
  1031. * Empties children array, and sets loaded to false
  1032. *
  1033. * @param bool $requirefulltree
  1034. */
  1035. public function purge_children($requirefulltree) {
  1036. $this->children = array();
  1037. $this->fulltree = ($requirefulltree || $this->fulltree);
  1038. $this->loaded = false;
  1039. //break circular dependencies - this helps PHP 5.2
  1040. while($this->category_cache) {
  1041. array_pop($this->category_cache);
  1042. }
  1043. $this->category_cache = array();
  1044. }
  1045. }
  1046. /**
  1047. * Links external PHP pages into the admin tree.
  1048. *
  1049. * See detailed usage example at the top of this document (adminlib.php)
  1050. *
  1051. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1052. */
  1053. class admin_externalpage implements part_of_admin_tree {
  1054. /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
  1055. public $name;
  1056. /** @var string The displayed name for this external page. Usually obtained through get_string(). */
  1057. public $visiblename;
  1058. /** @var string The external URL that we should link to when someone requests this external page. */
  1059. public $url;
  1060. /** @var string The role capability/permission a user must have to access this external page. */
  1061. public $req_capability;
  1062. /** @var object The context in which capability/permission should be checked, default is site context. */
  1063. public $context;
  1064. /** @var bool hidden in admin tree block. */
  1065. public $hidden;
  1066. /** @var mixed either string or array of string */
  1067. public $path;
  1068. /** @var array list of visible names of page parents */
  1069. public $visiblepath;
  1070. /**
  1071. * Constructor for adding an external page into the admin tree.
  1072. *
  1073. * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
  1074. * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
  1075. * @param string $url The external URL that we should link to when someone requests this external page.
  1076. * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
  1077. * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
  1078. * @param stdClass $context The context the page relates to. Not sure what happens
  1079. * if you specify something other than system or front page. Defaults to system.
  1080. */
  1081. public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
  1082. $this->name = $name;
  1083. $this->visiblename = $visiblename;
  1084. $this->url = $url;
  1085. if (is_array($req_capability)) {
  1086. $this->req_capability = $req_capability;
  1087. } else {
  1088. $this->req_capability = array($req_capability);
  1089. }
  1090. $this->hidden = $hidden;
  1091. $this->context = $context;
  1092. }
  1093. /**
  1094. * Returns a reference to the part_of_admin_tree object with internal name $name.
  1095. *
  1096. * @param string $name The internal name of the object we want.
  1097. * @param bool $findpath defaults to false
  1098. * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
  1099. */
  1100. public function locate($name, $findpath=false) {
  1101. if ($this->name == $name) {
  1102. if ($findpath) {
  1103. $this->visiblepath = array($this->visiblename);
  1104. $this->path = array($this->name);
  1105. }
  1106. return $this;
  1107. } else {
  1108. $return = NULL;
  1109. return $return;
  1110. }
  1111. }
  1112. /**
  1113. * This function always returns false, required function by interface
  1114. *
  1115. * @param string $name
  1116. * @return false
  1117. */
  1118. public function prune($name) {
  1119. return false;
  1120. }
  1121. /**
  1122. * Search using query
  1123. *
  1124. * @param string $query
  1125. * @return mixed array-object structure of found settings and pages
  1126. */
  1127. public function search($query) {
  1128. $found = false;
  1129. if (strpos(strtolower($this->name), $query) !== false) {
  1130. $found = true;
  1131. } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
  1132. $found = true;
  1133. }
  1134. if ($found) {
  1135. $result = new stdClass();
  1136. $result->page = $this;
  1137. $result->settings = array();
  1138. return array($this->name => $result);
  1139. } else {
  1140. return array();
  1141. }
  1142. }
  1143. /**
  1144. * Determines if the current user has access to this external page based on $this->req_capability.
  1145. *
  1146. * @return bool True if user has access, false otherwise.
  1147. */
  1148. public function check_access() {
  1149. global $CFG;
  1150. $context = empty($this->context) ? context_system::instance() : $this->context;
  1151. foreach($this->req_capability as $cap) {
  1152. if (has_capability($cap, $context)) {
  1153. return true;
  1154. }
  1155. }
  1156. return false;
  1157. }
  1158. /**
  1159. * Is this external page hidden in admin tree block?
  1160. *
  1161. * @return bool True if hidden
  1162. */
  1163. public function is_hidden() {
  1164. return $this->hidden;
  1165. }
  1166. /**
  1167. * Show we display Save button at the page bottom?
  1168. * @return bool
  1169. */
  1170. public function show_save() {
  1171. return false;
  1172. }
  1173. }
  1174. /**
  1175. * Used to store details of the dependency between two settings elements.
  1176. *
  1177. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1178. * @copyright 2017 Davo Smith, Synergy Learning
  1179. */
  1180. class admin_settingdependency {
  1181. /** @var string the name of the setting to be shown/hidden */
  1182. public $settingname;
  1183. /** @var string the setting this is dependent on */
  1184. public $dependenton;
  1185. /** @var string the condition to show/hide the element */
  1186. public $condition;
  1187. /** @var string the value to compare against */
  1188. public $value;
  1189. /** @var string[] list of valid conditions */
  1190. private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
  1191. /**
  1192. * admin_settingdependency constructor.
  1193. * @param string $settingname
  1194. * @param string $dependenton
  1195. * @param string $condition
  1196. * @param string $value
  1197. * @throws \coding_exception
  1198. */
  1199. public function __construct($settingname, $dependenton, $condition, $value) {
  1200. $this->settingname = $this->parse_name($settingname);
  1201. $this->dependenton = $this->parse_name($dependenton);
  1202. $this->condition = $condition;
  1203. $this->value = $value;
  1204. if (!in_array($this->condition, self::$validconditions)) {
  1205. throw new coding_exception("Invalid condition '$condition'");
  1206. }
  1207. }
  1208. /**
  1209. * Convert the setting name into the form field name.
  1210. * @param string $name
  1211. * @return string
  1212. */
  1213. private function parse_name($name) {
  1214. $bits = explode('/', $name);
  1215. $name = array_pop($bits);
  1216. $plugin = '';
  1217. if ($bits) {
  1218. $plugin = array_pop($bits);
  1219. if ($plugin === 'moodle') {
  1220. $plugin = '';
  1221. }
  1222. }
  1223. return 's_'.$plugin.'_'.$name;
  1224. }
  1225. /**
  1226. * Gather together all the dependencies in a format suitable for initialising javascript
  1227. * @param admin_settingdependency[] $dependencies
  1228. * @return array
  1229. */
  1230. public static function prepare_for_javascript($dependencies) {
  1231. $result = [];
  1232. foreach ($dependencies as $d) {
  1233. if (!isset($result[$d->dependenton])) {
  1234. $result[$d->dependenton] = [];
  1235. }
  1236. if (!isset($result[$d->dependenton][$d->condition])) {
  1237. $result[$d->dependenton][$d->condition] = [];
  1238. }
  1239. if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
  1240. $result[$d->dependenton][$d->condition][$d->value] = [];
  1241. }
  1242. $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
  1243. }
  1244. return $result;
  1245. }
  1246. }
  1247. /**
  1248. * Used to group a number of admin_setting objects into a page and add them to the admin tree.
  1249. *
  1250. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1251. */
  1252. class admin_settingpage implements part_of_admin_tree {
  1253. /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
  1254. public $name;
  1255. /** @var string The displayed name for this external page. Usually obtained through get_string(). */
  1256. public $visiblename;
  1257. /** @var mixed An array of admin_setting objects that are part of this setting page. */
  1258. public $settings;
  1259. /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
  1260. protected $dependencies = [];
  1261. /** @var string The role capability/permission a user must have to access this external page. */
  1262. public $req_capability;
  1263. /** @var object The context in which capability/permission should be checked, default is site context. */
  1264. public $context;
  1265. /** @var bool hidden in admin tree block. */
  1266. public $hidden;
  1267. /** @var mixed string of paths or array of strings of paths */
  1268. public $path;
  1269. /** @var array list of visible names of page parents */
  1270. public $visiblepath;
  1271. /**
  1272. * see admin_settingpage for details of this function
  1273. *
  1274. * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
  1275. * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
  1276. * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
  1277. * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
  1278. * @param stdClass $context The context the page relates to. Not sure what happens
  1279. * if you specify something other than system or front page. Defaults to system.
  1280. */
  1281. public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
  1282. $this->settings = new stdClass();
  1283. $this->name = $name;
  1284. $this->visiblename = $visiblename;
  1285. if (is_array($req_capability)) {
  1286. $this->req_capability = $req_capability;
  1287. } else {
  1288. $this->req_capability = array($req_capability);
  1289. }
  1290. $this->hidden = $hidden;
  1291. $this->context = $context;
  1292. }
  1293. /**
  1294. * see admin_category
  1295. *
  1296. * @param string $name
  1297. * @param bool $findpath
  1298. * @return mixed Object (this) if name == this->name, else returns null
  1299. */
  1300. public function locate($name, $findpath=false) {
  1301. if ($this->name == $name) {
  1302. if ($findpath) {
  1303. $this->visiblepath = array($this->visiblename);
  1304. $this->path = array($this->name);
  1305. }
  1306. return $this;
  1307. } else {
  1308. $return = NULL;
  1309. return $return;
  1310. }
  1311. }
  1312. /**
  1313. * Search string in settings page.
  1314. *
  1315. * @param string $query
  1316. * @return array
  1317. */
  1318. public function search($query) {
  1319. $found = array();
  1320. foreach ($this->settings as $setting) {
  1321. if ($setting->is_related($query)) {
  1322. $found[] = $setting;
  1323. }
  1324. }
  1325. if ($found) {
  1326. $result = new stdClass();
  1327. $result->page = $this;
  1328. $result->settings = $found;
  1329. return array($this->name => $result);
  1330. }
  1331. $found = false;
  1332. if (strpos(strtolower($this->name), $query) !== false) {
  1333. $found = true;
  1334. } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
  1335. $found = true;
  1336. }
  1337. if ($found) {
  1338. $result = new stdClass();
  1339. $result->page = $this;
  1340. $result->settings = array();
  1341. return array($this->name => $result);
  1342. } else {
  1343. return array();
  1344. }
  1345. }
  1346. /**
  1347. * This function always returns false, required by interface
  1348. *
  1349. * @param string $name
  1350. * @return bool Always false
  1351. */
  1352. public function prune($name) {
  1353. return false;
  1354. }
  1355. /**
  1356. * adds an admin_setting to this admin_settingpage
  1357. *
  1358. * 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
  1359. * n.b. each admin_setting in an admin_settingpage must have a unique internal name
  1360. *
  1361. * @param object $setting is the admin_setting object you want to add
  1362. * @return bool true if successful, false if not
  1363. */
  1364. public function add($setting) {
  1365. if (!($setting instanceof admin_setting)) {
  1366. debugging('error - not a setting instance');
  1367. return false;
  1368. }
  1369. $name = $setting->name;
  1370. if ($setting->plugin) {
  1371. $name = $setting->plugin . $name;
  1372. }
  1373. $this->settings->{$name} = $setting;
  1374. return true;
  1375. }
  1376. /**
  1377. * Hide the named setting if the specified condition is matched.
  1378. *
  1379. * @param string $settingname
  1380. * @param string $dependenton
  1381. * @param string $condition
  1382. * @param string $value
  1383. */
  1384. public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
  1385. $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
  1386. // Reformat the dependency name to the plugin | name format used in the display.
  1387. $dependenton = str_replace('/', ' | ', $dependenton);
  1388. // Let the setting know, so it can be displayed underneath.
  1389. $findname = str_replace('/', '', $settingname);
  1390. foreach ($this->settings as $name => $setting) {
  1391. if ($name === $findname) {
  1392. $setting->add_dependent_on($dependenton);
  1393. }
  1394. }
  1395. }
  1396. /**
  1397. * see admin_externalpage
  1398. *
  1399. * @return bool Returns true for yes false for no
  1400. */
  1401. public function check_access() {
  1402. global $CFG;
  1403. $context = empty($this->context) ? context_system::instance() : $this->context;
  1404. foreach($this->req_capability as $cap) {
  1405. if (has_capability($cap, $context)) {
  1406. return true;
  1407. }
  1408. }
  1409. return false;
  1410. }
  1411. /**
  1412. * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
  1413. * @return string Returns an XHTML string
  1414. */
  1415. public function output_html() {
  1416. $adminroot = admin_get_root();
  1417. $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
  1418. foreach($this->settings as $setting) {
  1419. $fullname = $setting->get_full_name();
  1420. if (array_key_exists($fullname, $adminroot->errors)) {
  1421. $data = $adminroot->errors[$fullname]->data;
  1422. } else {
  1423. $data = $setting->get_setting();
  1424. // do not use defaults if settings not available - upgrade settings handles the defaults!
  1425. }
  1426. $return .= $setting->output_html($data);
  1427. }
  1428. $return .= '</fieldset>';
  1429. return $return;
  1430. }
  1431. /**
  1432. * Is this settings page hidden in admin tree block?
  1433. *
  1434. * @return bool True if hidden
  1435. */
  1436. public function is_hidden() {
  1437. return $this->hidden;
  1438. }
  1439. /**
  1440. * Show we display Save button at the page bottom?
  1441. * @return bool
  1442. */
  1443. public function show_save() {
  1444. foreach($this->settings as $setting) {
  1445. if (empty($setting->nosave)) {
  1446. return true;
  1447. }
  1448. }
  1449. return false;
  1450. }
  1451. /**
  1452. * Should any of the settings on this page be shown / hidden based on conditions?
  1453. * @return bool
  1454. */
  1455. public function has_dependencies() {
  1456. return (bool)$this->dependencies;
  1457. }
  1458. /**
  1459. * Format the setting show/hide conditions ready to initialise the page javascript
  1460. * @return array
  1461. */
  1462. public function get_dependencies_for_javascript() {
  1463. if (!$this->has_dependencies()) {
  1464. return [];
  1465. }
  1466. return admin_settingdependency::prepare_for_javascript($this->dependencies);
  1467. }
  1468. }
  1469. /**
  1470. * Admin settings class. Only exists on setting pages.
  1471. * Read & write happens at this level; no authentication.
  1472. *
  1473. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1474. */
  1475. abstract class admin_setting {
  1476. /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
  1477. public $name;
  1478. /** @var string localised name */
  1479. public $visiblename;
  1480. /** @var string localised long description in Markdown format */
  1481. public $description;
  1482. /** @var mixed Can be string or array of string */
  1483. public $defaultsetting;
  1484. /** @var string */
  1485. public $updatedcallback;
  1486. /** @var mixed can be String or Null. Null means main config table */
  1487. public $plugin; // null means main config table
  1488. /** @var bool true indicates this setting does not actually save anything, just information */
  1489. public $nosave = false;
  1490. /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
  1491. public $affectsmodinfo = false;
  1492. /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
  1493. private $flags = array();
  1494. /** @var bool Whether this field must be forced LTR. */
  1495. private $forceltr = null;
  1496. /** @var array list of other settings that may cause this setting to be hidden */
  1497. private $dependenton = [];
  1498. /**
  1499. * Constructor
  1500. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  1501. * or 'myplugin/mysetting' for ones in config_plugins.
  1502. * @param string $visiblename localised name
  1503. * @param string $description localised long description
  1504. * @param mixed $defaultsetting string or array depending on implementation
  1505. */
  1506. public function __construct($name, $visiblename, $description, $defaultsetting) {
  1507. $this->parse_setting_name($name);
  1508. $this->visiblename = $visiblename;
  1509. $this->description = $description;
  1510. $this->defaultsetting = $defaultsetting;
  1511. }
  1512. /**
  1513. * Generic function to add a flag to this admin setting.
  1514. *
  1515. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
  1516. * @param bool $default - The default for the flag
  1517. * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
  1518. * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
  1519. */
  1520. protected function set_flag_options($enabled, $default, $shortname, $displayname) {
  1521. if (empty($this->flags[$shortname])) {
  1522. $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
  1523. } else {
  1524. $this->flags[$shortname]->set_options($enabled, $default);
  1525. }
  1526. }
  1527. /**
  1528. * Set the enabled options flag on this admin setting.
  1529. *
  1530. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
  1531. * @param bool $default - The default for the flag
  1532. */
  1533. public function set_enabled_flag_options($enabled, $default) {
  1534. $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
  1535. }
  1536. /**
  1537. * Set the advanced options flag on this admin setting.
  1538. *
  1539. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
  1540. * @param bool $default - The default for the flag
  1541. */
  1542. public function set_advanced_flag_options($enabled, $default) {
  1543. $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
  1544. }
  1545. /**
  1546. * Set the locked options flag on this admin setting.
  1547. *
  1548. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
  1549. * @param bool $default - The default for the flag
  1550. */
  1551. public function set_locked_flag_options($enabled, $default) {
  1552. $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
  1553. }
  1554. /**
  1555. * Set the required options flag on this admin setting.
  1556. *
  1557. * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
  1558. * @param bool $default - The default for the flag.
  1559. */
  1560. public function set_required_flag_options($enabled, $default) {
  1561. $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
  1562. }
  1563. /**
  1564. * Get the currently saved value for a setting flag
  1565. *
  1566. * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
  1567. * @return bool
  1568. */
  1569. public function get_setting_flag_value(admin_setting_flag $flag) {
  1570. $value = $this->config_read($this->name . '_' . $flag->get_shortname());
  1571. if (!isset($value)) {
  1572. $value = $flag->get_default();
  1573. }
  1574. return !empty($value);
  1575. }
  1576. /**
  1577. * Get the list of defaults for the flags on this setting.
  1578. *
  1579. * @param array of strings describing the defaults for this setting. This is appended to by this function.
  1580. */
  1581. public function get_setting_flag_defaults(& $defaults) {
  1582. foreach ($this->flags as $flag) {
  1583. if ($flag->is_enabled() && $flag->get_default()) {
  1584. $defaults[] = $flag->get_displayname();
  1585. }
  1586. }
  1587. }
  1588. /**
  1589. * Output the input fields for the advanced and locked flags on this setting.
  1590. *
  1591. * @param bool $adv - The current value of the advanced flag.
  1592. * @param bool $locked - The current value of the locked flag.
  1593. * @return string $output - The html for the flags.
  1594. */
  1595. public function output_setting_flags() {
  1596. $output = '';
  1597. foreach ($this->flags as $flag) {
  1598. if ($flag->is_enabled()) {
  1599. $output .= $flag->output_setting_flag($this);
  1600. }
  1601. }
  1602. if (!empty($output)) {
  1603. return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
  1604. }
  1605. return $output;
  1606. }
  1607. /**
  1608. * Write the values of the flags for this admin setting.
  1609. *
  1610. * @param array $data - The data submitted from the form or null to set the default value for new installs.
  1611. * @return bool - true if successful.
  1612. */
  1613. public function write_setting_flags($data) {
  1614. $result = true;
  1615. foreach ($this->flags as $flag) {
  1616. $result = $result && $flag->write_setting_flag($this, $data);
  1617. }
  1618. return $result;
  1619. }
  1620. /**
  1621. * Set up $this->name and potentially $this->plugin
  1622. *
  1623. * Set up $this->name and possibly $this->plugin based on whether $name looks
  1624. * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
  1625. * on the names, that is, output a developer debug warning if the name
  1626. * contains anything other than [a-zA-Z0-9_]+.
  1627. *
  1628. * @param string $name the setting name passed in to the constructor.
  1629. */
  1630. private function parse_setting_name($name) {
  1631. $bits = explode('/', $name);
  1632. if (count($bits) > 2) {
  1633. throw new moodle_exception('invalidadminsettingname', '', '', $name);
  1634. }
  1635. $this->name = array_pop($bits);
  1636. if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
  1637. throw new moodle_exception('invalidadminsettingname', '', '', $name);
  1638. }
  1639. if (!empty($bits)) {
  1640. $this->plugin = array_pop($bits);
  1641. if ($this->plugin === 'moodle') {
  1642. $this->plugin = null;
  1643. } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
  1644. throw new moodle_exception('invalidadminsettingname', '', '', $name);
  1645. }
  1646. }
  1647. }
  1648. /**
  1649. * Returns the fullname prefixed by the plugin
  1650. * @return string
  1651. */
  1652. public function get_full_name() {
  1653. return 's_'.$this->plugin.'_'.$this->name;
  1654. }
  1655. /**
  1656. * Returns the ID string based on plugin and name
  1657. * @return string
  1658. */
  1659. public function get_id() {
  1660. return 'id_s_'.$this->plugin.'_'.$this->name;
  1661. }
  1662. /**
  1663. * @param bool $affectsmodinfo If true, changes to this setting will
  1664. * cause the course cache to be rebuilt
  1665. */
  1666. public function set_affects_modinfo($affectsmodinfo) {
  1667. $this->affectsmodinfo = $affectsmodinfo;
  1668. }
  1669. /**
  1670. * Returns the config if possible
  1671. *
  1672. * @return mixed returns config if successful else null
  1673. */
  1674. public function config_read($name) {
  1675. global $CFG;
  1676. if (!empty($this->plugin)) {
  1677. $value = get_config($this->plugin, $name);
  1678. return $value === false ? NULL : $value;
  1679. } else {
  1680. if (isset($CFG->$name)) {
  1681. return $CFG->$name;
  1682. } else {
  1683. return NULL;
  1684. }
  1685. }
  1686. }
  1687. /**
  1688. * Used to set a config pair and log change
  1689. *
  1690. * @param string $name
  1691. * @param mixed $value Gets converted to string if not null
  1692. * @return bool Write setting to config table
  1693. */
  1694. public function config_write($name, $value) {
  1695. global $DB, $USER, $CFG;
  1696. if ($this->nosave) {
  1697. return true;
  1698. }
  1699. // make sure it is a real change
  1700. $oldvalue = get_config($this->plugin, $name);
  1701. $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
  1702. $value = is_null($value) ? null : (string)$value;
  1703. if ($oldvalue === $value) {
  1704. return true;
  1705. }
  1706. // store change
  1707. set_config($name, $value, $this->plugin);
  1708. // Some admin settings affect course modinfo
  1709. if ($this->affectsmodinfo) {
  1710. // Clear course cache for all courses
  1711. rebuild_course_cache(0, true);
  1712. }
  1713. $this->add_to_config_log($name, $oldvalue, $value);
  1714. return true; // BC only
  1715. }
  1716. /**
  1717. * Log config changes if necessary.
  1718. * @param string $name
  1719. * @param string $oldvalue
  1720. * @param string $value
  1721. */
  1722. protected function add_to_config_log($name, $oldvalue, $value) {
  1723. add_to_config_log($name, $oldvalue, $value, $this->plugin);
  1724. }
  1725. /**
  1726. * Returns current value of this setting
  1727. * @return mixed array or string depending on instance, NULL means not set yet
  1728. */
  1729. public abstract function get_setting();
  1730. /**
  1731. * Returns default setting if exists
  1732. * @return mixed array or string depending on instance; NULL means no default, user must supply
  1733. */
  1734. public function get_defaultsetting() {
  1735. $adminroot = admin_get_root(false, false);
  1736. if (!empty($adminroot->custom_defaults)) {
  1737. $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
  1738. if (isset($adminroot->custom_defaults[$plugin])) {
  1739. if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
  1740. return $adminroot->custom_defaults[$plugin][$this->name];
  1741. }
  1742. }
  1743. }
  1744. return $this->defaultsetting;
  1745. }
  1746. /**
  1747. * Store new setting
  1748. *
  1749. * @param mixed $data string or array, must not be NULL
  1750. * @return string empty string if ok, string error message otherwise
  1751. */
  1752. public abstract function write_setting($data);
  1753. /**
  1754. * Return part of form with setting
  1755. * This function should always be overwritten
  1756. *
  1757. * @param mixed $data array or string depending on setting
  1758. * @param string $query
  1759. * @return string
  1760. */
  1761. public function output_html($data, $query='') {
  1762. // should be overridden
  1763. return;
  1764. }
  1765. /**
  1766. * Function called if setting updated - cleanup, cache reset, etc.
  1767. * @param string $functionname Sets the function name
  1768. * @return void
  1769. */
  1770. public function set_updatedcallback($functionname) {
  1771. $this->updatedcallback = $functionname;
  1772. }
  1773. /**
  1774. * Execute postupdatecallback if necessary.
  1775. * @param mixed $original original value before write_setting()
  1776. * @return bool true if changed, false if not.
  1777. */
  1778. public function post_write_settings($original) {
  1779. // Comparison must work for arrays too.
  1780. if (serialize($original) === serialize($this->get_setting())) {
  1781. return false;
  1782. }
  1783. $callbackfunction = $this->updatedcallback;
  1784. if (!empty($callbackfunction) and is_callable($callbackfunction)) {
  1785. $callbackfunction($this->get_full_name());
  1786. }
  1787. return true;
  1788. }
  1789. /**
  1790. * Is setting related to query text - used when searching
  1791. * @param string $query
  1792. * @return bool
  1793. */
  1794. public function is_related($query) {
  1795. if (strpos(strtolower($this->name), $query) !== false) {
  1796. return true;
  1797. }
  1798. if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
  1799. return true;
  1800. }
  1801. if (strpos(core_text::strtolower($this->description), $query) !== false) {
  1802. return true;
  1803. }
  1804. $current = $this->get_setting();
  1805. if (!is_null($current)) {
  1806. if (is_string($current)) {
  1807. if (strpos(core_text::strtolower($current), $query) !== false) {
  1808. return true;
  1809. }
  1810. }
  1811. }
  1812. $default = $this->get_defaultsetting();
  1813. if (!is_null($default)) {
  1814. if (is_string($default)) {
  1815. if (strpos(core_text::strtolower($default), $query) !== false) {
  1816. return true;
  1817. }
  1818. }
  1819. }
  1820. return false;
  1821. }
  1822. /**
  1823. * Get whether this should be displayed in LTR mode.
  1824. *
  1825. * @return bool|null
  1826. */
  1827. public function get_force_ltr() {
  1828. return $this->forceltr;
  1829. }
  1830. /**
  1831. * Set whether to force LTR or not.
  1832. *
  1833. * @param bool $value True when forced, false when not force, null when unknown.
  1834. */
  1835. public function set_force_ltr($value) {
  1836. $this->forceltr = $value;
  1837. }
  1838. /**
  1839. * Add a setting to the list of those that could cause this one to be hidden
  1840. * @param string $dependenton
  1841. */
  1842. public function add_dependent_on($dependenton) {
  1843. $this->dependenton[] = $dependenton;
  1844. }
  1845. /**
  1846. * Get a list of the settings that could cause this one to be hidden.
  1847. * @return array
  1848. */
  1849. public function get_dependent_on() {
  1850. return $this->dependenton;
  1851. }
  1852. }
  1853. /**
  1854. * An additional option that can be applied to an admin setting.
  1855. * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
  1856. *
  1857. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1858. */
  1859. class admin_setting_flag {
  1860. /** @var bool Flag to indicate if this option can be toggled for this setting */
  1861. private $enabled = false;
  1862. /** @var bool Flag to indicate if this option defaults to true or false */
  1863. private $default = false;
  1864. /** @var string Short string used to create setting name - e.g. 'adv' */
  1865. private $shortname = '';
  1866. /** @var string String used as the label for this flag */
  1867. private $displayname = '';
  1868. /** @const Checkbox for this flag is displayed in admin page */
  1869. const ENABLED = true;
  1870. /** @const Checkbox for this flag is not displayed in admin page */
  1871. const DISABLED = false;
  1872. /**
  1873. * Constructor
  1874. *
  1875. * @param bool $enabled Can this option can be toggled.
  1876. * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
  1877. * @param bool $default The default checked state for this setting option.
  1878. * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
  1879. * @param string $displayname The displayname of this flag. Used as a label for the flag.
  1880. */
  1881. public function __construct($enabled, $default, $shortname, $displayname) {
  1882. $this->shortname = $shortname;
  1883. $this->displayname = $displayname;
  1884. $this->set_options($enabled, $default);
  1885. }
  1886. /**
  1887. * Update the values of this setting options class
  1888. *
  1889. * @param bool $enabled Can this option can be toggled.
  1890. * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
  1891. * @param bool $default The default checked state for this setting option.
  1892. */
  1893. public function set_options($enabled, $default) {
  1894. $this->enabled = $enabled;
  1895. $this->default = $default;
  1896. }
  1897. /**
  1898. * Should this option appear in the interface and be toggleable?
  1899. *
  1900. * @return bool Is it enabled?
  1901. */
  1902. public function is_enabled() {
  1903. return $this->enabled;
  1904. }
  1905. /**
  1906. * Should this option be checked by default?
  1907. *
  1908. * @return bool Is it on by default?
  1909. */
  1910. public function get_default() {
  1911. return $this->default;
  1912. }
  1913. /**
  1914. * Return the short name for this flag. e.g. 'adv' or 'locked'
  1915. *
  1916. * @return string
  1917. */
  1918. public function get_shortname() {
  1919. return $this->shortname;
  1920. }
  1921. /**
  1922. * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
  1923. *
  1924. * @return string
  1925. */
  1926. public function get_displayname() {
  1927. return $this->displayname;
  1928. }
  1929. /**
  1930. * Save the submitted data for this flag - or set it to the default if $data is null.
  1931. *
  1932. * @param admin_setting $setting - The admin setting for this flag
  1933. * @param array $data - The data submitted from the form or null to set the default value for new installs.
  1934. * @return bool
  1935. */
  1936. public function write_setting_flag(admin_setting $setting, $data) {
  1937. $result = true;
  1938. if ($this->is_enabled()) {
  1939. if (!isset($data)) {
  1940. $value = $this->get_default();
  1941. } else {
  1942. $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
  1943. }
  1944. $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
  1945. }
  1946. return $result;
  1947. }
  1948. /**
  1949. * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
  1950. *
  1951. * @param admin_setting $setting - The admin setting for this flag
  1952. * @return string - The html for the checkbox.
  1953. */
  1954. public function output_setting_flag(admin_setting $setting) {
  1955. global $OUTPUT;
  1956. $value = $setting->get_setting_flag_value($this);
  1957. $context = new stdClass();
  1958. $context->id = $setting->get_id() . '_' . $this->get_shortname();
  1959. $context->name = $setting->get_full_name() . '_' . $this->get_shortname();
  1960. $context->value = 1;
  1961. $context->checked = $value ? true : false;
  1962. $context->label = $this->get_displayname();
  1963. return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
  1964. }
  1965. }
  1966. /**
  1967. * No setting - just heading and text.
  1968. *
  1969. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  1970. */
  1971. class admin_setting_heading extends admin_setting {
  1972. /**
  1973. * not a setting, just text
  1974. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  1975. * @param string $heading heading
  1976. * @param string $information text in box
  1977. */
  1978. public function __construct($name, $heading, $information) {
  1979. $this->nosave = true;
  1980. parent::__construct($name, $heading, $information, '');
  1981. }
  1982. /**
  1983. * Always returns true
  1984. * @return bool Always returns true
  1985. */
  1986. public function get_setting() {
  1987. return true;
  1988. }
  1989. /**
  1990. * Always returns true
  1991. * @return bool Always returns true
  1992. */
  1993. public function get_defaultsetting() {
  1994. return true;
  1995. }
  1996. /**
  1997. * Never write settings
  1998. * @return string Always returns an empty string
  1999. */
  2000. public function write_setting($data) {
  2001. // do not write any setting
  2002. return '';
  2003. }
  2004. /**
  2005. * Returns an HTML string
  2006. * @return string Returns an HTML string
  2007. */
  2008. public function output_html($data, $query='') {
  2009. global $OUTPUT;
  2010. $context = new stdClass();
  2011. $context->title = $this->visiblename;
  2012. $context->description = $this->description;
  2013. $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
  2014. return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
  2015. }
  2016. }
  2017. /**
  2018. * No setting - just name and description in same row.
  2019. *
  2020. * @copyright 2018 onwards Amaia Anabitarte
  2021. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2022. */
  2023. class admin_setting_description extends admin_setting {
  2024. /**
  2025. * Not a setting, just text
  2026. *
  2027. * @param string $name
  2028. * @param string $visiblename
  2029. * @param string $description
  2030. */
  2031. public function __construct($name, $visiblename, $description) {
  2032. $this->nosave = true;
  2033. parent::__construct($name, $visiblename, $description, '');
  2034. }
  2035. /**
  2036. * Always returns true
  2037. *
  2038. * @return bool Always returns true
  2039. */
  2040. public function get_setting() {
  2041. return true;
  2042. }
  2043. /**
  2044. * Always returns true
  2045. *
  2046. * @return bool Always returns true
  2047. */
  2048. public function get_defaultsetting() {
  2049. return true;
  2050. }
  2051. /**
  2052. * Never write settings
  2053. *
  2054. * @param mixed $data Gets converted to str for comparison against yes value
  2055. * @return string Always returns an empty string
  2056. */
  2057. public function write_setting($data) {
  2058. // Do not write any setting.
  2059. return '';
  2060. }
  2061. /**
  2062. * Returns an HTML string
  2063. *
  2064. * @param string $data
  2065. * @param string $query
  2066. * @return string Returns an HTML string
  2067. */
  2068. public function output_html($data, $query='') {
  2069. global $OUTPUT;
  2070. $context = new stdClass();
  2071. $context->title = $this->visiblename;
  2072. $context->description = $this->description;
  2073. return $OUTPUT->render_from_template('core_admin/setting_description', $context);
  2074. }
  2075. }
  2076. /**
  2077. * The most flexible setting, the user enters text.
  2078. *
  2079. * This type of field should be used for config settings which are using
  2080. * English words and are not localised (passwords, database name, list of values, ...).
  2081. *
  2082. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2083. */
  2084. class admin_setting_configtext extends admin_setting {
  2085. /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
  2086. public $paramtype;
  2087. /** @var int default field size */
  2088. public $size;
  2089. /**
  2090. * Config text constructor
  2091. *
  2092. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2093. * @param string $visiblename localised
  2094. * @param string $description long localised info
  2095. * @param string $defaultsetting
  2096. * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
  2097. * @param int $size default field size
  2098. */
  2099. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
  2100. $this->paramtype = $paramtype;
  2101. if (!is_null($size)) {
  2102. $this->size = $size;
  2103. } else {
  2104. $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
  2105. }
  2106. parent::__construct($name, $visiblename, $description, $defaultsetting);
  2107. }
  2108. /**
  2109. * Get whether this should be displayed in LTR mode.
  2110. *
  2111. * Try to guess from the PARAM type unless specifically set.
  2112. */
  2113. public function get_force_ltr() {
  2114. $forceltr = parent::get_force_ltr();
  2115. if ($forceltr === null) {
  2116. return !is_rtl_compatible($this->paramtype);
  2117. }
  2118. return $forceltr;
  2119. }
  2120. /**
  2121. * Return the setting
  2122. *
  2123. * @return mixed returns config if successful else null
  2124. */
  2125. public function get_setting() {
  2126. return $this->config_read($this->name);
  2127. }
  2128. public function write_setting($data) {
  2129. if ($this->paramtype === PARAM_INT and $data === '') {
  2130. // do not complain if '' used instead of 0
  2131. $data = 0;
  2132. }
  2133. // $data is a string
  2134. $validated = $this->validate($data);
  2135. if ($validated !== true) {
  2136. return $validated;
  2137. }
  2138. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  2139. }
  2140. /**
  2141. * Validate data before storage
  2142. * @param string data
  2143. * @return mixed true if ok string if error found
  2144. */
  2145. public function validate($data) {
  2146. // allow paramtype to be a custom regex if it is the form of /pattern/
  2147. if (preg_match('#^/.*/$#', $this->paramtype)) {
  2148. if (preg_match($this->paramtype, $data)) {
  2149. return true;
  2150. } else {
  2151. return get_string('validateerror', 'admin');
  2152. }
  2153. } else if ($this->paramtype === PARAM_RAW) {
  2154. return true;
  2155. } else {
  2156. $cleaned = clean_param($data, $this->paramtype);
  2157. if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
  2158. return true;
  2159. } else {
  2160. return get_string('validateerror', 'admin');
  2161. }
  2162. }
  2163. }
  2164. /**
  2165. * Return an XHTML string for the setting
  2166. * @return string Returns an XHTML string
  2167. */
  2168. public function output_html($data, $query='') {
  2169. global $OUTPUT;
  2170. $default = $this->get_defaultsetting();
  2171. $context = (object) [
  2172. 'size' => $this->size,
  2173. 'id' => $this->get_id(),
  2174. 'name' => $this->get_full_name(),
  2175. 'value' => $data,
  2176. 'forceltr' => $this->get_force_ltr(),
  2177. ];
  2178. $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
  2179. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  2180. }
  2181. }
  2182. /**
  2183. * Text input with a maximum length constraint.
  2184. *
  2185. * @copyright 2015 onwards Ankit Agarwal
  2186. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2187. */
  2188. class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
  2189. /** @var int maximum number of chars allowed. */
  2190. protected $maxlength;
  2191. /**
  2192. * Config text constructor
  2193. *
  2194. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  2195. * or 'myplugin/mysetting' for ones in config_plugins.
  2196. * @param string $visiblename localised
  2197. * @param string $description long localised info
  2198. * @param string $defaultsetting
  2199. * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
  2200. * @param int $size default field size
  2201. * @param mixed $maxlength int maxlength allowed, 0 for infinite.
  2202. */
  2203. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
  2204. $size=null, $maxlength = 0) {
  2205. $this->maxlength = $maxlength;
  2206. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
  2207. }
  2208. /**
  2209. * Validate data before storage
  2210. *
  2211. * @param string $data data
  2212. * @return mixed true if ok string if error found
  2213. */
  2214. public function validate($data) {
  2215. $parentvalidation = parent::validate($data);
  2216. if ($parentvalidation === true) {
  2217. if ($this->maxlength > 0) {
  2218. // Max length check.
  2219. $length = core_text::strlen($data);
  2220. if ($length > $this->maxlength) {
  2221. return get_string('maximumchars', 'moodle', $this->maxlength);
  2222. }
  2223. return true;
  2224. } else {
  2225. return true; // No max length check needed.
  2226. }
  2227. } else {
  2228. return $parentvalidation;
  2229. }
  2230. }
  2231. }
  2232. /**
  2233. * General text area without html editor.
  2234. *
  2235. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2236. */
  2237. class admin_setting_configtextarea extends admin_setting_configtext {
  2238. private $rows;
  2239. private $cols;
  2240. /**
  2241. * @param string $name
  2242. * @param string $visiblename
  2243. * @param string $description
  2244. * @param mixed $defaultsetting string or array
  2245. * @param mixed $paramtype
  2246. * @param string $cols The number of columns to make the editor
  2247. * @param string $rows The number of rows to make the editor
  2248. */
  2249. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
  2250. $this->rows = $rows;
  2251. $this->cols = $cols;
  2252. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
  2253. }
  2254. /**
  2255. * Returns an XHTML string for the editor
  2256. *
  2257. * @param string $data
  2258. * @param string $query
  2259. * @return string XHTML string for the editor
  2260. */
  2261. public function output_html($data, $query='') {
  2262. global $OUTPUT;
  2263. $default = $this->get_defaultsetting();
  2264. $defaultinfo = $default;
  2265. if (!is_null($default) and $default !== '') {
  2266. $defaultinfo = "\n".$default;
  2267. }
  2268. $context = (object) [
  2269. 'cols' => $this->cols,
  2270. 'rows' => $this->rows,
  2271. 'id' => $this->get_id(),
  2272. 'name' => $this->get_full_name(),
  2273. 'value' => $data,
  2274. 'forceltr' => $this->get_force_ltr(),
  2275. ];
  2276. $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
  2277. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
  2278. }
  2279. }
  2280. /**
  2281. * General text area with html editor.
  2282. */
  2283. class admin_setting_confightmleditor extends admin_setting_configtextarea {
  2284. /**
  2285. * @param string $name
  2286. * @param string $visiblename
  2287. * @param string $description
  2288. * @param mixed $defaultsetting string or array
  2289. * @param mixed $paramtype
  2290. */
  2291. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
  2292. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
  2293. $this->set_force_ltr(false);
  2294. editors_head_setup();
  2295. }
  2296. /**
  2297. * Returns an XHTML string for the editor
  2298. *
  2299. * @param string $data
  2300. * @param string $query
  2301. * @return string XHTML string for the editor
  2302. */
  2303. public function output_html($data, $query='') {
  2304. $editor = editors_get_preferred_editor(FORMAT_HTML);
  2305. $editor->set_text($data);
  2306. $editor->use_editor($this->get_id(), array('noclean'=>true));
  2307. return parent::output_html($data, $query);
  2308. }
  2309. /**
  2310. * Checks if data has empty html.
  2311. *
  2312. * @param string $data
  2313. * @return string Empty when no errors.
  2314. */
  2315. public function write_setting($data) {
  2316. if (trim(html_to_text($data)) === '') {
  2317. $data = '';
  2318. }
  2319. return parent::write_setting($data);
  2320. }
  2321. }
  2322. /**
  2323. * Password field, allows unmasking of password
  2324. *
  2325. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2326. */
  2327. class admin_setting_configpasswordunmask extends admin_setting_configtext {
  2328. /**
  2329. * Constructor
  2330. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2331. * @param string $visiblename localised
  2332. * @param string $description long localised info
  2333. * @param string $defaultsetting default password
  2334. */
  2335. public function __construct($name, $visiblename, $description, $defaultsetting) {
  2336. parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
  2337. }
  2338. /**
  2339. * Log config changes if necessary.
  2340. * @param string $name
  2341. * @param string $oldvalue
  2342. * @param string $value
  2343. */
  2344. protected function add_to_config_log($name, $oldvalue, $value) {
  2345. if ($value !== '') {
  2346. $value = '********';
  2347. }
  2348. if ($oldvalue !== '' and $oldvalue !== null) {
  2349. $oldvalue = '********';
  2350. }
  2351. parent::add_to_config_log($name, $oldvalue, $value);
  2352. }
  2353. /**
  2354. * Returns HTML for the field.
  2355. *
  2356. * @param string $data Value for the field
  2357. * @param string $query Passed as final argument for format_admin_setting
  2358. * @return string Rendered HTML
  2359. */
  2360. public function output_html($data, $query='') {
  2361. global $OUTPUT, $CFG;
  2362. $forced = false;
  2363. if (empty($this->plugin)) {
  2364. if (array_key_exists($this->name, $CFG->config_php_settings)) {
  2365. $forced = true;
  2366. }
  2367. } else {
  2368. if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
  2369. and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
  2370. $forced = true;
  2371. }
  2372. }
  2373. $context = (object) [
  2374. 'id' => $this->get_id(),
  2375. 'name' => $this->get_full_name(),
  2376. 'size' => $this->size,
  2377. 'value' => $forced ? null : $data,
  2378. 'forceltr' => $this->get_force_ltr(),
  2379. 'forced' => $forced
  2380. ];
  2381. $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
  2382. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
  2383. }
  2384. }
  2385. /**
  2386. * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
  2387. *
  2388. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2389. * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
  2390. */
  2391. class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
  2392. /**
  2393. * Constructor
  2394. *
  2395. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2396. * @param string $visiblename localised
  2397. * @param string $description long localised info
  2398. * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
  2399. */
  2400. public function __construct($name, $visiblename, $description, $defaultsetting) {
  2401. parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
  2402. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  2403. }
  2404. }
  2405. /**
  2406. * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
  2407. * Note: Only advanced makes sense right now - locked does not.
  2408. *
  2409. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2410. */
  2411. class admin_setting_configempty extends admin_setting_configtext {
  2412. /**
  2413. * @param string $name
  2414. * @param string $visiblename
  2415. * @param string $description
  2416. */
  2417. public function __construct($name, $visiblename, $description) {
  2418. parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
  2419. }
  2420. /**
  2421. * Returns an XHTML string for the hidden field
  2422. *
  2423. * @param string $data
  2424. * @param string $query
  2425. * @return string XHTML string for the editor
  2426. */
  2427. public function output_html($data, $query='') {
  2428. global $OUTPUT;
  2429. $context = (object) [
  2430. 'id' => $this->get_id(),
  2431. 'name' => $this->get_full_name()
  2432. ];
  2433. $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
  2434. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
  2435. }
  2436. }
  2437. /**
  2438. * Path to directory
  2439. *
  2440. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2441. */
  2442. class admin_setting_configfile extends admin_setting_configtext {
  2443. /**
  2444. * Constructor
  2445. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2446. * @param string $visiblename localised
  2447. * @param string $description long localised info
  2448. * @param string $defaultdirectory default directory location
  2449. */
  2450. public function __construct($name, $visiblename, $description, $defaultdirectory) {
  2451. parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
  2452. }
  2453. /**
  2454. * Returns XHTML for the field
  2455. *
  2456. * Returns XHTML for the field and also checks whether the file
  2457. * specified in $data exists using file_exists()
  2458. *
  2459. * @param string $data File name and path to use in value attr
  2460. * @param string $query
  2461. * @return string XHTML field
  2462. */
  2463. public function output_html($data, $query='') {
  2464. global $CFG, $OUTPUT;
  2465. $default = $this->get_defaultsetting();
  2466. $context = (object) [
  2467. 'id' => $this->get_id(),
  2468. 'name' => $this->get_full_name(),
  2469. 'size' => $this->size,
  2470. 'value' => $data,
  2471. 'showvalidity' => !empty($data),
  2472. 'valid' => $data && file_exists($data),
  2473. 'readonly' => !empty($CFG->preventexecpath),
  2474. 'forceltr' => $this->get_force_ltr(),
  2475. ];
  2476. if ($context->readonly) {
  2477. $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
  2478. }
  2479. $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
  2480. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  2481. }
  2482. /**
  2483. * Checks if execpatch has been disabled in config.php
  2484. */
  2485. public function write_setting($data) {
  2486. global $CFG;
  2487. if (!empty($CFG->preventexecpath)) {
  2488. if ($this->get_setting() === null) {
  2489. // Use default during installation.
  2490. $data = $this->get_defaultsetting();
  2491. if ($data === null) {
  2492. $data = '';
  2493. }
  2494. } else {
  2495. return '';
  2496. }
  2497. }
  2498. return parent::write_setting($data);
  2499. }
  2500. }
  2501. /**
  2502. * Path to executable file
  2503. *
  2504. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2505. */
  2506. class admin_setting_configexecutable extends admin_setting_configfile {
  2507. /**
  2508. * Returns an XHTML field
  2509. *
  2510. * @param string $data This is the value for the field
  2511. * @param string $query
  2512. * @return string XHTML field
  2513. */
  2514. public function output_html($data, $query='') {
  2515. global $CFG, $OUTPUT;
  2516. $default = $this->get_defaultsetting();
  2517. require_once("$CFG->libdir/filelib.php");
  2518. $context = (object) [
  2519. 'id' => $this->get_id(),
  2520. 'name' => $this->get_full_name(),
  2521. 'size' => $this->size,
  2522. 'value' => $data,
  2523. 'showvalidity' => !empty($data),
  2524. 'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
  2525. 'readonly' => !empty($CFG->preventexecpath),
  2526. 'forceltr' => $this->get_force_ltr()
  2527. ];
  2528. if (!empty($CFG->preventexecpath)) {
  2529. $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
  2530. }
  2531. $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
  2532. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  2533. }
  2534. }
  2535. /**
  2536. * Path to directory
  2537. *
  2538. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2539. */
  2540. class admin_setting_configdirectory extends admin_setting_configfile {
  2541. /**
  2542. * Returns an XHTML field
  2543. *
  2544. * @param string $data This is the value for the field
  2545. * @param string $query
  2546. * @return string XHTML
  2547. */
  2548. public function output_html($data, $query='') {
  2549. global $CFG, $OUTPUT;
  2550. $default = $this->get_defaultsetting();
  2551. $context = (object) [
  2552. 'id' => $this->get_id(),
  2553. 'name' => $this->get_full_name(),
  2554. 'size' => $this->size,
  2555. 'value' => $data,
  2556. 'showvalidity' => !empty($data),
  2557. 'valid' => $data && file_exists($data) && is_dir($data),
  2558. 'readonly' => !empty($CFG->preventexecpath),
  2559. 'forceltr' => $this->get_force_ltr()
  2560. ];
  2561. if (!empty($CFG->preventexecpath)) {
  2562. $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
  2563. }
  2564. $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
  2565. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  2566. }
  2567. }
  2568. /**
  2569. * Checkbox
  2570. *
  2571. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2572. */
  2573. class admin_setting_configcheckbox extends admin_setting {
  2574. /** @var string Value used when checked */
  2575. public $yes;
  2576. /** @var string Value used when not checked */
  2577. public $no;
  2578. /**
  2579. * Constructor
  2580. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2581. * @param string $visiblename localised
  2582. * @param string $description long localised info
  2583. * @param string $defaultsetting
  2584. * @param string $yes value used when checked
  2585. * @param string $no value used when not checked
  2586. */
  2587. public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
  2588. parent::__construct($name, $visiblename, $description, $defaultsetting);
  2589. $this->yes = (string)$yes;
  2590. $this->no = (string)$no;
  2591. }
  2592. /**
  2593. * Retrieves the current setting using the objects name
  2594. *
  2595. * @return string
  2596. */
  2597. public function get_setting() {
  2598. return $this->config_read($this->name);
  2599. }
  2600. /**
  2601. * Sets the value for the setting
  2602. *
  2603. * Sets the value for the setting to either the yes or no values
  2604. * of the object by comparing $data to yes
  2605. *
  2606. * @param mixed $data Gets converted to str for comparison against yes value
  2607. * @return string empty string or error
  2608. */
  2609. public function write_setting($data) {
  2610. if ((string)$data === $this->yes) { // convert to strings before comparison
  2611. $data = $this->yes;
  2612. } else {
  2613. $data = $this->no;
  2614. }
  2615. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  2616. }
  2617. /**
  2618. * Returns an XHTML checkbox field
  2619. *
  2620. * @param string $data If $data matches yes then checkbox is checked
  2621. * @param string $query
  2622. * @return string XHTML field
  2623. */
  2624. public function output_html($data, $query='') {
  2625. global $OUTPUT;
  2626. $context = (object) [
  2627. 'id' => $this->get_id(),
  2628. 'name' => $this->get_full_name(),
  2629. 'no' => $this->no,
  2630. 'value' => $this->yes,
  2631. 'checked' => (string) $data === $this->yes,
  2632. ];
  2633. $default = $this->get_defaultsetting();
  2634. if (!is_null($default)) {
  2635. if ((string)$default === $this->yes) {
  2636. $defaultinfo = get_string('checkboxyes', 'admin');
  2637. } else {
  2638. $defaultinfo = get_string('checkboxno', 'admin');
  2639. }
  2640. } else {
  2641. $defaultinfo = NULL;
  2642. }
  2643. $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
  2644. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
  2645. }
  2646. }
  2647. /**
  2648. * Multiple checkboxes, each represents different value, stored in csv format
  2649. *
  2650. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2651. */
  2652. class admin_setting_configmulticheckbox extends admin_setting {
  2653. /** @var array Array of choices value=>label */
  2654. public $choices;
  2655. /**
  2656. * Constructor: uses parent::__construct
  2657. *
  2658. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2659. * @param string $visiblename localised
  2660. * @param string $description long localised info
  2661. * @param array $defaultsetting array of selected
  2662. * @param array $choices array of $value=>$label for each checkbox
  2663. */
  2664. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  2665. $this->choices = $choices;
  2666. parent::__construct($name, $visiblename, $description, $defaultsetting);
  2667. }
  2668. /**
  2669. * This public function may be used in ancestors for lazy loading of choices
  2670. *
  2671. * @todo Check if this function is still required content commented out only returns true
  2672. * @return bool true if loaded, false if error
  2673. */
  2674. public function load_choices() {
  2675. /*
  2676. if (is_array($this->choices)) {
  2677. return true;
  2678. }
  2679. .... load choices here
  2680. */
  2681. return true;
  2682. }
  2683. /**
  2684. * Is setting related to query text - used when searching
  2685. *
  2686. * @param string $query
  2687. * @return bool true on related, false on not or failure
  2688. */
  2689. public function is_related($query) {
  2690. if (!$this->load_choices() or empty($this->choices)) {
  2691. return false;
  2692. }
  2693. if (parent::is_related($query)) {
  2694. return true;
  2695. }
  2696. foreach ($this->choices as $desc) {
  2697. if (strpos(core_text::strtolower($desc), $query) !== false) {
  2698. return true;
  2699. }
  2700. }
  2701. return false;
  2702. }
  2703. /**
  2704. * Returns the current setting if it is set
  2705. *
  2706. * @return mixed null if null, else an array
  2707. */
  2708. public function get_setting() {
  2709. $result = $this->config_read($this->name);
  2710. if (is_null($result)) {
  2711. return NULL;
  2712. }
  2713. if ($result === '') {
  2714. return array();
  2715. }
  2716. $enabled = explode(',', $result);
  2717. $setting = array();
  2718. foreach ($enabled as $option) {
  2719. $setting[$option] = 1;
  2720. }
  2721. return $setting;
  2722. }
  2723. /**
  2724. * Saves the setting(s) provided in $data
  2725. *
  2726. * @param array $data An array of data, if not array returns empty str
  2727. * @return mixed empty string on useless data or bool true=success, false=failed
  2728. */
  2729. public function write_setting($data) {
  2730. if (!is_array($data)) {
  2731. return ''; // ignore it
  2732. }
  2733. if (!$this->load_choices() or empty($this->choices)) {
  2734. return '';
  2735. }
  2736. unset($data['xxxxx']);
  2737. $result = array();
  2738. foreach ($data as $key => $value) {
  2739. if ($value and array_key_exists($key, $this->choices)) {
  2740. $result[] = $key;
  2741. }
  2742. }
  2743. return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
  2744. }
  2745. /**
  2746. * Returns XHTML field(s) as required by choices
  2747. *
  2748. * Relies on data being an array should data ever be another valid vartype with
  2749. * acceptable value this may cause a warning/error
  2750. * if (!is_array($data)) would fix the problem
  2751. *
  2752. * @todo Add vartype handling to ensure $data is an array
  2753. *
  2754. * @param array $data An array of checked values
  2755. * @param string $query
  2756. * @return string XHTML field
  2757. */
  2758. public function output_html($data, $query='') {
  2759. global $OUTPUT;
  2760. if (!$this->load_choices() or empty($this->choices)) {
  2761. return '';
  2762. }
  2763. $default = $this->get_defaultsetting();
  2764. if (is_null($default)) {
  2765. $default = array();
  2766. }
  2767. if (is_null($data)) {
  2768. $data = array();
  2769. }
  2770. $context = (object) [
  2771. 'id' => $this->get_id(),
  2772. 'name' => $this->get_full_name(),
  2773. ];
  2774. $options = array();
  2775. $defaults = array();
  2776. foreach ($this->choices as $key => $description) {
  2777. if (!empty($default[$key])) {
  2778. $defaults[] = $description;
  2779. }
  2780. $options[] = [
  2781. 'key' => $key,
  2782. 'checked' => !empty($data[$key]),
  2783. 'label' => highlightfast($query, $description)
  2784. ];
  2785. }
  2786. if (is_null($default)) {
  2787. $defaultinfo = null;
  2788. } else if (!empty($defaults)) {
  2789. $defaultinfo = implode(', ', $defaults);
  2790. } else {
  2791. $defaultinfo = get_string('none');
  2792. }
  2793. $context->options = $options;
  2794. $context->hasoptions = !empty($options);
  2795. $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
  2796. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
  2797. }
  2798. }
  2799. /**
  2800. * Multiple checkboxes 2, value stored as string 00101011
  2801. *
  2802. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2803. */
  2804. class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
  2805. /**
  2806. * Returns the setting if set
  2807. *
  2808. * @return mixed null if not set, else an array of set settings
  2809. */
  2810. public function get_setting() {
  2811. $result = $this->config_read($this->name);
  2812. if (is_null($result)) {
  2813. return NULL;
  2814. }
  2815. if (!$this->load_choices()) {
  2816. return NULL;
  2817. }
  2818. $result = str_pad($result, count($this->choices), '0');
  2819. $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
  2820. $setting = array();
  2821. foreach ($this->choices as $key=>$unused) {
  2822. $value = array_shift($result);
  2823. if ($value) {
  2824. $setting[$key] = 1;
  2825. }
  2826. }
  2827. return $setting;
  2828. }
  2829. /**
  2830. * Save setting(s) provided in $data param
  2831. *
  2832. * @param array $data An array of settings to save
  2833. * @return mixed empty string for bad data or bool true=>success, false=>error
  2834. */
  2835. public function write_setting($data) {
  2836. if (!is_array($data)) {
  2837. return ''; // ignore it
  2838. }
  2839. if (!$this->load_choices() or empty($this->choices)) {
  2840. return '';
  2841. }
  2842. $result = '';
  2843. foreach ($this->choices as $key=>$unused) {
  2844. if (!empty($data[$key])) {
  2845. $result .= '1';
  2846. } else {
  2847. $result .= '0';
  2848. }
  2849. }
  2850. return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
  2851. }
  2852. }
  2853. /**
  2854. * Select one value from list
  2855. *
  2856. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  2857. */
  2858. class admin_setting_configselect extends admin_setting {
  2859. /** @var array Array of choices value=>label */
  2860. public $choices;
  2861. /** @var array Array of choices grouped using optgroups */
  2862. public $optgroups;
  2863. /**
  2864. * Constructor
  2865. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  2866. * @param string $visiblename localised
  2867. * @param string $description long localised info
  2868. * @param string|int $defaultsetting
  2869. * @param array $choices array of $value=>$label for each selection
  2870. */
  2871. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  2872. // Look for optgroup and single options.
  2873. if (is_array($choices)) {
  2874. $this->choices = [];
  2875. foreach ($choices as $key => $val) {
  2876. if (is_array($val)) {
  2877. $this->optgroups[$key] = $val;
  2878. $this->choices = array_merge($this->choices, $val);
  2879. } else {
  2880. $this->choices[$key] = $val;
  2881. }
  2882. }
  2883. }
  2884. parent::__construct($name, $visiblename, $description, $defaultsetting);
  2885. }
  2886. /**
  2887. * This function may be used in ancestors for lazy loading of choices
  2888. *
  2889. * Override this method if loading of choices is expensive, such
  2890. * as when it requires multiple db requests.
  2891. *
  2892. * @return bool true if loaded, false if error
  2893. */
  2894. public function load_choices() {
  2895. /*
  2896. if (is_array($this->choices)) {
  2897. return true;
  2898. }
  2899. .... load choices here
  2900. */
  2901. return true;
  2902. }
  2903. /**
  2904. * Check if this is $query is related to a choice
  2905. *
  2906. * @param string $query
  2907. * @return bool true if related, false if not
  2908. */
  2909. public function is_related($query) {
  2910. if (parent::is_related($query)) {
  2911. return true;
  2912. }
  2913. if (!$this->load_choices()) {
  2914. return false;
  2915. }
  2916. foreach ($this->choices as $key=>$value) {
  2917. if (strpos(core_text::strtolower($key), $query) !== false) {
  2918. return true;
  2919. }
  2920. if (strpos(core_text::strtolower($value), $query) !== false) {
  2921. return true;
  2922. }
  2923. }
  2924. return false;
  2925. }
  2926. /**
  2927. * Return the setting
  2928. *
  2929. * @return mixed returns config if successful else null
  2930. */
  2931. public function get_setting() {
  2932. return $this->config_read($this->name);
  2933. }
  2934. /**
  2935. * Save a setting
  2936. *
  2937. * @param string $data
  2938. * @return string empty of error string
  2939. */
  2940. public function write_setting($data) {
  2941. if (!$this->load_choices() or empty($this->choices)) {
  2942. return '';
  2943. }
  2944. if (!array_key_exists($data, $this->choices)) {
  2945. return ''; // ignore it
  2946. }
  2947. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  2948. }
  2949. /**
  2950. * Returns XHTML select field
  2951. *
  2952. * Ensure the options are loaded, and generate the XHTML for the select
  2953. * element and any warning message. Separating this out from output_html
  2954. * makes it easier to subclass this class.
  2955. *
  2956. * @param string $data the option to show as selected.
  2957. * @param string $current the currently selected option in the database, null if none.
  2958. * @param string $default the default selected option.
  2959. * @return array the HTML for the select element, and a warning message.
  2960. * @deprecated since Moodle 3.2
  2961. */
  2962. public function output_select_html($data, $current, $default, $extraname = '') {
  2963. debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
  2964. }
  2965. /**
  2966. * Returns XHTML select field and wrapping div(s)
  2967. *
  2968. * @see output_select_html()
  2969. *
  2970. * @param string $data the option to show as selected
  2971. * @param string $query
  2972. * @return string XHTML field and wrapping div
  2973. */
  2974. public function output_html($data, $query='') {
  2975. global $OUTPUT;
  2976. $default = $this->get_defaultsetting();
  2977. $current = $this->get_setting();
  2978. if (!$this->load_choices() || empty($this->choices)) {
  2979. return '';
  2980. }
  2981. $context = (object) [
  2982. 'id' => $this->get_id(),
  2983. 'name' => $this->get_full_name(),
  2984. ];
  2985. if (!is_null($default) && array_key_exists($default, $this->choices)) {
  2986. $defaultinfo = $this->choices[$default];
  2987. } else {
  2988. $defaultinfo = NULL;
  2989. }
  2990. // Warnings.
  2991. $warning = '';
  2992. if ($current === null) {
  2993. // First run.
  2994. } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
  2995. // No warning.
  2996. } else if (!array_key_exists($current, $this->choices)) {
  2997. $warning = get_string('warningcurrentsetting', 'admin', $current);
  2998. if (!is_null($default) && $data == $current) {
  2999. $data = $default; // Use default instead of first value when showing the form.
  3000. }
  3001. }
  3002. $options = [];
  3003. $template = 'core_admin/setting_configselect';
  3004. if (!empty($this->optgroups)) {
  3005. $optgroups = [];
  3006. foreach ($this->optgroups as $label => $choices) {
  3007. $optgroup = array('label' => $label, 'options' => []);
  3008. foreach ($choices as $value => $name) {
  3009. $optgroup['options'][] = [
  3010. 'value' => $value,
  3011. 'name' => $name,
  3012. 'selected' => (string) $value == $data
  3013. ];
  3014. unset($this->choices[$value]);
  3015. }
  3016. $optgroups[] = $optgroup;
  3017. }
  3018. $context->options = $options;
  3019. $context->optgroups = $optgroups;
  3020. $template = 'core_admin/setting_configselect_optgroup';
  3021. }
  3022. foreach ($this->choices as $value => $name) {
  3023. $options[] = [
  3024. 'value' => $value,
  3025. 'name' => $name,
  3026. 'selected' => (string) $value == $data
  3027. ];
  3028. }
  3029. $context->options = $options;
  3030. $element = $OUTPUT->render_from_template($template, $context);
  3031. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
  3032. }
  3033. }
  3034. /**
  3035. * Select multiple items from list
  3036. *
  3037. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3038. */
  3039. class admin_setting_configmultiselect extends admin_setting_configselect {
  3040. /**
  3041. * Constructor
  3042. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  3043. * @param string $visiblename localised
  3044. * @param string $description long localised info
  3045. * @param array $defaultsetting array of selected items
  3046. * @param array $choices array of $value=>$label for each list item
  3047. */
  3048. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  3049. parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
  3050. }
  3051. /**
  3052. * Returns the select setting(s)
  3053. *
  3054. * @return mixed null or array. Null if no settings else array of setting(s)
  3055. */
  3056. public function get_setting() {
  3057. $result = $this->config_read($this->name);
  3058. if (is_null($result)) {
  3059. return NULL;
  3060. }
  3061. if ($result === '') {
  3062. return array();
  3063. }
  3064. return explode(',', $result);
  3065. }
  3066. /**
  3067. * Saves setting(s) provided through $data
  3068. *
  3069. * Potential bug in the works should anyone call with this function
  3070. * using a vartype that is not an array
  3071. *
  3072. * @param array $data
  3073. */
  3074. public function write_setting($data) {
  3075. if (!is_array($data)) {
  3076. return ''; //ignore it
  3077. }
  3078. if (!$this->load_choices() or empty($this->choices)) {
  3079. return '';
  3080. }
  3081. unset($data['xxxxx']);
  3082. $save = array();
  3083. foreach ($data as $value) {
  3084. if (!array_key_exists($value, $this->choices)) {
  3085. continue; // ignore it
  3086. }
  3087. $save[] = $value;
  3088. }
  3089. return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
  3090. }
  3091. /**
  3092. * Is setting related to query text - used when searching
  3093. *
  3094. * @param string $query
  3095. * @return bool true if related, false if not
  3096. */
  3097. public function is_related($query) {
  3098. if (!$this->load_choices() or empty($this->choices)) {
  3099. return false;
  3100. }
  3101. if (parent::is_related($query)) {
  3102. return true;
  3103. }
  3104. foreach ($this->choices as $desc) {
  3105. if (strpos(core_text::strtolower($desc), $query) !== false) {
  3106. return true;
  3107. }
  3108. }
  3109. return false;
  3110. }
  3111. /**
  3112. * Returns XHTML multi-select field
  3113. *
  3114. * @todo Add vartype handling to ensure $data is an array
  3115. * @param array $data Array of values to select by default
  3116. * @param string $query
  3117. * @return string XHTML multi-select field
  3118. */
  3119. public function output_html($data, $query='') {
  3120. global $OUTPUT;
  3121. if (!$this->load_choices() or empty($this->choices)) {
  3122. return '';
  3123. }
  3124. $default = $this->get_defaultsetting();
  3125. if (is_null($default)) {
  3126. $default = array();
  3127. }
  3128. if (is_null($data)) {
  3129. $data = array();
  3130. }
  3131. $context = (object) [
  3132. 'id' => $this->get_id(),
  3133. 'name' => $this->get_full_name(),
  3134. 'size' => min(10, count($this->choices))
  3135. ];
  3136. $defaults = [];
  3137. $options = [];
  3138. $template = 'core_admin/setting_configmultiselect';
  3139. if (!empty($this->optgroups)) {
  3140. $optgroups = [];
  3141. foreach ($this->optgroups as $label => $choices) {
  3142. $optgroup = array('label' => $label, 'options' => []);
  3143. foreach ($choices as $value => $name) {
  3144. if (in_array($value, $default)) {
  3145. $defaults[] = $name;
  3146. }
  3147. $optgroup['options'][] = [
  3148. 'value' => $value,
  3149. 'name' => $name,
  3150. 'selected' => in_array($value, $data)
  3151. ];
  3152. unset($this->choices[$value]);
  3153. }
  3154. $optgroups[] = $optgroup;
  3155. }
  3156. $context->optgroups = $optgroups;
  3157. $template = 'core_admin/setting_configmultiselect_optgroup';
  3158. }
  3159. foreach ($this->choices as $value => $name) {
  3160. if (in_array($value, $default)) {
  3161. $defaults[] = $name;
  3162. }
  3163. $options[] = [
  3164. 'value' => $value,
  3165. 'name' => $name,
  3166. 'selected' => in_array($value, $data)
  3167. ];
  3168. }
  3169. $context->options = $options;
  3170. if (is_null($default)) {
  3171. $defaultinfo = NULL;
  3172. } if (!empty($defaults)) {
  3173. $defaultinfo = implode(', ', $defaults);
  3174. } else {
  3175. $defaultinfo = get_string('none');
  3176. }
  3177. $element = $OUTPUT->render_from_template($template, $context);
  3178. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
  3179. }
  3180. }
  3181. /**
  3182. * Time selector
  3183. *
  3184. * This is a liiitle bit messy. we're using two selects, but we're returning
  3185. * them as an array named after $name (so we only use $name2 internally for the setting)
  3186. *
  3187. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3188. */
  3189. class admin_setting_configtime extends admin_setting {
  3190. /** @var string Used for setting second select (minutes) */
  3191. public $name2;
  3192. /**
  3193. * Constructor
  3194. * @param string $hoursname setting for hours
  3195. * @param string $minutesname setting for hours
  3196. * @param string $visiblename localised
  3197. * @param string $description long localised info
  3198. * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
  3199. */
  3200. public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
  3201. $this->name2 = $minutesname;
  3202. parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
  3203. }
  3204. /**
  3205. * Get the selected time
  3206. *
  3207. * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
  3208. */
  3209. public function get_setting() {
  3210. $result1 = $this->config_read($this->name);
  3211. $result2 = $this->config_read($this->name2);
  3212. if (is_null($result1) or is_null($result2)) {
  3213. return NULL;
  3214. }
  3215. return array('h' => $result1, 'm' => $result2);
  3216. }
  3217. /**
  3218. * Store the time (hours and minutes)
  3219. *
  3220. * @param array $data Must be form 'h'=>xx, 'm'=>xx
  3221. * @return bool true if success, false if not
  3222. */
  3223. public function write_setting($data) {
  3224. if (!is_array($data)) {
  3225. return '';
  3226. }
  3227. $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
  3228. return ($result ? '' : get_string('errorsetting', 'admin'));
  3229. }
  3230. /**
  3231. * Returns XHTML time select fields
  3232. *
  3233. * @param array $data Must be form 'h'=>xx, 'm'=>xx
  3234. * @param string $query
  3235. * @return string XHTML time select fields and wrapping div(s)
  3236. */
  3237. public function output_html($data, $query='') {
  3238. global $OUTPUT;
  3239. $default = $this->get_defaultsetting();
  3240. if (is_array($default)) {
  3241. $defaultinfo = $default['h'].':'.$default['m'];
  3242. } else {
  3243. $defaultinfo = NULL;
  3244. }
  3245. $context = (object) [
  3246. 'id' => $this->get_id(),
  3247. 'name' => $this->get_full_name(),
  3248. 'hours' => array_map(function($i) use ($data) {
  3249. return [
  3250. 'value' => $i,
  3251. 'name' => $i,
  3252. 'selected' => $i == $data['h']
  3253. ];
  3254. }, range(0, 23)),
  3255. 'minutes' => array_map(function($i) use ($data) {
  3256. return [
  3257. 'value' => $i,
  3258. 'name' => $i,
  3259. 'selected' => $i == $data['m']
  3260. ];
  3261. }, range(0, 59, 5))
  3262. ];
  3263. $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
  3264. return format_admin_setting($this, $this->visiblename, $element, $this->description,
  3265. $this->get_id() . 'h', '', $defaultinfo, $query);
  3266. }
  3267. }
  3268. /**
  3269. * Seconds duration setting.
  3270. *
  3271. * @copyright 2012 Petr Skoda (http://skodak.org)
  3272. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3273. */
  3274. class admin_setting_configduration extends admin_setting {
  3275. /** @var int default duration unit */
  3276. protected $defaultunit;
  3277. /**
  3278. * Constructor
  3279. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  3280. * or 'myplugin/mysetting' for ones in config_plugins.
  3281. * @param string $visiblename localised name
  3282. * @param string $description localised long description
  3283. * @param mixed $defaultsetting string or array depending on implementation
  3284. * @param int $defaultunit - day, week, etc. (in seconds)
  3285. */
  3286. public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
  3287. if (is_number($defaultsetting)) {
  3288. $defaultsetting = self::parse_seconds($defaultsetting);
  3289. }
  3290. $units = self::get_units();
  3291. if (isset($units[$defaultunit])) {
  3292. $this->defaultunit = $defaultunit;
  3293. } else {
  3294. $this->defaultunit = 86400;
  3295. }
  3296. parent::__construct($name, $visiblename, $description, $defaultsetting);
  3297. }
  3298. /**
  3299. * Returns selectable units.
  3300. * @static
  3301. * @return array
  3302. */
  3303. protected static function get_units() {
  3304. return array(
  3305. 604800 => get_string('weeks'),
  3306. 86400 => get_string('days'),
  3307. 3600 => get_string('hours'),
  3308. 60 => get_string('minutes'),
  3309. 1 => get_string('seconds'),
  3310. );
  3311. }
  3312. /**
  3313. * Converts seconds to some more user friendly string.
  3314. * @static
  3315. * @param int $seconds
  3316. * @return string
  3317. */
  3318. protected static function get_duration_text($seconds) {
  3319. if (empty($seconds)) {
  3320. return get_string('none');
  3321. }
  3322. $data = self::parse_seconds($seconds);
  3323. switch ($data['u']) {
  3324. case (60*60*24*7):
  3325. return get_string('numweeks', '', $data['v']);
  3326. case (60*60*24):
  3327. return get_string('numdays', '', $data['v']);
  3328. case (60*60):
  3329. return get_string('numhours', '', $data['v']);
  3330. case (60):
  3331. return get_string('numminutes', '', $data['v']);
  3332. default:
  3333. return get_string('numseconds', '', $data['v']*$data['u']);
  3334. }
  3335. }
  3336. /**
  3337. * Finds suitable units for given duration.
  3338. * @static
  3339. * @param int $seconds
  3340. * @return array
  3341. */
  3342. protected static function parse_seconds($seconds) {
  3343. foreach (self::get_units() as $unit => $unused) {
  3344. if ($seconds % $unit === 0) {
  3345. return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
  3346. }
  3347. }
  3348. return array('v'=>(int)$seconds, 'u'=>1);
  3349. }
  3350. /**
  3351. * Get the selected duration as array.
  3352. *
  3353. * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
  3354. */
  3355. public function get_setting() {
  3356. $seconds = $this->config_read($this->name);
  3357. if (is_null($seconds)) {
  3358. return null;
  3359. }
  3360. return self::parse_seconds($seconds);
  3361. }
  3362. /**
  3363. * Store the duration as seconds.
  3364. *
  3365. * @param array $data Must be form 'h'=>xx, 'm'=>xx
  3366. * @return bool true if success, false if not
  3367. */
  3368. public function write_setting($data) {
  3369. if (!is_array($data)) {
  3370. return '';
  3371. }
  3372. $seconds = (int)($data['v']*$data['u']);
  3373. if ($seconds < 0) {
  3374. return get_string('errorsetting', 'admin');
  3375. }
  3376. $result = $this->config_write($this->name, $seconds);
  3377. return ($result ? '' : get_string('errorsetting', 'admin'));
  3378. }
  3379. /**
  3380. * Returns duration text+select fields.
  3381. *
  3382. * @param array $data Must be form 'v'=>xx, 'u'=>xx
  3383. * @param string $query
  3384. * @return string duration text+select fields and wrapping div(s)
  3385. */
  3386. public function output_html($data, $query='') {
  3387. global $OUTPUT;
  3388. $default = $this->get_defaultsetting();
  3389. if (is_number($default)) {
  3390. $defaultinfo = self::get_duration_text($default);
  3391. } else if (is_array($default)) {
  3392. $defaultinfo = self::get_duration_text($default['v']*$default['u']);
  3393. } else {
  3394. $defaultinfo = null;
  3395. }
  3396. $inputid = $this->get_id() . 'v';
  3397. $units = self::get_units();
  3398. $defaultunit = $this->defaultunit;
  3399. $context = (object) [
  3400. 'id' => $this->get_id(),
  3401. 'name' => $this->get_full_name(),
  3402. 'value' => $data['v'],
  3403. 'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
  3404. return [
  3405. 'value' => $unit,
  3406. 'name' => $units[$unit],
  3407. 'selected' => ($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']
  3408. ];
  3409. }, array_keys($units))
  3410. ];
  3411. $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
  3412. return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
  3413. }
  3414. }
  3415. /**
  3416. * Seconds duration setting with an advanced checkbox, that controls a additional
  3417. * $name.'_adv' setting.
  3418. *
  3419. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3420. * @copyright 2014 The Open University
  3421. */
  3422. class admin_setting_configduration_with_advanced extends admin_setting_configduration {
  3423. /**
  3424. * Constructor
  3425. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  3426. * or 'myplugin/mysetting' for ones in config_plugins.
  3427. * @param string $visiblename localised name
  3428. * @param string $description localised long description
  3429. * @param array $defaultsetting array of int value, and bool whether it is
  3430. * is advanced by default.
  3431. * @param int $defaultunit - day, week, etc. (in seconds)
  3432. */
  3433. public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
  3434. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
  3435. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  3436. }
  3437. }
  3438. /**
  3439. * Used to validate a textarea used for ip addresses
  3440. *
  3441. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3442. * @copyright 2011 Petr Skoda (http://skodak.org)
  3443. */
  3444. class admin_setting_configiplist extends admin_setting_configtextarea {
  3445. /**
  3446. * Validate the contents of the textarea as IP addresses
  3447. *
  3448. * Used to validate a new line separated list of IP addresses collected from
  3449. * a textarea control
  3450. *
  3451. * @param string $data A list of IP Addresses separated by new lines
  3452. * @return mixed bool true for success or string:error on failure
  3453. */
  3454. public function validate($data) {
  3455. if(!empty($data)) {
  3456. $lines = explode("\n", $data);
  3457. } else {
  3458. return true;
  3459. }
  3460. $result = true;
  3461. $badips = array();
  3462. foreach ($lines as $line) {
  3463. $tokens = explode('#', $line);
  3464. $ip = trim($tokens[0]);
  3465. if (empty($ip)) {
  3466. continue;
  3467. }
  3468. if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
  3469. preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
  3470. preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
  3471. } else {
  3472. $result = false;
  3473. $badips[] = $ip;
  3474. }
  3475. }
  3476. if($result) {
  3477. return true;
  3478. } else {
  3479. return get_string('validateiperror', 'admin', join(', ', $badips));
  3480. }
  3481. }
  3482. }
  3483. /**
  3484. * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
  3485. *
  3486. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3487. * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
  3488. */
  3489. class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
  3490. /**
  3491. * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
  3492. * Used to validate a new line separated list of entries collected from a textarea control.
  3493. *
  3494. * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
  3495. * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
  3496. * via the get_setting() method, which has been overriden.
  3497. *
  3498. * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
  3499. * @return mixed bool true for success or string:error on failure
  3500. */
  3501. public function validate($data) {
  3502. if (empty($data)) {
  3503. return true;
  3504. }
  3505. $entries = explode("\n", $data);
  3506. $badentries = [];
  3507. foreach ($entries as $key => $entry) {
  3508. $entry = trim($entry);
  3509. if (empty($entry)) {
  3510. return get_string('validateemptylineerror', 'admin');
  3511. }
  3512. // Validate each string entry against the supported formats.
  3513. if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
  3514. || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
  3515. || \core\ip_utils::is_domain_matching_pattern($entry)) {
  3516. continue;
  3517. }
  3518. // Otherwise, the entry is invalid.
  3519. $badentries[] = $entry;
  3520. }
  3521. if ($badentries) {
  3522. return get_string('validateerrorlist', 'admin', join(', ', $badentries));
  3523. }
  3524. return true;
  3525. }
  3526. /**
  3527. * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
  3528. *
  3529. * @param string $data the setting data, as sent from the web form.
  3530. * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
  3531. */
  3532. protected function ace_encode($data) {
  3533. if (empty($data)) {
  3534. return $data;
  3535. }
  3536. $entries = explode("\n", $data);
  3537. foreach ($entries as $key => $entry) {
  3538. $entry = trim($entry);
  3539. // This regex matches any string that has non-ascii character.
  3540. if (preg_match('/[^\x00-\x7f]/', $entry)) {
  3541. // If we can convert the unicode string to an idn, do so.
  3542. // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
  3543. $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
  3544. $entries[$key] = $val ? $val : $entry;
  3545. }
  3546. }
  3547. return implode("\n", $entries);
  3548. }
  3549. /**
  3550. * Decode any ascii-encoded domain names back to their utf-8 representation for display.
  3551. *
  3552. * @param string $data the setting data, as found in the database.
  3553. * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
  3554. */
  3555. protected function ace_decode($data) {
  3556. $entries = explode("\n", $data);
  3557. foreach ($entries as $key => $entry) {
  3558. $entry = trim($entry);
  3559. if (strpos($entry, 'xn--') !== false) {
  3560. $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
  3561. }
  3562. }
  3563. return implode("\n", $entries);
  3564. }
  3565. /**
  3566. * Override, providing utf8-decoding for ascii-encoded IDN strings.
  3567. *
  3568. * @return mixed returns punycode-converted setting string if successful, else null.
  3569. */
  3570. public function get_setting() {
  3571. // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
  3572. $data = $this->config_read($this->name);
  3573. if (function_exists('idn_to_utf8') && !is_null($data)) {
  3574. $data = $this->ace_decode($data);
  3575. }
  3576. return $data;
  3577. }
  3578. /**
  3579. * Override, providing ascii-encoding for utf8 (native) IDN strings.
  3580. *
  3581. * @param string $data
  3582. * @return string
  3583. */
  3584. public function write_setting($data) {
  3585. if ($this->paramtype === PARAM_INT and $data === '') {
  3586. // Do not complain if '' used instead of 0.
  3587. $data = 0;
  3588. }
  3589. // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
  3590. if (function_exists('idn_to_ascii')) {
  3591. $data = $this->ace_encode($data);
  3592. }
  3593. $validated = $this->validate($data);
  3594. if ($validated !== true) {
  3595. return $validated;
  3596. }
  3597. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  3598. }
  3599. }
  3600. /**
  3601. * Used to validate a textarea used for port numbers.
  3602. *
  3603. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3604. * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
  3605. */
  3606. class admin_setting_configportlist extends admin_setting_configtextarea {
  3607. /**
  3608. * Validate the contents of the textarea as port numbers.
  3609. * Used to validate a new line separated list of ports collected from a textarea control.
  3610. *
  3611. * @param string $data A list of ports separated by new lines
  3612. * @return mixed bool true for success or string:error on failure
  3613. */
  3614. public function validate($data) {
  3615. if (empty($data)) {
  3616. return true;
  3617. }
  3618. $ports = explode("\n", $data);
  3619. $badentries = [];
  3620. foreach ($ports as $port) {
  3621. $port = trim($port);
  3622. if (empty($port)) {
  3623. return get_string('validateemptylineerror', 'admin');
  3624. }
  3625. // Is the string a valid integer number?
  3626. if (strval(intval($port)) !== $port || intval($port) <= 0) {
  3627. $badentries[] = $port;
  3628. }
  3629. }
  3630. if ($badentries) {
  3631. return get_string('validateerrorlist', 'admin', $badentries);
  3632. }
  3633. return true;
  3634. }
  3635. }
  3636. /**
  3637. * An admin setting for selecting one or more users who have a capability
  3638. * in the system context
  3639. *
  3640. * An admin setting for selecting one or more users, who have a particular capability
  3641. * in the system context. Warning, make sure the list will never be too long. There is
  3642. * no paging or searching of this list.
  3643. *
  3644. * To correctly get a list of users from this config setting, you need to call the
  3645. * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
  3646. *
  3647. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3648. */
  3649. class admin_setting_users_with_capability extends admin_setting_configmultiselect {
  3650. /** @var string The capabilities name */
  3651. protected $capability;
  3652. /** @var int include admin users too */
  3653. protected $includeadmins;
  3654. /**
  3655. * Constructor.
  3656. *
  3657. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  3658. * @param string $visiblename localised name
  3659. * @param string $description localised long description
  3660. * @param array $defaultsetting array of usernames
  3661. * @param string $capability string capability name.
  3662. * @param bool $includeadmins include administrators
  3663. */
  3664. function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
  3665. $this->capability = $capability;
  3666. $this->includeadmins = $includeadmins;
  3667. parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
  3668. }
  3669. /**
  3670. * Load all of the uses who have the capability into choice array
  3671. *
  3672. * @return bool Always returns true
  3673. */
  3674. function load_choices() {
  3675. if (is_array($this->choices)) {
  3676. return true;
  3677. }
  3678. list($sort, $sortparams) = users_order_by_sql('u');
  3679. if (!empty($sortparams)) {
  3680. throw new coding_exception('users_order_by_sql returned some query parameters. ' .
  3681. 'This is unexpected, and a problem because there is no way to pass these ' .
  3682. 'parameters to get_users_by_capability. See MDL-34657.');
  3683. }
  3684. $userfields = 'u.id, u.username, ' . get_all_user_name_fields(true, 'u');
  3685. $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
  3686. $this->choices = array(
  3687. '$@NONE@$' => get_string('nobody'),
  3688. '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
  3689. );
  3690. if ($this->includeadmins) {
  3691. $admins = get_admins();
  3692. foreach ($admins as $user) {
  3693. $this->choices[$user->id] = fullname($user);
  3694. }
  3695. }
  3696. if (is_array($users)) {
  3697. foreach ($users as $user) {
  3698. $this->choices[$user->id] = fullname($user);
  3699. }
  3700. }
  3701. return true;
  3702. }
  3703. /**
  3704. * Returns the default setting for class
  3705. *
  3706. * @return mixed Array, or string. Empty string if no default
  3707. */
  3708. public function get_defaultsetting() {
  3709. $this->load_choices();
  3710. $defaultsetting = parent::get_defaultsetting();
  3711. if (empty($defaultsetting)) {
  3712. return array('$@NONE@$');
  3713. } else if (array_key_exists($defaultsetting, $this->choices)) {
  3714. return $defaultsetting;
  3715. } else {
  3716. return '';
  3717. }
  3718. }
  3719. /**
  3720. * Returns the current setting
  3721. *
  3722. * @return mixed array or string
  3723. */
  3724. public function get_setting() {
  3725. $result = parent::get_setting();
  3726. if ($result === null) {
  3727. // this is necessary for settings upgrade
  3728. return null;
  3729. }
  3730. if (empty($result)) {
  3731. $result = array('$@NONE@$');
  3732. }
  3733. return $result;
  3734. }
  3735. /**
  3736. * Save the chosen setting provided as $data
  3737. *
  3738. * @param array $data
  3739. * @return mixed string or array
  3740. */
  3741. public function write_setting($data) {
  3742. // If all is selected, remove any explicit options.
  3743. if (in_array('$@ALL@$', $data)) {
  3744. $data = array('$@ALL@$');
  3745. }
  3746. // None never needs to be written to the DB.
  3747. if (in_array('$@NONE@$', $data)) {
  3748. unset($data[array_search('$@NONE@$', $data)]);
  3749. }
  3750. return parent::write_setting($data);
  3751. }
  3752. }
  3753. /**
  3754. * Special checkbox for calendar - resets SESSION vars.
  3755. *
  3756. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3757. */
  3758. class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
  3759. /**
  3760. * Calls the parent::__construct with default values
  3761. *
  3762. * name => calendar_adminseesall
  3763. * visiblename => get_string('adminseesall', 'admin')
  3764. * description => get_string('helpadminseesall', 'admin')
  3765. * defaultsetting => 0
  3766. */
  3767. public function __construct() {
  3768. parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
  3769. get_string('helpadminseesall', 'admin'), '0');
  3770. }
  3771. /**
  3772. * Stores the setting passed in $data
  3773. *
  3774. * @param mixed gets converted to string for comparison
  3775. * @return string empty string or error message
  3776. */
  3777. public function write_setting($data) {
  3778. global $SESSION;
  3779. return parent::write_setting($data);
  3780. }
  3781. }
  3782. /**
  3783. * Special select for settings that are altered in setup.php and can not be altered on the fly
  3784. *
  3785. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3786. */
  3787. class admin_setting_special_selectsetup extends admin_setting_configselect {
  3788. /**
  3789. * Reads the setting directly from the database
  3790. *
  3791. * @return mixed
  3792. */
  3793. public function get_setting() {
  3794. // read directly from db!
  3795. return get_config(NULL, $this->name);
  3796. }
  3797. /**
  3798. * Save the setting passed in $data
  3799. *
  3800. * @param string $data The setting to save
  3801. * @return string empty or error message
  3802. */
  3803. public function write_setting($data) {
  3804. global $CFG;
  3805. // do not change active CFG setting!
  3806. $current = $CFG->{$this->name};
  3807. $result = parent::write_setting($data);
  3808. $CFG->{$this->name} = $current;
  3809. return $result;
  3810. }
  3811. }
  3812. /**
  3813. * Special select for frontpage - stores data in course table
  3814. *
  3815. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3816. */
  3817. class admin_setting_sitesetselect extends admin_setting_configselect {
  3818. /**
  3819. * Returns the site name for the selected site
  3820. *
  3821. * @see get_site()
  3822. * @return string The site name of the selected site
  3823. */
  3824. public function get_setting() {
  3825. $site = course_get_format(get_site())->get_course();
  3826. return $site->{$this->name};
  3827. }
  3828. /**
  3829. * Updates the database and save the setting
  3830. *
  3831. * @param string data
  3832. * @return string empty or error message
  3833. */
  3834. public function write_setting($data) {
  3835. global $DB, $SITE, $COURSE;
  3836. if (!in_array($data, array_keys($this->choices))) {
  3837. return get_string('errorsetting', 'admin');
  3838. }
  3839. $record = new stdClass();
  3840. $record->id = SITEID;
  3841. $temp = $this->name;
  3842. $record->$temp = $data;
  3843. $record->timemodified = time();
  3844. course_get_format($SITE)->update_course_format_options($record);
  3845. $DB->update_record('course', $record);
  3846. // Reset caches.
  3847. $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
  3848. if ($SITE->id == $COURSE->id) {
  3849. $COURSE = $SITE;
  3850. }
  3851. format_base::reset_course_cache($SITE->id);
  3852. return '';
  3853. }
  3854. }
  3855. /**
  3856. * Select for blog's bloglevel setting: if set to 0, will set blog_menu
  3857. * block to hidden.
  3858. *
  3859. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3860. */
  3861. class admin_setting_bloglevel extends admin_setting_configselect {
  3862. /**
  3863. * Updates the database and save the setting
  3864. *
  3865. * @param string data
  3866. * @return string empty or error message
  3867. */
  3868. public function write_setting($data) {
  3869. global $DB, $CFG;
  3870. if ($data == 0) {
  3871. $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
  3872. foreach ($blogblocks as $block) {
  3873. $DB->set_field('block', 'visible', 0, array('id' => $block->id));
  3874. }
  3875. } else {
  3876. // reenable all blocks only when switching from disabled blogs
  3877. if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
  3878. $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
  3879. foreach ($blogblocks as $block) {
  3880. $DB->set_field('block', 'visible', 1, array('id' => $block->id));
  3881. }
  3882. }
  3883. }
  3884. return parent::write_setting($data);
  3885. }
  3886. }
  3887. /**
  3888. * Special select - lists on the frontpage - hacky
  3889. *
  3890. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  3891. */
  3892. class admin_setting_courselist_frontpage extends admin_setting {
  3893. /** @var array Array of choices value=>label */
  3894. public $choices;
  3895. /**
  3896. * Construct override, requires one param
  3897. *
  3898. * @param bool $loggedin Is the user logged in
  3899. */
  3900. public function __construct($loggedin) {
  3901. global $CFG;
  3902. require_once($CFG->dirroot.'/course/lib.php');
  3903. $name = 'frontpage'.($loggedin ? 'loggedin' : '');
  3904. $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
  3905. $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
  3906. $defaults = array(FRONTPAGEALLCOURSELIST);
  3907. parent::__construct($name, $visiblename, $description, $defaults);
  3908. }
  3909. /**
  3910. * Loads the choices available
  3911. *
  3912. * @return bool always returns true
  3913. */
  3914. public function load_choices() {
  3915. if (is_array($this->choices)) {
  3916. return true;
  3917. }
  3918. $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
  3919. FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
  3920. FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
  3921. FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
  3922. FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
  3923. FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
  3924. 'none' => get_string('none'));
  3925. if ($this->name === 'frontpage') {
  3926. unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
  3927. }
  3928. return true;
  3929. }
  3930. /**
  3931. * Returns the selected settings
  3932. *
  3933. * @param mixed array or setting or null
  3934. */
  3935. public function get_setting() {
  3936. $result = $this->config_read($this->name);
  3937. if (is_null($result)) {
  3938. return NULL;
  3939. }
  3940. if ($result === '') {
  3941. return array();
  3942. }
  3943. return explode(',', $result);
  3944. }
  3945. /**
  3946. * Save the selected options
  3947. *
  3948. * @param array $data
  3949. * @return mixed empty string (data is not an array) or bool true=success false=failure
  3950. */
  3951. public function write_setting($data) {
  3952. if (!is_array($data)) {
  3953. return '';
  3954. }
  3955. $this->load_choices();
  3956. $save = array();
  3957. foreach($data as $datum) {
  3958. if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
  3959. continue;
  3960. }
  3961. $save[$datum] = $datum; // no duplicates
  3962. }
  3963. return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
  3964. }
  3965. /**
  3966. * Return XHTML select field and wrapping div
  3967. *
  3968. * @todo Add vartype handling to make sure $data is an array
  3969. * @param array $data Array of elements to select by default
  3970. * @return string XHTML select field and wrapping div
  3971. */
  3972. public function output_html($data, $query='') {
  3973. global $OUTPUT;
  3974. $this->load_choices();
  3975. $currentsetting = array();
  3976. foreach ($data as $key) {
  3977. if ($key != 'none' and array_key_exists($key, $this->choices)) {
  3978. $currentsetting[] = $key; // already selected first
  3979. }
  3980. }
  3981. $context = (object) [
  3982. 'id' => $this->get_id(),
  3983. 'name' => $this->get_full_name(),
  3984. ];
  3985. $options = $this->choices;
  3986. $selects = [];
  3987. for ($i = 0; $i < count($this->choices) - 1; $i++) {
  3988. if (!array_key_exists($i, $currentsetting)) {
  3989. $currentsetting[$i] = 'none';
  3990. }
  3991. $selects[] = [
  3992. 'key' => $i,
  3993. 'options' => array_map(function($option) use ($options, $currentsetting, $i) {
  3994. return [
  3995. 'name' => $options[$option],
  3996. 'value' => $option,
  3997. 'selected' => $currentsetting[$i] == $option
  3998. ];
  3999. }, array_keys($options))
  4000. ];
  4001. }
  4002. $context->selects = $selects;
  4003. $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
  4004. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
  4005. }
  4006. }
  4007. /**
  4008. * Special checkbox for frontpage - stores data in course table
  4009. *
  4010. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4011. */
  4012. class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
  4013. /**
  4014. * Returns the current sites name
  4015. *
  4016. * @return string
  4017. */
  4018. public function get_setting() {
  4019. $site = course_get_format(get_site())->get_course();
  4020. return $site->{$this->name};
  4021. }
  4022. /**
  4023. * Save the selected setting
  4024. *
  4025. * @param string $data The selected site
  4026. * @return string empty string or error message
  4027. */
  4028. public function write_setting($data) {
  4029. global $DB, $SITE, $COURSE;
  4030. $record = new stdClass();
  4031. $record->id = $SITE->id;
  4032. $record->{$this->name} = ($data == '1' ? 1 : 0);
  4033. $record->timemodified = time();
  4034. course_get_format($SITE)->update_course_format_options($record);
  4035. $DB->update_record('course', $record);
  4036. // Reset caches.
  4037. $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
  4038. if ($SITE->id == $COURSE->id) {
  4039. $COURSE = $SITE;
  4040. }
  4041. format_base::reset_course_cache($SITE->id);
  4042. return '';
  4043. }
  4044. }
  4045. /**
  4046. * Special text for frontpage - stores data in course table.
  4047. * Empty string means not set here. Manual setting is required.
  4048. *
  4049. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4050. */
  4051. class admin_setting_sitesettext extends admin_setting_configtext {
  4052. /**
  4053. * Constructor.
  4054. */
  4055. public function __construct() {
  4056. call_user_func_array(['parent', '__construct'], func_get_args());
  4057. $this->set_force_ltr(false);
  4058. }
  4059. /**
  4060. * Return the current setting
  4061. *
  4062. * @return mixed string or null
  4063. */
  4064. public function get_setting() {
  4065. $site = course_get_format(get_site())->get_course();
  4066. return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
  4067. }
  4068. /**
  4069. * Validate the selected data
  4070. *
  4071. * @param string $data The selected value to validate
  4072. * @return mixed true or message string
  4073. */
  4074. public function validate($data) {
  4075. global $DB, $SITE;
  4076. $cleaned = clean_param($data, PARAM_TEXT);
  4077. if ($cleaned === '') {
  4078. return get_string('required');
  4079. }
  4080. if ($this->name ==='shortname' &&
  4081. $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
  4082. return get_string('shortnametaken', 'error', $data);
  4083. }
  4084. if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
  4085. return true;
  4086. } else {
  4087. return get_string('validateerror', 'admin');
  4088. }
  4089. }
  4090. /**
  4091. * Save the selected setting
  4092. *
  4093. * @param string $data The selected value
  4094. * @return string empty or error message
  4095. */
  4096. public function write_setting($data) {
  4097. global $DB, $SITE, $COURSE;
  4098. $data = trim($data);
  4099. $validated = $this->validate($data);
  4100. if ($validated !== true) {
  4101. return $validated;
  4102. }
  4103. $record = new stdClass();
  4104. $record->id = $SITE->id;
  4105. $record->{$this->name} = $data;
  4106. $record->timemodified = time();
  4107. course_get_format($SITE)->update_course_format_options($record);
  4108. $DB->update_record('course', $record);
  4109. // Reset caches.
  4110. $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
  4111. if ($SITE->id == $COURSE->id) {
  4112. $COURSE = $SITE;
  4113. }
  4114. format_base::reset_course_cache($SITE->id);
  4115. return '';
  4116. }
  4117. }
  4118. /**
  4119. * Special text editor for site description.
  4120. *
  4121. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4122. */
  4123. class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
  4124. /**
  4125. * Calls parent::__construct with specific arguments
  4126. */
  4127. public function __construct() {
  4128. parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
  4129. PARAM_RAW, 60, 15);
  4130. }
  4131. /**
  4132. * Return the current setting
  4133. * @return string The current setting
  4134. */
  4135. public function get_setting() {
  4136. $site = course_get_format(get_site())->get_course();
  4137. return $site->{$this->name};
  4138. }
  4139. /**
  4140. * Save the new setting
  4141. *
  4142. * @param string $data The new value to save
  4143. * @return string empty or error message
  4144. */
  4145. public function write_setting($data) {
  4146. global $DB, $SITE, $COURSE;
  4147. $record = new stdClass();
  4148. $record->id = $SITE->id;
  4149. $record->{$this->name} = $data;
  4150. $record->timemodified = time();
  4151. course_get_format($SITE)->update_course_format_options($record);
  4152. $DB->update_record('course', $record);
  4153. // Reset caches.
  4154. $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
  4155. if ($SITE->id == $COURSE->id) {
  4156. $COURSE = $SITE;
  4157. }
  4158. format_base::reset_course_cache($SITE->id);
  4159. return '';
  4160. }
  4161. }
  4162. /**
  4163. * Administration interface for emoticon_manager settings.
  4164. *
  4165. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4166. */
  4167. class admin_setting_emoticons extends admin_setting {
  4168. /**
  4169. * Calls parent::__construct with specific args
  4170. */
  4171. public function __construct() {
  4172. global $CFG;
  4173. $manager = get_emoticon_manager();
  4174. $defaults = $this->prepare_form_data($manager->default_emoticons());
  4175. parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
  4176. }
  4177. /**
  4178. * Return the current setting(s)
  4179. *
  4180. * @return array Current settings array
  4181. */
  4182. public function get_setting() {
  4183. global $CFG;
  4184. $manager = get_emoticon_manager();
  4185. $config = $this->config_read($this->name);
  4186. if (is_null($config)) {
  4187. return null;
  4188. }
  4189. $config = $manager->decode_stored_config($config);
  4190. if (is_null($config)) {
  4191. return null;
  4192. }
  4193. return $this->prepare_form_data($config);
  4194. }
  4195. /**
  4196. * Save selected settings
  4197. *
  4198. * @param array $data Array of settings to save
  4199. * @return bool
  4200. */
  4201. public function write_setting($data) {
  4202. $manager = get_emoticon_manager();
  4203. $emoticons = $this->process_form_data($data);
  4204. if ($emoticons === false) {
  4205. return false;
  4206. }
  4207. if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
  4208. return ''; // success
  4209. } else {
  4210. return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
  4211. }
  4212. }
  4213. /**
  4214. * Return XHTML field(s) for options
  4215. *
  4216. * @param array $data Array of options to set in HTML
  4217. * @return string XHTML string for the fields and wrapping div(s)
  4218. */
  4219. public function output_html($data, $query='') {
  4220. global $OUTPUT;
  4221. $context = (object) [
  4222. 'name' => $this->get_full_name(),
  4223. 'emoticons' => [],
  4224. 'forceltr' => true,
  4225. ];
  4226. $i = 0;
  4227. foreach ($data as $field => $value) {
  4228. // When $i == 0: text.
  4229. // When $i == 1: imagename.
  4230. // When $i == 2: imagecomponent.
  4231. // When $i == 3: altidentifier.
  4232. // When $i == 4: altcomponent.
  4233. $fields[$i] = (object) [
  4234. 'field' => $field,
  4235. 'value' => $value,
  4236. 'index' => $i
  4237. ];
  4238. $i++;
  4239. if ($i > 4) {
  4240. $icon = null;
  4241. if (!empty($fields[1]->value)) {
  4242. if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
  4243. $alt = get_string($fields[3]->value, $fields[4]->value);
  4244. } else {
  4245. $alt = $fields[0]->value;
  4246. }
  4247. $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
  4248. }
  4249. $context->emoticons[] = [
  4250. 'fields' => $fields,
  4251. 'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
  4252. ];
  4253. $fields = [];
  4254. $i = 0;
  4255. }
  4256. }
  4257. $context->reseturl = new moodle_url('/admin/resetemoticons.php');
  4258. $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
  4259. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
  4260. }
  4261. /**
  4262. * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
  4263. *
  4264. * @see self::process_form_data()
  4265. * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
  4266. * @return array of form fields and their values
  4267. */
  4268. protected function prepare_form_data(array $emoticons) {
  4269. $form = array();
  4270. $i = 0;
  4271. foreach ($emoticons as $emoticon) {
  4272. $form['text'.$i] = $emoticon->text;
  4273. $form['imagename'.$i] = $emoticon->imagename;
  4274. $form['imagecomponent'.$i] = $emoticon->imagecomponent;
  4275. $form['altidentifier'.$i] = $emoticon->altidentifier;
  4276. $form['altcomponent'.$i] = $emoticon->altcomponent;
  4277. $i++;
  4278. }
  4279. // add one more blank field set for new object
  4280. $form['text'.$i] = '';
  4281. $form['imagename'.$i] = '';
  4282. $form['imagecomponent'.$i] = '';
  4283. $form['altidentifier'.$i] = '';
  4284. $form['altcomponent'.$i] = '';
  4285. return $form;
  4286. }
  4287. /**
  4288. * Converts the data from admin settings form into an array of emoticon objects
  4289. *
  4290. * @see self::prepare_form_data()
  4291. * @param array $data array of admin form fields and values
  4292. * @return false|array of emoticon objects
  4293. */
  4294. protected function process_form_data(array $form) {
  4295. $count = count($form); // number of form field values
  4296. if ($count % 5) {
  4297. // we must get five fields per emoticon object
  4298. return false;
  4299. }
  4300. $emoticons = array();
  4301. for ($i = 0; $i < $count / 5; $i++) {
  4302. $emoticon = new stdClass();
  4303. $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
  4304. $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
  4305. $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
  4306. $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
  4307. $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
  4308. if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
  4309. // prevent from breaking http://url.addresses by accident
  4310. $emoticon->text = '';
  4311. }
  4312. if (strlen($emoticon->text) < 2) {
  4313. // do not allow single character emoticons
  4314. $emoticon->text = '';
  4315. }
  4316. if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
  4317. // emoticon text must contain some non-alphanumeric character to prevent
  4318. // breaking HTML tags
  4319. $emoticon->text = '';
  4320. }
  4321. if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
  4322. $emoticons[] = $emoticon;
  4323. }
  4324. }
  4325. return $emoticons;
  4326. }
  4327. }
  4328. /**
  4329. * Special setting for limiting of the list of available languages.
  4330. *
  4331. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4332. */
  4333. class admin_setting_langlist extends admin_setting_configtext {
  4334. /**
  4335. * Calls parent::__construct with specific arguments
  4336. */
  4337. public function __construct() {
  4338. parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
  4339. }
  4340. /**
  4341. * Save the new setting
  4342. *
  4343. * @param string $data The new setting
  4344. * @return bool
  4345. */
  4346. public function write_setting($data) {
  4347. $return = parent::write_setting($data);
  4348. get_string_manager()->reset_caches();
  4349. return $return;
  4350. }
  4351. }
  4352. /**
  4353. * Selection of one of the recognised countries using the list
  4354. * returned by {@link get_list_of_countries()}.
  4355. *
  4356. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4357. */
  4358. class admin_settings_country_select extends admin_setting_configselect {
  4359. protected $includeall;
  4360. public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
  4361. $this->includeall = $includeall;
  4362. parent::__construct($name, $visiblename, $description, $defaultsetting, null);
  4363. }
  4364. /**
  4365. * Lazy-load the available choices for the select box
  4366. */
  4367. public function load_choices() {
  4368. global $CFG;
  4369. if (is_array($this->choices)) {
  4370. return true;
  4371. }
  4372. $this->choices = array_merge(
  4373. array('0' => get_string('choosedots')),
  4374. get_string_manager()->get_list_of_countries($this->includeall));
  4375. return true;
  4376. }
  4377. }
  4378. /**
  4379. * admin_setting_configselect for the default number of sections in a course,
  4380. * simply so we can lazy-load the choices.
  4381. *
  4382. * @copyright 2011 The Open University
  4383. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4384. */
  4385. class admin_settings_num_course_sections extends admin_setting_configselect {
  4386. public function __construct($name, $visiblename, $description, $defaultsetting) {
  4387. parent::__construct($name, $visiblename, $description, $defaultsetting, array());
  4388. }
  4389. /** Lazy-load the available choices for the select box */
  4390. public function load_choices() {
  4391. $max = get_config('moodlecourse', 'maxsections');
  4392. if (!isset($max) || !is_numeric($max)) {
  4393. $max = 52;
  4394. }
  4395. for ($i = 0; $i <= $max; $i++) {
  4396. $this->choices[$i] = "$i";
  4397. }
  4398. return true;
  4399. }
  4400. }
  4401. /**
  4402. * Course category selection
  4403. *
  4404. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4405. */
  4406. class admin_settings_coursecat_select extends admin_setting_configselect {
  4407. /**
  4408. * Calls parent::__construct with specific arguments
  4409. */
  4410. public function __construct($name, $visiblename, $description, $defaultsetting) {
  4411. parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
  4412. }
  4413. /**
  4414. * Load the available choices for the select box
  4415. *
  4416. * @return bool
  4417. */
  4418. public function load_choices() {
  4419. global $CFG;
  4420. require_once($CFG->dirroot.'/course/lib.php');
  4421. if (is_array($this->choices)) {
  4422. return true;
  4423. }
  4424. $this->choices = make_categories_options();
  4425. return true;
  4426. }
  4427. }
  4428. /**
  4429. * Special control for selecting days to backup
  4430. *
  4431. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4432. */
  4433. class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
  4434. /**
  4435. * Calls parent::__construct with specific arguments
  4436. */
  4437. public function __construct() {
  4438. parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
  4439. $this->plugin = 'backup';
  4440. }
  4441. /**
  4442. * Load the available choices for the select box
  4443. *
  4444. * @return bool Always returns true
  4445. */
  4446. public function load_choices() {
  4447. if (is_array($this->choices)) {
  4448. return true;
  4449. }
  4450. $this->choices = array();
  4451. $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
  4452. foreach ($days as $day) {
  4453. $this->choices[$day] = get_string($day, 'calendar');
  4454. }
  4455. return true;
  4456. }
  4457. }
  4458. /**
  4459. * Special setting for backup auto destination.
  4460. *
  4461. * @package core
  4462. * @subpackage admin
  4463. * @copyright 2014 Frédéric Massart - FMCorz.net
  4464. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4465. */
  4466. class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
  4467. /**
  4468. * Calls parent::__construct with specific arguments.
  4469. */
  4470. public function __construct() {
  4471. parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
  4472. }
  4473. /**
  4474. * Check if the directory must be set, depending on backup/backup_auto_storage.
  4475. *
  4476. * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
  4477. * there will be conflicts if this validation happens before the other one.
  4478. *
  4479. * @param string $data Form data.
  4480. * @return string Empty when no errors.
  4481. */
  4482. public function write_setting($data) {
  4483. $storage = (int) get_config('backup', 'backup_auto_storage');
  4484. if ($storage !== 0) {
  4485. if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
  4486. // The directory must exist and be writable.
  4487. return get_string('backuperrorinvaliddestination');
  4488. }
  4489. }
  4490. return parent::write_setting($data);
  4491. }
  4492. }
  4493. /**
  4494. * Special debug setting
  4495. *
  4496. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4497. */
  4498. class admin_setting_special_debug extends admin_setting_configselect {
  4499. /**
  4500. * Calls parent::__construct with specific arguments
  4501. */
  4502. public function __construct() {
  4503. parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
  4504. }
  4505. /**
  4506. * Load the available choices for the select box
  4507. *
  4508. * @return bool
  4509. */
  4510. public function load_choices() {
  4511. if (is_array($this->choices)) {
  4512. return true;
  4513. }
  4514. $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
  4515. DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
  4516. DEBUG_NORMAL => get_string('debugnormal', 'admin'),
  4517. DEBUG_ALL => get_string('debugall', 'admin'),
  4518. DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
  4519. return true;
  4520. }
  4521. }
  4522. /**
  4523. * Special admin control
  4524. *
  4525. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4526. */
  4527. class admin_setting_special_calendar_weekend extends admin_setting {
  4528. /**
  4529. * Calls parent::__construct with specific arguments
  4530. */
  4531. public function __construct() {
  4532. $name = 'calendar_weekend';
  4533. $visiblename = get_string('calendar_weekend', 'admin');
  4534. $description = get_string('helpweekenddays', 'admin');
  4535. $default = array ('0', '6'); // Saturdays and Sundays
  4536. parent::__construct($name, $visiblename, $description, $default);
  4537. }
  4538. /**
  4539. * Gets the current settings as an array
  4540. *
  4541. * @return mixed Null if none, else array of settings
  4542. */
  4543. public function get_setting() {
  4544. $result = $this->config_read($this->name);
  4545. if (is_null($result)) {
  4546. return NULL;
  4547. }
  4548. if ($result === '') {
  4549. return array();
  4550. }
  4551. $settings = array();
  4552. for ($i=0; $i<7; $i++) {
  4553. if ($result & (1 << $i)) {
  4554. $settings[] = $i;
  4555. }
  4556. }
  4557. return $settings;
  4558. }
  4559. /**
  4560. * Save the new settings
  4561. *
  4562. * @param array $data Array of new settings
  4563. * @return bool
  4564. */
  4565. public function write_setting($data) {
  4566. if (!is_array($data)) {
  4567. return '';
  4568. }
  4569. unset($data['xxxxx']);
  4570. $result = 0;
  4571. foreach($data as $index) {
  4572. $result |= 1 << $index;
  4573. }
  4574. return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
  4575. }
  4576. /**
  4577. * Return XHTML to display the control
  4578. *
  4579. * @param array $data array of selected days
  4580. * @param string $query
  4581. * @return string XHTML for display (field + wrapping div(s)
  4582. */
  4583. public function output_html($data, $query='') {
  4584. global $OUTPUT;
  4585. // The order matters very much because of the implied numeric keys.
  4586. $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
  4587. $context = (object) [
  4588. 'name' => $this->get_full_name(),
  4589. 'id' => $this->get_id(),
  4590. 'days' => array_map(function($index) use ($days, $data) {
  4591. return [
  4592. 'index' => $index,
  4593. 'label' => get_string($days[$index], 'calendar'),
  4594. 'checked' => in_array($index, $data)
  4595. ];
  4596. }, array_keys($days))
  4597. ];
  4598. $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
  4599. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
  4600. }
  4601. }
  4602. /**
  4603. * Admin setting that allows a user to pick a behaviour.
  4604. *
  4605. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4606. */
  4607. class admin_setting_question_behaviour extends admin_setting_configselect {
  4608. /**
  4609. * @param string $name name of config variable
  4610. * @param string $visiblename display name
  4611. * @param string $description description
  4612. * @param string $default default.
  4613. */
  4614. public function __construct($name, $visiblename, $description, $default) {
  4615. parent::__construct($name, $visiblename, $description, $default, null);
  4616. }
  4617. /**
  4618. * Load list of behaviours as choices
  4619. * @return bool true => success, false => error.
  4620. */
  4621. public function load_choices() {
  4622. global $CFG;
  4623. require_once($CFG->dirroot . '/question/engine/lib.php');
  4624. $this->choices = question_engine::get_behaviour_options('');
  4625. return true;
  4626. }
  4627. }
  4628. /**
  4629. * Admin setting that allows a user to pick appropriate roles for something.
  4630. *
  4631. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4632. */
  4633. class admin_setting_pickroles extends admin_setting_configmulticheckbox {
  4634. /** @var array Array of capabilities which identify roles */
  4635. private $types;
  4636. /**
  4637. * @param string $name Name of config variable
  4638. * @param string $visiblename Display name
  4639. * @param string $description Description
  4640. * @param array $types Array of archetypes which identify
  4641. * roles that will be enabled by default.
  4642. */
  4643. public function __construct($name, $visiblename, $description, $types) {
  4644. parent::__construct($name, $visiblename, $description, NULL, NULL);
  4645. $this->types = $types;
  4646. }
  4647. /**
  4648. * Load roles as choices
  4649. *
  4650. * @return bool true=>success, false=>error
  4651. */
  4652. public function load_choices() {
  4653. global $CFG, $DB;
  4654. if (during_initial_install()) {
  4655. return false;
  4656. }
  4657. if (is_array($this->choices)) {
  4658. return true;
  4659. }
  4660. if ($roles = get_all_roles()) {
  4661. $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
  4662. return true;
  4663. } else {
  4664. return false;
  4665. }
  4666. }
  4667. /**
  4668. * Return the default setting for this control
  4669. *
  4670. * @return array Array of default settings
  4671. */
  4672. public function get_defaultsetting() {
  4673. global $CFG;
  4674. if (during_initial_install()) {
  4675. return null;
  4676. }
  4677. $result = array();
  4678. foreach($this->types as $archetype) {
  4679. if ($caproles = get_archetype_roles($archetype)) {
  4680. foreach ($caproles as $caprole) {
  4681. $result[$caprole->id] = 1;
  4682. }
  4683. }
  4684. }
  4685. return $result;
  4686. }
  4687. }
  4688. /**
  4689. * Admin setting that is a list of installed filter plugins.
  4690. *
  4691. * @copyright 2015 The Open University
  4692. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4693. */
  4694. class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
  4695. /**
  4696. * Constructor
  4697. *
  4698. * @param string $name unique ascii name, either 'mysetting' for settings
  4699. * that in config, or 'myplugin/mysetting' for ones in config_plugins.
  4700. * @param string $visiblename localised name
  4701. * @param string $description localised long description
  4702. * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
  4703. */
  4704. public function __construct($name, $visiblename, $description, $default) {
  4705. if (empty($default)) {
  4706. $default = array();
  4707. }
  4708. $this->load_choices();
  4709. foreach ($default as $plugin) {
  4710. if (!isset($this->choices[$plugin])) {
  4711. unset($default[$plugin]);
  4712. }
  4713. }
  4714. parent::__construct($name, $visiblename, $description, $default, null);
  4715. }
  4716. public function load_choices() {
  4717. if (is_array($this->choices)) {
  4718. return true;
  4719. }
  4720. $this->choices = array();
  4721. foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
  4722. $this->choices[$plugin] = filter_get_name($plugin);
  4723. }
  4724. return true;
  4725. }
  4726. }
  4727. /**
  4728. * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
  4729. *
  4730. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4731. */
  4732. class admin_setting_configtext_with_advanced extends admin_setting_configtext {
  4733. /**
  4734. * Constructor
  4735. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  4736. * @param string $visiblename localised
  4737. * @param string $description long localised info
  4738. * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
  4739. * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
  4740. * @param int $size default field size
  4741. */
  4742. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
  4743. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
  4744. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  4745. }
  4746. }
  4747. /**
  4748. * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
  4749. *
  4750. * @copyright 2009 Petr Skoda (http://skodak.org)
  4751. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4752. */
  4753. class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
  4754. /**
  4755. * Constructor
  4756. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  4757. * @param string $visiblename localised
  4758. * @param string $description long localised info
  4759. * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
  4760. * @param string $yes value used when checked
  4761. * @param string $no value used when not checked
  4762. */
  4763. public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
  4764. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
  4765. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  4766. }
  4767. }
  4768. /**
  4769. * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
  4770. *
  4771. * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
  4772. *
  4773. * @copyright 2010 Sam Hemelryk
  4774. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4775. */
  4776. class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
  4777. /**
  4778. * Constructor
  4779. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
  4780. * @param string $visiblename localised
  4781. * @param string $description long localised info
  4782. * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
  4783. * @param string $yes value used when checked
  4784. * @param string $no value used when not checked
  4785. */
  4786. public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
  4787. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
  4788. $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
  4789. }
  4790. }
  4791. /**
  4792. * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
  4793. *
  4794. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4795. */
  4796. class admin_setting_configselect_with_advanced extends admin_setting_configselect {
  4797. /**
  4798. * Calls parent::__construct with specific arguments
  4799. */
  4800. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  4801. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
  4802. $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
  4803. }
  4804. }
  4805. /**
  4806. * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
  4807. *
  4808. * @copyright 2017 Marina Glancy
  4809. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4810. */
  4811. class admin_setting_configselect_with_lock extends admin_setting_configselect {
  4812. /**
  4813. * Constructor
  4814. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  4815. * or 'myplugin/mysetting' for ones in config_plugins.
  4816. * @param string $visiblename localised
  4817. * @param string $description long localised info
  4818. * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
  4819. * @param array $choices array of $value=>$label for each selection
  4820. */
  4821. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  4822. parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
  4823. $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
  4824. }
  4825. }
  4826. /**
  4827. * Graded roles in gradebook
  4828. *
  4829. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4830. */
  4831. class admin_setting_special_gradebookroles extends admin_setting_pickroles {
  4832. /**
  4833. * Calls parent::__construct with specific arguments
  4834. */
  4835. public function __construct() {
  4836. parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
  4837. get_string('configgradebookroles', 'admin'),
  4838. array('student'));
  4839. }
  4840. }
  4841. /**
  4842. *
  4843. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4844. */
  4845. class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
  4846. /**
  4847. * Saves the new settings passed in $data
  4848. *
  4849. * @param string $data
  4850. * @return mixed string or Array
  4851. */
  4852. public function write_setting($data) {
  4853. global $CFG, $DB;
  4854. $oldvalue = $this->config_read($this->name);
  4855. $return = parent::write_setting($data);
  4856. $newvalue = $this->config_read($this->name);
  4857. if ($oldvalue !== $newvalue) {
  4858. // force full regrading
  4859. $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
  4860. }
  4861. return $return;
  4862. }
  4863. }
  4864. /**
  4865. * Which roles to show on course description page
  4866. *
  4867. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4868. */
  4869. class admin_setting_special_coursecontact extends admin_setting_pickroles {
  4870. /**
  4871. * Calls parent::__construct with specific arguments
  4872. */
  4873. public function __construct() {
  4874. parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
  4875. get_string('coursecontact_desc', 'admin'),
  4876. array('editingteacher'));
  4877. $this->set_updatedcallback(function (){
  4878. cache::make('core', 'coursecontacts')->purge();
  4879. });
  4880. }
  4881. }
  4882. /**
  4883. *
  4884. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4885. */
  4886. class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
  4887. /**
  4888. * Calls parent::__construct with specific arguments
  4889. */
  4890. public function __construct() {
  4891. parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
  4892. get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
  4893. }
  4894. /**
  4895. * Old syntax of class constructor. Deprecated in PHP7.
  4896. *
  4897. * @deprecated since Moodle 3.1
  4898. */
  4899. public function admin_setting_special_gradelimiting() {
  4900. debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
  4901. self::__construct();
  4902. }
  4903. /**
  4904. * Force site regrading
  4905. */
  4906. function regrade_all() {
  4907. global $CFG;
  4908. require_once("$CFG->libdir/gradelib.php");
  4909. grade_force_site_regrading();
  4910. }
  4911. /**
  4912. * Saves the new settings
  4913. *
  4914. * @param mixed $data
  4915. * @return string empty string or error message
  4916. */
  4917. function write_setting($data) {
  4918. $previous = $this->get_setting();
  4919. if ($previous === null) {
  4920. if ($data) {
  4921. $this->regrade_all();
  4922. }
  4923. } else {
  4924. if ($data != $previous) {
  4925. $this->regrade_all();
  4926. }
  4927. }
  4928. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  4929. }
  4930. }
  4931. /**
  4932. * Special setting for $CFG->grade_minmaxtouse.
  4933. *
  4934. * @package core
  4935. * @copyright 2015 Frédéric Massart - FMCorz.net
  4936. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4937. */
  4938. class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
  4939. /**
  4940. * Constructor.
  4941. */
  4942. public function __construct() {
  4943. parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
  4944. new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
  4945. array(
  4946. GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
  4947. GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
  4948. )
  4949. );
  4950. }
  4951. /**
  4952. * Saves the new setting.
  4953. *
  4954. * @param mixed $data
  4955. * @return string empty string or error message
  4956. */
  4957. function write_setting($data) {
  4958. global $CFG;
  4959. $previous = $this->get_setting();
  4960. $result = parent::write_setting($data);
  4961. // If saved and the value has changed.
  4962. if (empty($result) && $previous != $data) {
  4963. require_once($CFG->libdir . '/gradelib.php');
  4964. grade_force_site_regrading();
  4965. }
  4966. return $result;
  4967. }
  4968. }
  4969. /**
  4970. * Primary grade export plugin - has state tracking.
  4971. *
  4972. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  4973. */
  4974. class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
  4975. /**
  4976. * Calls parent::__construct with specific arguments
  4977. */
  4978. public function __construct() {
  4979. parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
  4980. get_string('configgradeexport', 'admin'), array(), NULL);
  4981. }
  4982. /**
  4983. * Load the available choices for the multicheckbox
  4984. *
  4985. * @return bool always returns true
  4986. */
  4987. public function load_choices() {
  4988. if (is_array($this->choices)) {
  4989. return true;
  4990. }
  4991. $this->choices = array();
  4992. if ($plugins = core_component::get_plugin_list('gradeexport')) {
  4993. foreach($plugins as $plugin => $unused) {
  4994. $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
  4995. }
  4996. }
  4997. return true;
  4998. }
  4999. }
  5000. /**
  5001. * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
  5002. *
  5003. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5004. */
  5005. class admin_setting_special_gradepointdefault extends admin_setting_configtext {
  5006. /**
  5007. * Config gradepointmax constructor
  5008. *
  5009. * @param string $name Overidden by "gradepointmax"
  5010. * @param string $visiblename Overridden by "gradepointmax" language string.
  5011. * @param string $description Overridden by "gradepointmax_help" language string.
  5012. * @param string $defaultsetting Not used, overridden by 100.
  5013. * @param mixed $paramtype Overridden by PARAM_INT.
  5014. * @param int $size Overridden by 5.
  5015. */
  5016. public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
  5017. $name = 'gradepointdefault';
  5018. $visiblename = get_string('gradepointdefault', 'grades');
  5019. $description = get_string('gradepointdefault_help', 'grades');
  5020. $defaultsetting = 100;
  5021. $paramtype = PARAM_INT;
  5022. $size = 5;
  5023. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
  5024. }
  5025. /**
  5026. * Validate data before storage
  5027. * @param string $data The submitted data
  5028. * @return bool|string true if ok, string if error found
  5029. */
  5030. public function validate($data) {
  5031. global $CFG;
  5032. if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
  5033. return true;
  5034. } else {
  5035. return get_string('gradepointdefault_validateerror', 'grades');
  5036. }
  5037. }
  5038. }
  5039. /**
  5040. * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
  5041. *
  5042. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5043. */
  5044. class admin_setting_special_gradepointmax extends admin_setting_configtext {
  5045. /**
  5046. * Config gradepointmax constructor
  5047. *
  5048. * @param string $name Overidden by "gradepointmax"
  5049. * @param string $visiblename Overridden by "gradepointmax" language string.
  5050. * @param string $description Overridden by "gradepointmax_help" language string.
  5051. * @param string $defaultsetting Not used, overridden by 100.
  5052. * @param mixed $paramtype Overridden by PARAM_INT.
  5053. * @param int $size Overridden by 5.
  5054. */
  5055. public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
  5056. $name = 'gradepointmax';
  5057. $visiblename = get_string('gradepointmax', 'grades');
  5058. $description = get_string('gradepointmax_help', 'grades');
  5059. $defaultsetting = 100;
  5060. $paramtype = PARAM_INT;
  5061. $size = 5;
  5062. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
  5063. }
  5064. /**
  5065. * Save the selected setting
  5066. *
  5067. * @param string $data The selected site
  5068. * @return string empty string or error message
  5069. */
  5070. public function write_setting($data) {
  5071. if ($data === '') {
  5072. $data = (int)$this->defaultsetting;
  5073. } else {
  5074. $data = $data;
  5075. }
  5076. return parent::write_setting($data);
  5077. }
  5078. /**
  5079. * Validate data before storage
  5080. * @param string $data The submitted data
  5081. * @return bool|string true if ok, string if error found
  5082. */
  5083. public function validate($data) {
  5084. if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
  5085. return true;
  5086. } else {
  5087. return get_string('gradepointmax_validateerror', 'grades');
  5088. }
  5089. }
  5090. /**
  5091. * Return an XHTML string for the setting
  5092. * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
  5093. * @param string $query search query to be highlighted
  5094. * @return string XHTML to display control
  5095. */
  5096. public function output_html($data, $query = '') {
  5097. global $OUTPUT;
  5098. $default = $this->get_defaultsetting();
  5099. $context = (object) [
  5100. 'size' => $this->size,
  5101. 'id' => $this->get_id(),
  5102. 'name' => $this->get_full_name(),
  5103. 'value' => $data,
  5104. 'attributes' => [
  5105. 'maxlength' => 5
  5106. ],
  5107. 'forceltr' => $this->get_force_ltr()
  5108. ];
  5109. $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
  5110. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  5111. }
  5112. }
  5113. /**
  5114. * Grade category settings
  5115. *
  5116. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5117. */
  5118. class admin_setting_gradecat_combo extends admin_setting {
  5119. /** @var array Array of choices */
  5120. public $choices;
  5121. /**
  5122. * Sets choices and calls parent::__construct with passed arguments
  5123. * @param string $name
  5124. * @param string $visiblename
  5125. * @param string $description
  5126. * @param mixed $defaultsetting string or array depending on implementation
  5127. * @param array $choices An array of choices for the control
  5128. */
  5129. public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
  5130. $this->choices = $choices;
  5131. parent::__construct($name, $visiblename, $description, $defaultsetting);
  5132. }
  5133. /**
  5134. * Return the current setting(s) array
  5135. *
  5136. * @return array Array of value=>xx, forced=>xx, adv=>xx
  5137. */
  5138. public function get_setting() {
  5139. global $CFG;
  5140. $value = $this->config_read($this->name);
  5141. $flag = $this->config_read($this->name.'_flag');
  5142. if (is_null($value) or is_null($flag)) {
  5143. return NULL;
  5144. }
  5145. $flag = (int)$flag;
  5146. $forced = (boolean)(1 & $flag); // first bit
  5147. $adv = (boolean)(2 & $flag); // second bit
  5148. return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
  5149. }
  5150. /**
  5151. * Save the new settings passed in $data
  5152. *
  5153. * @todo Add vartype handling to ensure $data is array
  5154. * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
  5155. * @return string empty or error message
  5156. */
  5157. public function write_setting($data) {
  5158. global $CFG;
  5159. $value = $data['value'];
  5160. $forced = empty($data['forced']) ? 0 : 1;
  5161. $adv = empty($data['adv']) ? 0 : 2;
  5162. $flag = ($forced | $adv); //bitwise or
  5163. if (!in_array($value, array_keys($this->choices))) {
  5164. return 'Error setting ';
  5165. }
  5166. $oldvalue = $this->config_read($this->name);
  5167. $oldflag = (int)$this->config_read($this->name.'_flag');
  5168. $oldforced = (1 & $oldflag); // first bit
  5169. $result1 = $this->config_write($this->name, $value);
  5170. $result2 = $this->config_write($this->name.'_flag', $flag);
  5171. // force regrade if needed
  5172. if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
  5173. require_once($CFG->libdir.'/gradelib.php');
  5174. grade_category::updated_forced_settings();
  5175. }
  5176. if ($result1 and $result2) {
  5177. return '';
  5178. } else {
  5179. return get_string('errorsetting', 'admin');
  5180. }
  5181. }
  5182. /**
  5183. * Return XHTML to display the field and wrapping div
  5184. *
  5185. * @todo Add vartype handling to ensure $data is array
  5186. * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
  5187. * @param string $query
  5188. * @return string XHTML to display control
  5189. */
  5190. public function output_html($data, $query='') {
  5191. global $OUTPUT;
  5192. $value = $data['value'];
  5193. $default = $this->get_defaultsetting();
  5194. if (!is_null($default)) {
  5195. $defaultinfo = array();
  5196. if (isset($this->choices[$default['value']])) {
  5197. $defaultinfo[] = $this->choices[$default['value']];
  5198. }
  5199. if (!empty($default['forced'])) {
  5200. $defaultinfo[] = get_string('force');
  5201. }
  5202. if (!empty($default['adv'])) {
  5203. $defaultinfo[] = get_string('advanced');
  5204. }
  5205. $defaultinfo = implode(', ', $defaultinfo);
  5206. } else {
  5207. $defaultinfo = NULL;
  5208. }
  5209. $options = $this->choices;
  5210. $context = (object) [
  5211. 'id' => $this->get_id(),
  5212. 'name' => $this->get_full_name(),
  5213. 'forced' => !empty($data['forced']),
  5214. 'advanced' => !empty($data['adv']),
  5215. 'options' => array_map(function($option) use ($options, $value) {
  5216. return [
  5217. 'value' => $option,
  5218. 'name' => $options[$option],
  5219. 'selected' => $option == $value
  5220. ];
  5221. }, array_keys($options)),
  5222. ];
  5223. $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
  5224. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
  5225. }
  5226. }
  5227. /**
  5228. * Selection of grade report in user profiles
  5229. *
  5230. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5231. */
  5232. class admin_setting_grade_profilereport extends admin_setting_configselect {
  5233. /**
  5234. * Calls parent::__construct with specific arguments
  5235. */
  5236. public function __construct() {
  5237. parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
  5238. }
  5239. /**
  5240. * Loads an array of choices for the configselect control
  5241. *
  5242. * @return bool always return true
  5243. */
  5244. public function load_choices() {
  5245. if (is_array($this->choices)) {
  5246. return true;
  5247. }
  5248. $this->choices = array();
  5249. global $CFG;
  5250. require_once($CFG->libdir.'/gradelib.php');
  5251. foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
  5252. if (file_exists($plugindir.'/lib.php')) {
  5253. require_once($plugindir.'/lib.php');
  5254. $functionname = 'grade_report_'.$plugin.'_profilereport';
  5255. if (function_exists($functionname)) {
  5256. $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
  5257. }
  5258. }
  5259. }
  5260. return true;
  5261. }
  5262. }
  5263. /**
  5264. * Provides a selection of grade reports to be used for "grades".
  5265. *
  5266. * @copyright 2015 Adrian Greeve <adrian@moodle.com>
  5267. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5268. */
  5269. class admin_setting_my_grades_report extends admin_setting_configselect {
  5270. /**
  5271. * Calls parent::__construct with specific arguments.
  5272. */
  5273. public function __construct() {
  5274. parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
  5275. new lang_string('mygrades_desc', 'grades'), 'overview', null);
  5276. }
  5277. /**
  5278. * Loads an array of choices for the configselect control.
  5279. *
  5280. * @return bool always returns true.
  5281. */
  5282. public function load_choices() {
  5283. global $CFG; // Remove this line and behold the horror of behat test failures!
  5284. $this->choices = array();
  5285. foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
  5286. if (file_exists($plugindir . '/lib.php')) {
  5287. require_once($plugindir . '/lib.php');
  5288. // Check to see if the class exists. Check the correct plugin convention first.
  5289. if (class_exists('gradereport_' . $plugin)) {
  5290. $classname = 'gradereport_' . $plugin;
  5291. } else if (class_exists('grade_report_' . $plugin)) {
  5292. // We are using the old plugin naming convention.
  5293. $classname = 'grade_report_' . $plugin;
  5294. } else {
  5295. continue;
  5296. }
  5297. if ($classname::supports_mygrades()) {
  5298. $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
  5299. }
  5300. }
  5301. }
  5302. // Add an option to specify an external url.
  5303. $this->choices['external'] = get_string('externalurl', 'grades');
  5304. return true;
  5305. }
  5306. }
  5307. /**
  5308. * Special class for register auth selection
  5309. *
  5310. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5311. */
  5312. class admin_setting_special_registerauth extends admin_setting_configselect {
  5313. /**
  5314. * Calls parent::__construct with specific arguments
  5315. */
  5316. public function __construct() {
  5317. parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
  5318. }
  5319. /**
  5320. * Returns the default option
  5321. *
  5322. * @return string empty or default option
  5323. */
  5324. public function get_defaultsetting() {
  5325. $this->load_choices();
  5326. $defaultsetting = parent::get_defaultsetting();
  5327. if (array_key_exists($defaultsetting, $this->choices)) {
  5328. return $defaultsetting;
  5329. } else {
  5330. return '';
  5331. }
  5332. }
  5333. /**
  5334. * Loads the possible choices for the array
  5335. *
  5336. * @return bool always returns true
  5337. */
  5338. public function load_choices() {
  5339. global $CFG;
  5340. if (is_array($this->choices)) {
  5341. return true;
  5342. }
  5343. $this->choices = array();
  5344. $this->choices[''] = get_string('disable');
  5345. $authsenabled = get_enabled_auth_plugins(true);
  5346. foreach ($authsenabled as $auth) {
  5347. $authplugin = get_auth_plugin($auth);
  5348. if (!$authplugin->can_signup()) {
  5349. continue;
  5350. }
  5351. // Get the auth title (from core or own auth lang files)
  5352. $authtitle = $authplugin->get_title();
  5353. $this->choices[$auth] = $authtitle;
  5354. }
  5355. return true;
  5356. }
  5357. }
  5358. /**
  5359. * General plugins manager
  5360. */
  5361. class admin_page_pluginsoverview extends admin_externalpage {
  5362. /**
  5363. * Sets basic information about the external page
  5364. */
  5365. public function __construct() {
  5366. global $CFG;
  5367. parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
  5368. "$CFG->wwwroot/$CFG->admin/plugins.php");
  5369. }
  5370. }
  5371. /**
  5372. * Module manage page
  5373. *
  5374. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5375. */
  5376. class admin_page_managemods extends admin_externalpage {
  5377. /**
  5378. * Calls parent::__construct with specific arguments
  5379. */
  5380. public function __construct() {
  5381. global $CFG;
  5382. parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
  5383. }
  5384. /**
  5385. * Try to find the specified module
  5386. *
  5387. * @param string $query The module to search for
  5388. * @return array
  5389. */
  5390. public function search($query) {
  5391. global $CFG, $DB;
  5392. if ($result = parent::search($query)) {
  5393. return $result;
  5394. }
  5395. $found = false;
  5396. if ($modules = $DB->get_records('modules')) {
  5397. foreach ($modules as $module) {
  5398. if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
  5399. continue;
  5400. }
  5401. if (strpos($module->name, $query) !== false) {
  5402. $found = true;
  5403. break;
  5404. }
  5405. $strmodulename = get_string('modulename', $module->name);
  5406. if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
  5407. $found = true;
  5408. break;
  5409. }
  5410. }
  5411. }
  5412. if ($found) {
  5413. $result = new stdClass();
  5414. $result->page = $this;
  5415. $result->settings = array();
  5416. return array($this->name => $result);
  5417. } else {
  5418. return array();
  5419. }
  5420. }
  5421. }
  5422. /**
  5423. * Special class for enrol plugins management.
  5424. *
  5425. * @copyright 2010 Petr Skoda {@link http://skodak.org}
  5426. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5427. */
  5428. class admin_setting_manageenrols extends admin_setting {
  5429. /**
  5430. * Calls parent::__construct with specific arguments
  5431. */
  5432. public function __construct() {
  5433. $this->nosave = true;
  5434. parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
  5435. }
  5436. /**
  5437. * Always returns true, does nothing
  5438. *
  5439. * @return true
  5440. */
  5441. public function get_setting() {
  5442. return true;
  5443. }
  5444. /**
  5445. * Always returns true, does nothing
  5446. *
  5447. * @return true
  5448. */
  5449. public function get_defaultsetting() {
  5450. return true;
  5451. }
  5452. /**
  5453. * Always returns '', does not write anything
  5454. *
  5455. * @return string Always returns ''
  5456. */
  5457. public function write_setting($data) {
  5458. // do not write any setting
  5459. return '';
  5460. }
  5461. /**
  5462. * Checks if $query is one of the available enrol plugins
  5463. *
  5464. * @param string $query The string to search for
  5465. * @return bool Returns true if found, false if not
  5466. */
  5467. public function is_related($query) {
  5468. if (parent::is_related($query)) {
  5469. return true;
  5470. }
  5471. $query = core_text::strtolower($query);
  5472. $enrols = enrol_get_plugins(false);
  5473. foreach ($enrols as $name=>$enrol) {
  5474. $localised = get_string('pluginname', 'enrol_'.$name);
  5475. if (strpos(core_text::strtolower($name), $query) !== false) {
  5476. return true;
  5477. }
  5478. if (strpos(core_text::strtolower($localised), $query) !== false) {
  5479. return true;
  5480. }
  5481. }
  5482. return false;
  5483. }
  5484. /**
  5485. * Builds the XHTML to display the control
  5486. *
  5487. * @param string $data Unused
  5488. * @param string $query
  5489. * @return string
  5490. */
  5491. public function output_html($data, $query='') {
  5492. global $CFG, $OUTPUT, $DB, $PAGE;
  5493. // Display strings.
  5494. $strup = get_string('up');
  5495. $strdown = get_string('down');
  5496. $strsettings = get_string('settings');
  5497. $strenable = get_string('enable');
  5498. $strdisable = get_string('disable');
  5499. $struninstall = get_string('uninstallplugin', 'core_admin');
  5500. $strusage = get_string('enrolusage', 'enrol');
  5501. $strversion = get_string('version');
  5502. $strtest = get_string('testsettings', 'core_enrol');
  5503. $pluginmanager = core_plugin_manager::instance();
  5504. $enrols_available = enrol_get_plugins(false);
  5505. $active_enrols = enrol_get_plugins(true);
  5506. $allenrols = array();
  5507. foreach ($active_enrols as $key=>$enrol) {
  5508. $allenrols[$key] = true;
  5509. }
  5510. foreach ($enrols_available as $key=>$enrol) {
  5511. $allenrols[$key] = true;
  5512. }
  5513. // Now find all borked plugins and at least allow then to uninstall.
  5514. $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
  5515. foreach ($condidates as $candidate) {
  5516. if (empty($allenrols[$candidate])) {
  5517. $allenrols[$candidate] = true;
  5518. }
  5519. }
  5520. $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
  5521. $return .= $OUTPUT->box_start('generalbox enrolsui');
  5522. $table = new html_table();
  5523. $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
  5524. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  5525. $table->id = 'courseenrolmentplugins';
  5526. $table->attributes['class'] = 'admintable generaltable';
  5527. $table->data = array();
  5528. // Iterate through enrol plugins and add to the display table.
  5529. $updowncount = 1;
  5530. $enrolcount = count($active_enrols);
  5531. $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
  5532. $printed = array();
  5533. foreach($allenrols as $enrol => $unused) {
  5534. $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
  5535. $version = get_config('enrol_'.$enrol, 'version');
  5536. if ($version === false) {
  5537. $version = '';
  5538. }
  5539. if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
  5540. $name = get_string('pluginname', 'enrol_'.$enrol);
  5541. } else {
  5542. $name = $enrol;
  5543. }
  5544. // Usage.
  5545. $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
  5546. $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
  5547. $usage = "$ci / $cp";
  5548. // Hide/show links.
  5549. $class = '';
  5550. if (isset($active_enrols[$enrol])) {
  5551. $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
  5552. $hideshow = "<a href=\"$aurl\">";
  5553. $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
  5554. $enabled = true;
  5555. $displayname = $name;
  5556. } else if (isset($enrols_available[$enrol])) {
  5557. $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
  5558. $hideshow = "<a href=\"$aurl\">";
  5559. $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
  5560. $enabled = false;
  5561. $displayname = $name;
  5562. $class = 'dimmed_text';
  5563. } else {
  5564. $hideshow = '';
  5565. $enabled = false;
  5566. $displayname = '<span class="notifyproblem">'.$name.'</span>';
  5567. }
  5568. if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
  5569. $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
  5570. } else {
  5571. $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
  5572. }
  5573. // Up/down link (only if enrol is enabled).
  5574. $updown = '';
  5575. if ($enabled) {
  5576. if ($updowncount > 1) {
  5577. $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
  5578. $updown .= "<a href=\"$aurl\">";
  5579. $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
  5580. } else {
  5581. $updown .= $OUTPUT->spacer() . '&nbsp;';
  5582. }
  5583. if ($updowncount < $enrolcount) {
  5584. $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
  5585. $updown .= "<a href=\"$aurl\">";
  5586. $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
  5587. } else {
  5588. $updown .= $OUTPUT->spacer() . '&nbsp;';
  5589. }
  5590. ++$updowncount;
  5591. }
  5592. // Add settings link.
  5593. if (!$version) {
  5594. $settings = '';
  5595. } else if ($surl = $plugininfo->get_settings_url()) {
  5596. $settings = html_writer::link($surl, $strsettings);
  5597. } else {
  5598. $settings = '';
  5599. }
  5600. // Add uninstall info.
  5601. $uninstall = '';
  5602. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
  5603. $uninstall = html_writer::link($uninstallurl, $struninstall);
  5604. }
  5605. $test = '';
  5606. if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
  5607. $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey()));
  5608. $test = html_writer::link($testsettingsurl, $strtest);
  5609. }
  5610. // Add a row to the table.
  5611. $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
  5612. if ($class) {
  5613. $row->attributes['class'] = $class;
  5614. }
  5615. $table->data[] = $row;
  5616. $printed[$enrol] = true;
  5617. }
  5618. $return .= html_writer::table($table);
  5619. $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
  5620. $return .= $OUTPUT->box_end();
  5621. return highlight($query, $return);
  5622. }
  5623. }
  5624. /**
  5625. * Blocks manage page
  5626. *
  5627. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5628. */
  5629. class admin_page_manageblocks extends admin_externalpage {
  5630. /**
  5631. * Calls parent::__construct with specific arguments
  5632. */
  5633. public function __construct() {
  5634. global $CFG;
  5635. parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
  5636. }
  5637. /**
  5638. * Search for a specific block
  5639. *
  5640. * @param string $query The string to search for
  5641. * @return array
  5642. */
  5643. public function search($query) {
  5644. global $CFG, $DB;
  5645. if ($result = parent::search($query)) {
  5646. return $result;
  5647. }
  5648. $found = false;
  5649. if ($blocks = $DB->get_records('block')) {
  5650. foreach ($blocks as $block) {
  5651. if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
  5652. continue;
  5653. }
  5654. if (strpos($block->name, $query) !== false) {
  5655. $found = true;
  5656. break;
  5657. }
  5658. $strblockname = get_string('pluginname', 'block_'.$block->name);
  5659. if (strpos(core_text::strtolower($strblockname), $query) !== false) {
  5660. $found = true;
  5661. break;
  5662. }
  5663. }
  5664. }
  5665. if ($found) {
  5666. $result = new stdClass();
  5667. $result->page = $this;
  5668. $result->settings = array();
  5669. return array($this->name => $result);
  5670. } else {
  5671. return array();
  5672. }
  5673. }
  5674. }
  5675. /**
  5676. * Message outputs configuration
  5677. *
  5678. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5679. */
  5680. class admin_page_managemessageoutputs extends admin_externalpage {
  5681. /**
  5682. * Calls parent::__construct with specific arguments
  5683. */
  5684. public function __construct() {
  5685. global $CFG;
  5686. parent::__construct('managemessageoutputs',
  5687. get_string('defaultmessageoutputs', 'message'),
  5688. new moodle_url('/admin/message.php')
  5689. );
  5690. }
  5691. /**
  5692. * Search for a specific message processor
  5693. *
  5694. * @param string $query The string to search for
  5695. * @return array
  5696. */
  5697. public function search($query) {
  5698. global $CFG, $DB;
  5699. if ($result = parent::search($query)) {
  5700. return $result;
  5701. }
  5702. $found = false;
  5703. if ($processors = get_message_processors()) {
  5704. foreach ($processors as $processor) {
  5705. if (!$processor->available) {
  5706. continue;
  5707. }
  5708. if (strpos($processor->name, $query) !== false) {
  5709. $found = true;
  5710. break;
  5711. }
  5712. $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
  5713. if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
  5714. $found = true;
  5715. break;
  5716. }
  5717. }
  5718. }
  5719. if ($found) {
  5720. $result = new stdClass();
  5721. $result->page = $this;
  5722. $result->settings = array();
  5723. return array($this->name => $result);
  5724. } else {
  5725. return array();
  5726. }
  5727. }
  5728. }
  5729. /**
  5730. * Default message outputs configuration
  5731. *
  5732. * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
  5733. * @todo MDL-64866 This will be deleted in Moodle 4.1.
  5734. *
  5735. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5736. */
  5737. class admin_page_defaultmessageoutputs extends admin_page_managemessageoutputs {
  5738. /**
  5739. * Calls parent::__construct with specific arguments
  5740. *
  5741. * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
  5742. * @todo MDL-64866 This will be deleted in Moodle 4.1.
  5743. */
  5744. public function __construct() {
  5745. global $CFG;
  5746. debugging('admin_page_defaultmessageoutputs class is deprecated. Please use admin_page_managemessageoutputs instead.',
  5747. DEBUG_DEVELOPER);
  5748. admin_externalpage::__construct('defaultmessageoutputs', get_string('defaultmessageoutputs', 'message'), new moodle_url('/message/defaultoutputs.php'));
  5749. }
  5750. }
  5751. /**
  5752. * Manage question behaviours page
  5753. *
  5754. * @copyright 2011 The Open University
  5755. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5756. */
  5757. class admin_page_manageqbehaviours extends admin_externalpage {
  5758. /**
  5759. * Constructor
  5760. */
  5761. public function __construct() {
  5762. global $CFG;
  5763. parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
  5764. new moodle_url('/admin/qbehaviours.php'));
  5765. }
  5766. /**
  5767. * Search question behaviours for the specified string
  5768. *
  5769. * @param string $query The string to search for in question behaviours
  5770. * @return array
  5771. */
  5772. public function search($query) {
  5773. global $CFG;
  5774. if ($result = parent::search($query)) {
  5775. return $result;
  5776. }
  5777. $found = false;
  5778. require_once($CFG->dirroot . '/question/engine/lib.php');
  5779. foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
  5780. if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
  5781. $query) !== false) {
  5782. $found = true;
  5783. break;
  5784. }
  5785. }
  5786. if ($found) {
  5787. $result = new stdClass();
  5788. $result->page = $this;
  5789. $result->settings = array();
  5790. return array($this->name => $result);
  5791. } else {
  5792. return array();
  5793. }
  5794. }
  5795. }
  5796. /**
  5797. * Question type manage page
  5798. *
  5799. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5800. */
  5801. class admin_page_manageqtypes extends admin_externalpage {
  5802. /**
  5803. * Calls parent::__construct with specific arguments
  5804. */
  5805. public function __construct() {
  5806. global $CFG;
  5807. parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
  5808. new moodle_url('/admin/qtypes.php'));
  5809. }
  5810. /**
  5811. * Search question types for the specified string
  5812. *
  5813. * @param string $query The string to search for in question types
  5814. * @return array
  5815. */
  5816. public function search($query) {
  5817. global $CFG;
  5818. if ($result = parent::search($query)) {
  5819. return $result;
  5820. }
  5821. $found = false;
  5822. require_once($CFG->dirroot . '/question/engine/bank.php');
  5823. foreach (question_bank::get_all_qtypes() as $qtype) {
  5824. if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
  5825. $found = true;
  5826. break;
  5827. }
  5828. }
  5829. if ($found) {
  5830. $result = new stdClass();
  5831. $result->page = $this;
  5832. $result->settings = array();
  5833. return array($this->name => $result);
  5834. } else {
  5835. return array();
  5836. }
  5837. }
  5838. }
  5839. class admin_page_manageportfolios extends admin_externalpage {
  5840. /**
  5841. * Calls parent::__construct with specific arguments
  5842. */
  5843. public function __construct() {
  5844. global $CFG;
  5845. parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
  5846. "$CFG->wwwroot/$CFG->admin/portfolio.php");
  5847. }
  5848. /**
  5849. * Searches page for the specified string.
  5850. * @param string $query The string to search for
  5851. * @return bool True if it is found on this page
  5852. */
  5853. public function search($query) {
  5854. global $CFG;
  5855. if ($result = parent::search($query)) {
  5856. return $result;
  5857. }
  5858. $found = false;
  5859. $portfolios = core_component::get_plugin_list('portfolio');
  5860. foreach ($portfolios as $p => $dir) {
  5861. if (strpos($p, $query) !== false) {
  5862. $found = true;
  5863. break;
  5864. }
  5865. }
  5866. if (!$found) {
  5867. foreach (portfolio_instances(false, false) as $instance) {
  5868. $title = $instance->get('name');
  5869. if (strpos(core_text::strtolower($title), $query) !== false) {
  5870. $found = true;
  5871. break;
  5872. }
  5873. }
  5874. }
  5875. if ($found) {
  5876. $result = new stdClass();
  5877. $result->page = $this;
  5878. $result->settings = array();
  5879. return array($this->name => $result);
  5880. } else {
  5881. return array();
  5882. }
  5883. }
  5884. }
  5885. class admin_page_managerepositories extends admin_externalpage {
  5886. /**
  5887. * Calls parent::__construct with specific arguments
  5888. */
  5889. public function __construct() {
  5890. global $CFG;
  5891. parent::__construct('managerepositories', get_string('manage',
  5892. 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
  5893. }
  5894. /**
  5895. * Searches page for the specified string.
  5896. * @param string $query The string to search for
  5897. * @return bool True if it is found on this page
  5898. */
  5899. public function search($query) {
  5900. global $CFG;
  5901. if ($result = parent::search($query)) {
  5902. return $result;
  5903. }
  5904. $found = false;
  5905. $repositories= core_component::get_plugin_list('repository');
  5906. foreach ($repositories as $p => $dir) {
  5907. if (strpos($p, $query) !== false) {
  5908. $found = true;
  5909. break;
  5910. }
  5911. }
  5912. if (!$found) {
  5913. foreach (repository::get_types() as $instance) {
  5914. $title = $instance->get_typename();
  5915. if (strpos(core_text::strtolower($title), $query) !== false) {
  5916. $found = true;
  5917. break;
  5918. }
  5919. }
  5920. }
  5921. if ($found) {
  5922. $result = new stdClass();
  5923. $result->page = $this;
  5924. $result->settings = array();
  5925. return array($this->name => $result);
  5926. } else {
  5927. return array();
  5928. }
  5929. }
  5930. }
  5931. /**
  5932. * Special class for authentication administration.
  5933. *
  5934. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  5935. */
  5936. class admin_setting_manageauths extends admin_setting {
  5937. /**
  5938. * Calls parent::__construct with specific arguments
  5939. */
  5940. public function __construct() {
  5941. $this->nosave = true;
  5942. parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
  5943. }
  5944. /**
  5945. * Always returns true
  5946. *
  5947. * @return true
  5948. */
  5949. public function get_setting() {
  5950. return true;
  5951. }
  5952. /**
  5953. * Always returns true
  5954. *
  5955. * @return true
  5956. */
  5957. public function get_defaultsetting() {
  5958. return true;
  5959. }
  5960. /**
  5961. * Always returns '' and doesn't write anything
  5962. *
  5963. * @return string Always returns ''
  5964. */
  5965. public function write_setting($data) {
  5966. // do not write any setting
  5967. return '';
  5968. }
  5969. /**
  5970. * Search to find if Query is related to auth plugin
  5971. *
  5972. * @param string $query The string to search for
  5973. * @return bool true for related false for not
  5974. */
  5975. public function is_related($query) {
  5976. if (parent::is_related($query)) {
  5977. return true;
  5978. }
  5979. $authsavailable = core_component::get_plugin_list('auth');
  5980. foreach ($authsavailable as $auth => $dir) {
  5981. if (strpos($auth, $query) !== false) {
  5982. return true;
  5983. }
  5984. $authplugin = get_auth_plugin($auth);
  5985. $authtitle = $authplugin->get_title();
  5986. if (strpos(core_text::strtolower($authtitle), $query) !== false) {
  5987. return true;
  5988. }
  5989. }
  5990. return false;
  5991. }
  5992. /**
  5993. * Return XHTML to display control
  5994. *
  5995. * @param mixed $data Unused
  5996. * @param string $query
  5997. * @return string highlight
  5998. */
  5999. public function output_html($data, $query='') {
  6000. global $CFG, $OUTPUT, $DB;
  6001. // display strings
  6002. $txt = get_strings(array('authenticationplugins', 'users', 'administration',
  6003. 'settings', 'edit', 'name', 'enable', 'disable',
  6004. 'up', 'down', 'none', 'users'));
  6005. $txt->updown = "$txt->up/$txt->down";
  6006. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  6007. $txt->testsettings = get_string('testsettings', 'core_auth');
  6008. $authsavailable = core_component::get_plugin_list('auth');
  6009. get_enabled_auth_plugins(true); // fix the list of enabled auths
  6010. if (empty($CFG->auth)) {
  6011. $authsenabled = array();
  6012. } else {
  6013. $authsenabled = explode(',', $CFG->auth);
  6014. }
  6015. // construct the display array, with enabled auth plugins at the top, in order
  6016. $displayauths = array();
  6017. $registrationauths = array();
  6018. $registrationauths[''] = $txt->disable;
  6019. $authplugins = array();
  6020. foreach ($authsenabled as $auth) {
  6021. $authplugin = get_auth_plugin($auth);
  6022. $authplugins[$auth] = $authplugin;
  6023. /// Get the auth title (from core or own auth lang files)
  6024. $authtitle = $authplugin->get_title();
  6025. /// Apply titles
  6026. $displayauths[$auth] = $authtitle;
  6027. if ($authplugin->can_signup()) {
  6028. $registrationauths[$auth] = $authtitle;
  6029. }
  6030. }
  6031. foreach ($authsavailable as $auth => $dir) {
  6032. if (array_key_exists($auth, $displayauths)) {
  6033. continue; //already in the list
  6034. }
  6035. $authplugin = get_auth_plugin($auth);
  6036. $authplugins[$auth] = $authplugin;
  6037. /// Get the auth title (from core or own auth lang files)
  6038. $authtitle = $authplugin->get_title();
  6039. /// Apply titles
  6040. $displayauths[$auth] = $authtitle;
  6041. if ($authplugin->can_signup()) {
  6042. $registrationauths[$auth] = $authtitle;
  6043. }
  6044. }
  6045. $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
  6046. $return .= $OUTPUT->box_start('generalbox authsui');
  6047. $table = new html_table();
  6048. $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
  6049. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  6050. $table->data = array();
  6051. $table->attributes['class'] = 'admintable generaltable';
  6052. $table->id = 'manageauthtable';
  6053. //add always enabled plugins first
  6054. $displayname = $displayauths['manual'];
  6055. $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
  6056. $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
  6057. $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
  6058. $displayname = $displayauths['nologin'];
  6059. $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
  6060. $table->data[] = array($displayname, $usercount, '', '', '', '', '');
  6061. // iterate through auth plugins and add to the display table
  6062. $updowncount = 1;
  6063. $authcount = count($authsenabled);
  6064. $url = "auth.php?sesskey=" . sesskey();
  6065. foreach ($displayauths as $auth => $name) {
  6066. if ($auth == 'manual' or $auth == 'nologin') {
  6067. continue;
  6068. }
  6069. $class = '';
  6070. // hide/show link
  6071. if (in_array($auth, $authsenabled)) {
  6072. $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
  6073. $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
  6074. $enabled = true;
  6075. $displayname = $name;
  6076. }
  6077. else {
  6078. $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
  6079. $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
  6080. $enabled = false;
  6081. $displayname = $name;
  6082. $class = 'dimmed_text';
  6083. }
  6084. $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
  6085. // up/down link (only if auth is enabled)
  6086. $updown = '';
  6087. if ($enabled) {
  6088. if ($updowncount > 1) {
  6089. $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
  6090. $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
  6091. }
  6092. else {
  6093. $updown .= $OUTPUT->spacer() . '&nbsp;';
  6094. }
  6095. if ($updowncount < $authcount) {
  6096. $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
  6097. $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
  6098. }
  6099. else {
  6100. $updown .= $OUTPUT->spacer() . '&nbsp;';
  6101. }
  6102. ++ $updowncount;
  6103. }
  6104. // settings link
  6105. if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
  6106. $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
  6107. } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
  6108. $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
  6109. } else {
  6110. $settings = '';
  6111. }
  6112. // Uninstall link.
  6113. $uninstall = '';
  6114. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
  6115. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  6116. }
  6117. $test = '';
  6118. if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
  6119. $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey()));
  6120. $test = html_writer::link($testurl, $txt->testsettings);
  6121. }
  6122. // Add a row to the table.
  6123. $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
  6124. if ($class) {
  6125. $row->attributes['class'] = $class;
  6126. }
  6127. $table->data[] = $row;
  6128. }
  6129. $return .= html_writer::table($table);
  6130. $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
  6131. $return .= $OUTPUT->box_end();
  6132. return highlight($query, $return);
  6133. }
  6134. }
  6135. /**
  6136. * Special class for authentication administration.
  6137. *
  6138. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6139. */
  6140. class admin_setting_manageeditors extends admin_setting {
  6141. /**
  6142. * Calls parent::__construct with specific arguments
  6143. */
  6144. public function __construct() {
  6145. $this->nosave = true;
  6146. parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
  6147. }
  6148. /**
  6149. * Always returns true, does nothing
  6150. *
  6151. * @return true
  6152. */
  6153. public function get_setting() {
  6154. return true;
  6155. }
  6156. /**
  6157. * Always returns true, does nothing
  6158. *
  6159. * @return true
  6160. */
  6161. public function get_defaultsetting() {
  6162. return true;
  6163. }
  6164. /**
  6165. * Always returns '', does not write anything
  6166. *
  6167. * @return string Always returns ''
  6168. */
  6169. public function write_setting($data) {
  6170. // do not write any setting
  6171. return '';
  6172. }
  6173. /**
  6174. * Checks if $query is one of the available editors
  6175. *
  6176. * @param string $query The string to search for
  6177. * @return bool Returns true if found, false if not
  6178. */
  6179. public function is_related($query) {
  6180. if (parent::is_related($query)) {
  6181. return true;
  6182. }
  6183. $editors_available = editors_get_available();
  6184. foreach ($editors_available as $editor=>$editorstr) {
  6185. if (strpos($editor, $query) !== false) {
  6186. return true;
  6187. }
  6188. if (strpos(core_text::strtolower($editorstr), $query) !== false) {
  6189. return true;
  6190. }
  6191. }
  6192. return false;
  6193. }
  6194. /**
  6195. * Builds the XHTML to display the control
  6196. *
  6197. * @param string $data Unused
  6198. * @param string $query
  6199. * @return string
  6200. */
  6201. public function output_html($data, $query='') {
  6202. global $CFG, $OUTPUT;
  6203. // display strings
  6204. $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
  6205. 'up', 'down', 'none'));
  6206. $struninstall = get_string('uninstallplugin', 'core_admin');
  6207. $txt->updown = "$txt->up/$txt->down";
  6208. $editors_available = editors_get_available();
  6209. $active_editors = explode(',', $CFG->texteditors);
  6210. $active_editors = array_reverse($active_editors);
  6211. foreach ($active_editors as $key=>$editor) {
  6212. if (empty($editors_available[$editor])) {
  6213. unset($active_editors[$key]);
  6214. } else {
  6215. $name = $editors_available[$editor];
  6216. unset($editors_available[$editor]);
  6217. $editors_available[$editor] = $name;
  6218. }
  6219. }
  6220. if (empty($active_editors)) {
  6221. //$active_editors = array('textarea');
  6222. }
  6223. $editors_available = array_reverse($editors_available, true);
  6224. $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
  6225. $return .= $OUTPUT->box_start('generalbox editorsui');
  6226. $table = new html_table();
  6227. $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
  6228. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  6229. $table->id = 'editormanagement';
  6230. $table->attributes['class'] = 'admintable generaltable';
  6231. $table->data = array();
  6232. // iterate through auth plugins and add to the display table
  6233. $updowncount = 1;
  6234. $editorcount = count($active_editors);
  6235. $url = "editors.php?sesskey=" . sesskey();
  6236. foreach ($editors_available as $editor => $name) {
  6237. // hide/show link
  6238. $class = '';
  6239. if (in_array($editor, $active_editors)) {
  6240. $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
  6241. $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
  6242. $enabled = true;
  6243. $displayname = $name;
  6244. }
  6245. else {
  6246. $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
  6247. $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
  6248. $enabled = false;
  6249. $displayname = $name;
  6250. $class = 'dimmed_text';
  6251. }
  6252. // up/down link (only if auth is enabled)
  6253. $updown = '';
  6254. if ($enabled) {
  6255. if ($updowncount > 1) {
  6256. $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
  6257. $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
  6258. }
  6259. else {
  6260. $updown .= $OUTPUT->spacer() . '&nbsp;';
  6261. }
  6262. if ($updowncount < $editorcount) {
  6263. $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
  6264. $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
  6265. }
  6266. else {
  6267. $updown .= $OUTPUT->spacer() . '&nbsp;';
  6268. }
  6269. ++ $updowncount;
  6270. }
  6271. // settings link
  6272. if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
  6273. $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
  6274. $settings = "<a href='$eurl'>{$txt->settings}</a>";
  6275. } else {
  6276. $settings = '';
  6277. }
  6278. $uninstall = '';
  6279. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('editor_'.$editor, 'manage')) {
  6280. $uninstall = html_writer::link($uninstallurl, $struninstall);
  6281. }
  6282. // Add a row to the table.
  6283. $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
  6284. if ($class) {
  6285. $row->attributes['class'] = $class;
  6286. }
  6287. $table->data[] = $row;
  6288. }
  6289. $return .= html_writer::table($table);
  6290. $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
  6291. $return .= $OUTPUT->box_end();
  6292. return highlight($query, $return);
  6293. }
  6294. }
  6295. /**
  6296. * Special class for antiviruses administration.
  6297. *
  6298. * @copyright 2015 Ruslan Kabalin, Lancaster University.
  6299. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6300. */
  6301. class admin_setting_manageantiviruses extends admin_setting {
  6302. /**
  6303. * Calls parent::__construct with specific arguments
  6304. */
  6305. public function __construct() {
  6306. $this->nosave = true;
  6307. parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', '');
  6308. }
  6309. /**
  6310. * Always returns true, does nothing
  6311. *
  6312. * @return true
  6313. */
  6314. public function get_setting() {
  6315. return true;
  6316. }
  6317. /**
  6318. * Always returns true, does nothing
  6319. *
  6320. * @return true
  6321. */
  6322. public function get_defaultsetting() {
  6323. return true;
  6324. }
  6325. /**
  6326. * Always returns '', does not write anything
  6327. *
  6328. * @param string $data Unused
  6329. * @return string Always returns ''
  6330. */
  6331. public function write_setting($data) {
  6332. // Do not write any setting.
  6333. return '';
  6334. }
  6335. /**
  6336. * Checks if $query is one of the available editors
  6337. *
  6338. * @param string $query The string to search for
  6339. * @return bool Returns true if found, false if not
  6340. */
  6341. public function is_related($query) {
  6342. if (parent::is_related($query)) {
  6343. return true;
  6344. }
  6345. $antivirusesavailable = \core\antivirus\manager::get_available();
  6346. foreach ($antivirusesavailable as $antivirus => $antivirusstr) {
  6347. if (strpos($antivirus, $query) !== false) {
  6348. return true;
  6349. }
  6350. if (strpos(core_text::strtolower($antivirusstr), $query) !== false) {
  6351. return true;
  6352. }
  6353. }
  6354. return false;
  6355. }
  6356. /**
  6357. * Builds the XHTML to display the control
  6358. *
  6359. * @param string $data Unused
  6360. * @param string $query
  6361. * @return string
  6362. */
  6363. public function output_html($data, $query='') {
  6364. global $CFG, $OUTPUT;
  6365. // Display strings.
  6366. $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
  6367. 'up', 'down', 'none'));
  6368. $struninstall = get_string('uninstallplugin', 'core_admin');
  6369. $txt->updown = "$txt->up/$txt->down";
  6370. $antivirusesavailable = \core\antivirus\manager::get_available();
  6371. $activeantiviruses = explode(',', $CFG->antiviruses);
  6372. $activeantiviruses = array_reverse($activeantiviruses);
  6373. foreach ($activeantiviruses as $key => $antivirus) {
  6374. if (empty($antivirusesavailable[$antivirus])) {
  6375. unset($activeantiviruses[$key]);
  6376. } else {
  6377. $name = $antivirusesavailable[$antivirus];
  6378. unset($antivirusesavailable[$antivirus]);
  6379. $antivirusesavailable[$antivirus] = $name;
  6380. }
  6381. }
  6382. $antivirusesavailable = array_reverse($antivirusesavailable, true);
  6383. $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true);
  6384. $return .= $OUTPUT->box_start('generalbox antivirusesui');
  6385. $table = new html_table();
  6386. $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
  6387. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  6388. $table->id = 'antivirusmanagement';
  6389. $table->attributes['class'] = 'admintable generaltable';
  6390. $table->data = array();
  6391. // Iterate through auth plugins and add to the display table.
  6392. $updowncount = 1;
  6393. $antiviruscount = count($activeantiviruses);
  6394. $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey()));
  6395. foreach ($antivirusesavailable as $antivirus => $name) {
  6396. // Hide/show link.
  6397. $class = '';
  6398. if (in_array($antivirus, $activeantiviruses)) {
  6399. $hideshowurl = $baseurl;
  6400. $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus));
  6401. $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable'));
  6402. $hideshow = html_writer::link($hideshowurl, $hideshowimg);
  6403. $enabled = true;
  6404. $displayname = $name;
  6405. } else {
  6406. $hideshowurl = $baseurl;
  6407. $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus));
  6408. $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable'));
  6409. $hideshow = html_writer::link($hideshowurl, $hideshowimg);
  6410. $enabled = false;
  6411. $displayname = $name;
  6412. $class = 'dimmed_text';
  6413. }
  6414. // Up/down link.
  6415. $updown = '';
  6416. if ($enabled) {
  6417. if ($updowncount > 1) {
  6418. $updownurl = $baseurl;
  6419. $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus));
  6420. $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup'));
  6421. $updown = html_writer::link($updownurl, $updownimg);
  6422. } else {
  6423. $updownimg = $OUTPUT->spacer();
  6424. }
  6425. if ($updowncount < $antiviruscount) {
  6426. $updownurl = $baseurl;
  6427. $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus));
  6428. $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown'));
  6429. $updown = html_writer::link($updownurl, $updownimg);
  6430. } else {
  6431. $updownimg = $OUTPUT->spacer();
  6432. }
  6433. ++ $updowncount;
  6434. }
  6435. // Settings link.
  6436. if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) {
  6437. $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus));
  6438. $settings = html_writer::link($eurl, $txt->settings);
  6439. } else {
  6440. $settings = '';
  6441. }
  6442. $uninstall = '';
  6443. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) {
  6444. $uninstall = html_writer::link($uninstallurl, $struninstall);
  6445. }
  6446. // Add a row to the table.
  6447. $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
  6448. if ($class) {
  6449. $row->attributes['class'] = $class;
  6450. }
  6451. $table->data[] = $row;
  6452. }
  6453. $return .= html_writer::table($table);
  6454. $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin');
  6455. $return .= $OUTPUT->box_end();
  6456. return highlight($query, $return);
  6457. }
  6458. }
  6459. /**
  6460. * Special class for license administration.
  6461. *
  6462. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6463. */
  6464. class admin_setting_managelicenses extends admin_setting {
  6465. /**
  6466. * Calls parent::__construct with specific arguments
  6467. */
  6468. public function __construct() {
  6469. $this->nosave = true;
  6470. parent::__construct('licensesui', get_string('licensesettings', 'admin'), '', '');
  6471. }
  6472. /**
  6473. * Always returns true, does nothing
  6474. *
  6475. * @return true
  6476. */
  6477. public function get_setting() {
  6478. return true;
  6479. }
  6480. /**
  6481. * Always returns true, does nothing
  6482. *
  6483. * @return true
  6484. */
  6485. public function get_defaultsetting() {
  6486. return true;
  6487. }
  6488. /**
  6489. * Always returns '', does not write anything
  6490. *
  6491. * @return string Always returns ''
  6492. */
  6493. public function write_setting($data) {
  6494. // do not write any setting
  6495. return '';
  6496. }
  6497. /**
  6498. * Builds the XHTML to display the control
  6499. *
  6500. * @param string $data Unused
  6501. * @param string $query
  6502. * @return string
  6503. */
  6504. public function output_html($data, $query='') {
  6505. global $CFG, $OUTPUT;
  6506. require_once($CFG->libdir . '/licenselib.php');
  6507. $url = "licenses.php?sesskey=" . sesskey();
  6508. // display strings
  6509. $txt = get_strings(array('administration', 'settings', 'name', 'enable', 'disable', 'none'));
  6510. $licenses = license_manager::get_licenses();
  6511. $return = $OUTPUT->heading(get_string('availablelicenses', 'admin'), 3, 'main', true);
  6512. $return .= $OUTPUT->box_start('generalbox editorsui');
  6513. $table = new html_table();
  6514. $table->head = array($txt->name, $txt->enable);
  6515. $table->colclasses = array('leftalign', 'centeralign');
  6516. $table->id = 'availablelicenses';
  6517. $table->attributes['class'] = 'admintable generaltable';
  6518. $table->data = array();
  6519. foreach ($licenses as $value) {
  6520. $displayname = html_writer::link($value->source, get_string($value->shortname, 'license'), array('target'=>'_blank'));
  6521. if ($value->enabled == 1) {
  6522. $hideshow = html_writer::link($url.'&action=disable&license='.$value->shortname,
  6523. $OUTPUT->pix_icon('t/hide', get_string('disable')));
  6524. } else {
  6525. $hideshow = html_writer::link($url.'&action=enable&license='.$value->shortname,
  6526. $OUTPUT->pix_icon('t/show', get_string('enable')));
  6527. }
  6528. if ($value->shortname == $CFG->sitedefaultlicense) {
  6529. $displayname .= ' '.$OUTPUT->pix_icon('t/locked', get_string('default'));
  6530. $hideshow = '';
  6531. }
  6532. $enabled = true;
  6533. $table->data[] =array($displayname, $hideshow);
  6534. }
  6535. $return .= html_writer::table($table);
  6536. $return .= $OUTPUT->box_end();
  6537. return highlight($query, $return);
  6538. }
  6539. }
  6540. /**
  6541. * Course formats manager. Allows to enable/disable formats and jump to settings
  6542. */
  6543. class admin_setting_manageformats extends admin_setting {
  6544. /**
  6545. * Calls parent::__construct with specific arguments
  6546. */
  6547. public function __construct() {
  6548. $this->nosave = true;
  6549. parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
  6550. }
  6551. /**
  6552. * Always returns true
  6553. *
  6554. * @return true
  6555. */
  6556. public function get_setting() {
  6557. return true;
  6558. }
  6559. /**
  6560. * Always returns true
  6561. *
  6562. * @return true
  6563. */
  6564. public function get_defaultsetting() {
  6565. return true;
  6566. }
  6567. /**
  6568. * Always returns '' and doesn't write anything
  6569. *
  6570. * @param mixed $data string or array, must not be NULL
  6571. * @return string Always returns ''
  6572. */
  6573. public function write_setting($data) {
  6574. // do not write any setting
  6575. return '';
  6576. }
  6577. /**
  6578. * Search to find if Query is related to format plugin
  6579. *
  6580. * @param string $query The string to search for
  6581. * @return bool true for related false for not
  6582. */
  6583. public function is_related($query) {
  6584. if (parent::is_related($query)) {
  6585. return true;
  6586. }
  6587. $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
  6588. foreach ($formats as $format) {
  6589. if (strpos($format->component, $query) !== false ||
  6590. strpos(core_text::strtolower($format->displayname), $query) !== false) {
  6591. return true;
  6592. }
  6593. }
  6594. return false;
  6595. }
  6596. /**
  6597. * Return XHTML to display control
  6598. *
  6599. * @param mixed $data Unused
  6600. * @param string $query
  6601. * @return string highlight
  6602. */
  6603. public function output_html($data, $query='') {
  6604. global $CFG, $OUTPUT;
  6605. $return = '';
  6606. $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
  6607. $return .= $OUTPUT->box_start('generalbox formatsui');
  6608. $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
  6609. // display strings
  6610. $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
  6611. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  6612. $txt->updown = "$txt->up/$txt->down";
  6613. $table = new html_table();
  6614. $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
  6615. $table->align = array('left', 'center', 'center', 'center', 'center');
  6616. $table->attributes['class'] = 'manageformattable generaltable admintable';
  6617. $table->data = array();
  6618. $cnt = 0;
  6619. $defaultformat = get_config('moodlecourse', 'format');
  6620. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  6621. foreach ($formats as $format) {
  6622. $url = new moodle_url('/admin/courseformats.php',
  6623. array('sesskey' => sesskey(), 'format' => $format->name));
  6624. $isdefault = '';
  6625. $class = '';
  6626. if ($format->is_enabled()) {
  6627. $strformatname = $format->displayname;
  6628. if ($defaultformat === $format->name) {
  6629. $hideshow = $txt->default;
  6630. } else {
  6631. $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
  6632. $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
  6633. }
  6634. } else {
  6635. $strformatname = $format->displayname;
  6636. $class = 'dimmed_text';
  6637. $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
  6638. $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
  6639. }
  6640. $updown = '';
  6641. if ($cnt) {
  6642. $updown .= html_writer::link($url->out(false, array('action' => 'up')),
  6643. $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
  6644. } else {
  6645. $updown .= $spacer;
  6646. }
  6647. if ($cnt < count($formats) - 1) {
  6648. $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
  6649. $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
  6650. } else {
  6651. $updown .= $spacer;
  6652. }
  6653. $cnt++;
  6654. $settings = '';
  6655. if ($format->get_settings_url()) {
  6656. $settings = html_writer::link($format->get_settings_url(), $txt->settings);
  6657. }
  6658. $uninstall = '';
  6659. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
  6660. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  6661. }
  6662. $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
  6663. if ($class) {
  6664. $row->attributes['class'] = $class;
  6665. }
  6666. $table->data[] = $row;
  6667. }
  6668. $return .= html_writer::table($table);
  6669. $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
  6670. $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
  6671. $return .= $OUTPUT->box_end();
  6672. return highlight($query, $return);
  6673. }
  6674. }
  6675. /**
  6676. * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
  6677. *
  6678. * @package core
  6679. * @copyright 2018 Toni Barbera
  6680. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6681. */
  6682. class admin_setting_managecustomfields extends admin_setting {
  6683. /**
  6684. * Calls parent::__construct with specific arguments
  6685. */
  6686. public function __construct() {
  6687. $this->nosave = true;
  6688. parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
  6689. }
  6690. /**
  6691. * Always returns true
  6692. *
  6693. * @return true
  6694. */
  6695. public function get_setting() {
  6696. return true;
  6697. }
  6698. /**
  6699. * Always returns true
  6700. *
  6701. * @return true
  6702. */
  6703. public function get_defaultsetting() {
  6704. return true;
  6705. }
  6706. /**
  6707. * Always returns '' and doesn't write anything
  6708. *
  6709. * @param mixed $data string or array, must not be NULL
  6710. * @return string Always returns ''
  6711. */
  6712. public function write_setting($data) {
  6713. // Do not write any setting.
  6714. return '';
  6715. }
  6716. /**
  6717. * Search to find if Query is related to format plugin
  6718. *
  6719. * @param string $query The string to search for
  6720. * @return bool true for related false for not
  6721. */
  6722. public function is_related($query) {
  6723. if (parent::is_related($query)) {
  6724. return true;
  6725. }
  6726. $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
  6727. foreach ($formats as $format) {
  6728. if (strpos($format->component, $query) !== false ||
  6729. strpos(core_text::strtolower($format->displayname), $query) !== false) {
  6730. return true;
  6731. }
  6732. }
  6733. return false;
  6734. }
  6735. /**
  6736. * Return XHTML to display control
  6737. *
  6738. * @param mixed $data Unused
  6739. * @param string $query
  6740. * @return string highlight
  6741. */
  6742. public function output_html($data, $query='') {
  6743. global $CFG, $OUTPUT;
  6744. $return = '';
  6745. $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
  6746. $return .= $OUTPUT->box_start('generalbox customfieldsui');
  6747. $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
  6748. $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
  6749. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  6750. $txt->updown = "$txt->up/$txt->down";
  6751. $table = new html_table();
  6752. $table->head = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
  6753. $table->align = array('left', 'center', 'center', 'center');
  6754. $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
  6755. $table->data = array();
  6756. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  6757. foreach ($fields as $field) {
  6758. $url = new moodle_url('/admin/customfields.php',
  6759. array('sesskey' => sesskey(), 'field' => $field->name));
  6760. if ($field->is_enabled()) {
  6761. $strfieldname = $field->displayname;
  6762. $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
  6763. $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
  6764. } else {
  6765. $strfieldname = $field->displayname;
  6766. $class = 'dimmed_text';
  6767. $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
  6768. $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
  6769. }
  6770. $settings = '';
  6771. if ($field->get_settings_url()) {
  6772. $settings = html_writer::link($field->get_settings_url(), $txt->settings);
  6773. }
  6774. $uninstall = '';
  6775. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
  6776. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  6777. }
  6778. $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
  6779. $table->data[] = $row;
  6780. }
  6781. $return .= html_writer::table($table);
  6782. $return .= $OUTPUT->box_end();
  6783. return highlight($query, $return);
  6784. }
  6785. }
  6786. /**
  6787. * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
  6788. *
  6789. * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net)
  6790. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6791. */
  6792. class admin_setting_managedataformats extends admin_setting {
  6793. /**
  6794. * Calls parent::__construct with specific arguments
  6795. */
  6796. public function __construct() {
  6797. $this->nosave = true;
  6798. parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
  6799. }
  6800. /**
  6801. * Always returns true
  6802. *
  6803. * @return true
  6804. */
  6805. public function get_setting() {
  6806. return true;
  6807. }
  6808. /**
  6809. * Always returns true
  6810. *
  6811. * @return true
  6812. */
  6813. public function get_defaultsetting() {
  6814. return true;
  6815. }
  6816. /**
  6817. * Always returns '' and doesn't write anything
  6818. *
  6819. * @param mixed $data string or array, must not be NULL
  6820. * @return string Always returns ''
  6821. */
  6822. public function write_setting($data) {
  6823. // Do not write any setting.
  6824. return '';
  6825. }
  6826. /**
  6827. * Search to find if Query is related to format plugin
  6828. *
  6829. * @param string $query The string to search for
  6830. * @return bool true for related false for not
  6831. */
  6832. public function is_related($query) {
  6833. if (parent::is_related($query)) {
  6834. return true;
  6835. }
  6836. $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
  6837. foreach ($formats as $format) {
  6838. if (strpos($format->component, $query) !== false ||
  6839. strpos(core_text::strtolower($format->displayname), $query) !== false) {
  6840. return true;
  6841. }
  6842. }
  6843. return false;
  6844. }
  6845. /**
  6846. * Return XHTML to display control
  6847. *
  6848. * @param mixed $data Unused
  6849. * @param string $query
  6850. * @return string highlight
  6851. */
  6852. public function output_html($data, $query='') {
  6853. global $CFG, $OUTPUT;
  6854. $return = '';
  6855. $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
  6856. $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
  6857. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  6858. $txt->updown = "$txt->up/$txt->down";
  6859. $table = new html_table();
  6860. $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
  6861. $table->align = array('left', 'center', 'center', 'center', 'center');
  6862. $table->attributes['class'] = 'manageformattable generaltable admintable';
  6863. $table->data = array();
  6864. $cnt = 0;
  6865. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  6866. $totalenabled = 0;
  6867. foreach ($formats as $format) {
  6868. if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
  6869. $totalenabled++;
  6870. }
  6871. }
  6872. foreach ($formats as $format) {
  6873. $status = $format->get_status();
  6874. $url = new moodle_url('/admin/dataformats.php',
  6875. array('sesskey' => sesskey(), 'name' => $format->name));
  6876. $class = '';
  6877. if ($format->is_enabled()) {
  6878. $strformatname = $format->displayname;
  6879. if ($totalenabled == 1&& $format->is_enabled()) {
  6880. $hideshow = '';
  6881. } else {
  6882. $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
  6883. $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
  6884. }
  6885. } else {
  6886. $class = 'dimmed_text';
  6887. $strformatname = $format->displayname;
  6888. $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
  6889. $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
  6890. }
  6891. $updown = '';
  6892. if ($cnt) {
  6893. $updown .= html_writer::link($url->out(false, array('action' => 'up')),
  6894. $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
  6895. } else {
  6896. $updown .= $spacer;
  6897. }
  6898. if ($cnt < count($formats) - 1) {
  6899. $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
  6900. $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
  6901. } else {
  6902. $updown .= $spacer;
  6903. }
  6904. $uninstall = '';
  6905. if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
  6906. $uninstall = get_string('status_missing', 'core_plugin');
  6907. } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
  6908. $uninstall = get_string('status_new', 'core_plugin');
  6909. } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
  6910. if ($totalenabled != 1 || !$format->is_enabled()) {
  6911. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  6912. }
  6913. }
  6914. $settings = '';
  6915. if ($format->get_settings_url()) {
  6916. $settings = html_writer::link($format->get_settings_url(), $txt->settings);
  6917. }
  6918. $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
  6919. if ($class) {
  6920. $row->attributes['class'] = $class;
  6921. }
  6922. $table->data[] = $row;
  6923. $cnt++;
  6924. }
  6925. $return .= html_writer::table($table);
  6926. return highlight($query, $return);
  6927. }
  6928. }
  6929. /**
  6930. * Special class for filter administration.
  6931. *
  6932. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6933. */
  6934. class admin_page_managefilters extends admin_externalpage {
  6935. /**
  6936. * Calls parent::__construct with specific arguments
  6937. */
  6938. public function __construct() {
  6939. global $CFG;
  6940. parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
  6941. }
  6942. /**
  6943. * Searches all installed filters for specified filter
  6944. *
  6945. * @param string $query The filter(string) to search for
  6946. * @param string $query
  6947. */
  6948. public function search($query) {
  6949. global $CFG;
  6950. if ($result = parent::search($query)) {
  6951. return $result;
  6952. }
  6953. $found = false;
  6954. $filternames = filter_get_all_installed();
  6955. foreach ($filternames as $path => $strfiltername) {
  6956. if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
  6957. $found = true;
  6958. break;
  6959. }
  6960. if (strpos($path, $query) !== false) {
  6961. $found = true;
  6962. break;
  6963. }
  6964. }
  6965. if ($found) {
  6966. $result = new stdClass;
  6967. $result->page = $this;
  6968. $result->settings = array();
  6969. return array($this->name => $result);
  6970. } else {
  6971. return array();
  6972. }
  6973. }
  6974. }
  6975. /**
  6976. * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
  6977. * Requires a get_rank method on the plugininfo class for sorting.
  6978. *
  6979. * @copyright 2017 Damyon Wiese
  6980. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  6981. */
  6982. abstract class admin_setting_manage_plugins extends admin_setting {
  6983. /**
  6984. * Get the admin settings section name (just a unique string)
  6985. *
  6986. * @return string
  6987. */
  6988. public function get_section_name() {
  6989. return 'manage' . $this->get_plugin_type() . 'plugins';
  6990. }
  6991. /**
  6992. * Get the admin settings section title (use get_string).
  6993. *
  6994. * @return string
  6995. */
  6996. abstract public function get_section_title();
  6997. /**
  6998. * Get the type of plugin to manage.
  6999. *
  7000. * @return string
  7001. */
  7002. abstract public function get_plugin_type();
  7003. /**
  7004. * Get the name of the second column.
  7005. *
  7006. * @return string
  7007. */
  7008. public function get_info_column_name() {
  7009. return '';
  7010. }
  7011. /**
  7012. * Get the type of plugin to manage.
  7013. *
  7014. * @param plugininfo The plugin info class.
  7015. * @return string
  7016. */
  7017. abstract public function get_info_column($plugininfo);
  7018. /**
  7019. * Calls parent::__construct with specific arguments
  7020. */
  7021. public function __construct() {
  7022. $this->nosave = true;
  7023. parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
  7024. }
  7025. /**
  7026. * Always returns true, does nothing
  7027. *
  7028. * @return true
  7029. */
  7030. public function get_setting() {
  7031. return true;
  7032. }
  7033. /**
  7034. * Always returns true, does nothing
  7035. *
  7036. * @return true
  7037. */
  7038. public function get_defaultsetting() {
  7039. return true;
  7040. }
  7041. /**
  7042. * Always returns '', does not write anything
  7043. *
  7044. * @param mixed $data
  7045. * @return string Always returns ''
  7046. */
  7047. public function write_setting($data) {
  7048. // Do not write any setting.
  7049. return '';
  7050. }
  7051. /**
  7052. * Checks if $query is one of the available plugins of this type
  7053. *
  7054. * @param string $query The string to search for
  7055. * @return bool Returns true if found, false if not
  7056. */
  7057. public function is_related($query) {
  7058. if (parent::is_related($query)) {
  7059. return true;
  7060. }
  7061. $query = core_text::strtolower($query);
  7062. $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
  7063. foreach ($plugins as $name => $plugin) {
  7064. $localised = $plugin->displayname;
  7065. if (strpos(core_text::strtolower($name), $query) !== false) {
  7066. return true;
  7067. }
  7068. if (strpos(core_text::strtolower($localised), $query) !== false) {
  7069. return true;
  7070. }
  7071. }
  7072. return false;
  7073. }
  7074. /**
  7075. * The URL for the management page for this plugintype.
  7076. *
  7077. * @return moodle_url
  7078. */
  7079. protected function get_manage_url() {
  7080. return new moodle_url('/admin/updatesetting.php');
  7081. }
  7082. /**
  7083. * Builds the HTML to display the control.
  7084. *
  7085. * @param string $data Unused
  7086. * @param string $query
  7087. * @return string
  7088. */
  7089. public function output_html($data, $query = '') {
  7090. global $CFG, $OUTPUT, $DB, $PAGE;
  7091. $context = (object) [
  7092. 'manageurl' => new moodle_url($this->get_manage_url(), [
  7093. 'type' => $this->get_plugin_type(),
  7094. 'sesskey' => sesskey(),
  7095. ]),
  7096. 'infocolumnname' => $this->get_info_column_name(),
  7097. 'plugins' => [],
  7098. ];
  7099. $pluginmanager = core_plugin_manager::instance();
  7100. $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
  7101. $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
  7102. $plugins = array_merge($enabled, $allplugins);
  7103. foreach ($plugins as $key => $plugin) {
  7104. $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
  7105. $pluginkey = (object) [
  7106. 'plugin' => $plugin->displayname,
  7107. 'enabled' => $plugin->is_enabled(),
  7108. 'togglelink' => '',
  7109. 'moveuplink' => '',
  7110. 'movedownlink' => '',
  7111. 'settingslink' => $plugin->get_settings_url(),
  7112. 'uninstalllink' => '',
  7113. 'info' => '',
  7114. ];
  7115. // Enable/Disable link.
  7116. $togglelink = new moodle_url($pluginlink);
  7117. if ($plugin->is_enabled()) {
  7118. $toggletarget = false;
  7119. $togglelink->param('action', 'disable');
  7120. if (count($context->plugins)) {
  7121. // This is not the first plugin.
  7122. $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
  7123. }
  7124. if (count($enabled) > count($context->plugins) + 1) {
  7125. // This is not the last plugin.
  7126. $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
  7127. }
  7128. $pluginkey->info = $this->get_info_column($plugin);
  7129. } else {
  7130. $toggletarget = true;
  7131. $togglelink->param('action', 'enable');
  7132. }
  7133. $pluginkey->toggletarget = $toggletarget;
  7134. $pluginkey->togglelink = $togglelink;
  7135. $frankenstyle = $plugin->type . '_' . $plugin->name;
  7136. if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
  7137. // This plugin supports uninstallation.
  7138. $pluginkey->uninstalllink = $uninstalllink;
  7139. }
  7140. if (!empty($this->get_info_column_name())) {
  7141. // This plugintype has an info column.
  7142. $pluginkey->info = $this->get_info_column($plugin);
  7143. }
  7144. $context->plugins[] = $pluginkey;
  7145. }
  7146. $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
  7147. return highlight($query, $str);
  7148. }
  7149. }
  7150. /**
  7151. * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
  7152. * Requires a get_rank method on the plugininfo class for sorting.
  7153. *
  7154. * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
  7155. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7156. */
  7157. class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
  7158. public function get_section_title() {
  7159. return get_string('type_fileconverter_plural', 'plugin');
  7160. }
  7161. public function get_plugin_type() {
  7162. return 'fileconverter';
  7163. }
  7164. public function get_info_column_name() {
  7165. return get_string('supportedconversions', 'plugin');
  7166. }
  7167. public function get_info_column($plugininfo) {
  7168. return $plugininfo->get_supported_conversions();
  7169. }
  7170. }
  7171. /**
  7172. * Special class for media player plugins management.
  7173. *
  7174. * @copyright 2016 Marina Glancy
  7175. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7176. */
  7177. class admin_setting_managemediaplayers extends admin_setting {
  7178. /**
  7179. * Calls parent::__construct with specific arguments
  7180. */
  7181. public function __construct() {
  7182. $this->nosave = true;
  7183. parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
  7184. }
  7185. /**
  7186. * Always returns true, does nothing
  7187. *
  7188. * @return true
  7189. */
  7190. public function get_setting() {
  7191. return true;
  7192. }
  7193. /**
  7194. * Always returns true, does nothing
  7195. *
  7196. * @return true
  7197. */
  7198. public function get_defaultsetting() {
  7199. return true;
  7200. }
  7201. /**
  7202. * Always returns '', does not write anything
  7203. *
  7204. * @param mixed $data
  7205. * @return string Always returns ''
  7206. */
  7207. public function write_setting($data) {
  7208. // Do not write any setting.
  7209. return '';
  7210. }
  7211. /**
  7212. * Checks if $query is one of the available enrol plugins
  7213. *
  7214. * @param string $query The string to search for
  7215. * @return bool Returns true if found, false if not
  7216. */
  7217. public function is_related($query) {
  7218. if (parent::is_related($query)) {
  7219. return true;
  7220. }
  7221. $query = core_text::strtolower($query);
  7222. $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
  7223. foreach ($plugins as $name => $plugin) {
  7224. $localised = $plugin->displayname;
  7225. if (strpos(core_text::strtolower($name), $query) !== false) {
  7226. return true;
  7227. }
  7228. if (strpos(core_text::strtolower($localised), $query) !== false) {
  7229. return true;
  7230. }
  7231. }
  7232. return false;
  7233. }
  7234. /**
  7235. * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
  7236. * @return \core\plugininfo\media[]
  7237. */
  7238. protected function get_sorted_plugins() {
  7239. $pluginmanager = core_plugin_manager::instance();
  7240. $plugins = $pluginmanager->get_plugins_of_type('media');
  7241. $enabledplugins = $pluginmanager->get_enabled_plugins('media');
  7242. // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
  7243. \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
  7244. $order = array_values($enabledplugins);
  7245. $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
  7246. $sortedplugins = array();
  7247. foreach ($order as $name) {
  7248. $sortedplugins[$name] = $plugins[$name];
  7249. }
  7250. return $sortedplugins;
  7251. }
  7252. /**
  7253. * Builds the XHTML to display the control
  7254. *
  7255. * @param string $data Unused
  7256. * @param string $query
  7257. * @return string
  7258. */
  7259. public function output_html($data, $query='') {
  7260. global $CFG, $OUTPUT, $DB, $PAGE;
  7261. // Display strings.
  7262. $strup = get_string('up');
  7263. $strdown = get_string('down');
  7264. $strsettings = get_string('settings');
  7265. $strenable = get_string('enable');
  7266. $strdisable = get_string('disable');
  7267. $struninstall = get_string('uninstallplugin', 'core_admin');
  7268. $strversion = get_string('version');
  7269. $strname = get_string('name');
  7270. $strsupports = get_string('supports', 'core_media');
  7271. $pluginmanager = core_plugin_manager::instance();
  7272. $plugins = $this->get_sorted_plugins();
  7273. $enabledplugins = $pluginmanager->get_enabled_plugins('media');
  7274. $return = $OUTPUT->box_start('generalbox mediaplayersui');
  7275. $table = new html_table();
  7276. $table->head = array($strname, $strsupports, $strversion,
  7277. $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
  7278. $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
  7279. 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  7280. $table->id = 'mediaplayerplugins';
  7281. $table->attributes['class'] = 'admintable generaltable';
  7282. $table->data = array();
  7283. // Iterate through media plugins and add to the display table.
  7284. $updowncount = 1;
  7285. $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
  7286. $printed = array();
  7287. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  7288. $usedextensions = [];
  7289. foreach ($plugins as $name => $plugin) {
  7290. $url->param('media', $name);
  7291. $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
  7292. $version = $plugininfo->versiondb;
  7293. $supports = $plugininfo->supports($usedextensions);
  7294. // Hide/show links.
  7295. $class = '';
  7296. if (!$plugininfo->is_installed_and_upgraded()) {
  7297. $hideshow = '';
  7298. $enabled = false;
  7299. $displayname = '<span class="notifyproblem">'.$name.'</span>';
  7300. } else {
  7301. $enabled = $plugininfo->is_enabled();
  7302. if ($enabled) {
  7303. $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
  7304. $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
  7305. } else {
  7306. $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
  7307. $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
  7308. $class = 'dimmed_text';
  7309. }
  7310. $displayname = $plugin->displayname;
  7311. if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
  7312. $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
  7313. }
  7314. }
  7315. if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
  7316. $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
  7317. } else {
  7318. $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
  7319. }
  7320. // Up/down link (only if enrol is enabled).
  7321. $updown = '';
  7322. if ($enabled) {
  7323. if ($updowncount > 1) {
  7324. $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
  7325. $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
  7326. } else {
  7327. $updown = $spacer;
  7328. }
  7329. if ($updowncount < count($enabledplugins)) {
  7330. $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
  7331. $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
  7332. } else {
  7333. $updown .= $spacer;
  7334. }
  7335. ++$updowncount;
  7336. }
  7337. $uninstall = '';
  7338. $status = $plugininfo->get_status();
  7339. if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
  7340. $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
  7341. }
  7342. if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
  7343. $uninstall = get_string('status_new', 'core_plugin');
  7344. } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
  7345. $uninstall .= html_writer::link($uninstallurl, $struninstall);
  7346. }
  7347. $settings = '';
  7348. if ($plugininfo->get_settings_url()) {
  7349. $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
  7350. }
  7351. // Add a row to the table.
  7352. $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
  7353. if ($class) {
  7354. $row->attributes['class'] = $class;
  7355. }
  7356. $table->data[] = $row;
  7357. $printed[$name] = true;
  7358. }
  7359. $return .= html_writer::table($table);
  7360. $return .= $OUTPUT->box_end();
  7361. return highlight($query, $return);
  7362. }
  7363. }
  7364. /**
  7365. * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
  7366. *
  7367. * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
  7368. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  7369. */
  7370. class admin_setting_managecontentbankcontenttypes extends admin_setting {
  7371. /**
  7372. * Calls parent::__construct with specific arguments
  7373. */
  7374. public function __construct() {
  7375. $this->nosave = true;
  7376. parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
  7377. }
  7378. /**
  7379. * Always returns true
  7380. *
  7381. * @return true
  7382. */
  7383. public function get_setting() {
  7384. return true;
  7385. }
  7386. /**
  7387. * Always returns true
  7388. *
  7389. * @return true
  7390. */
  7391. public function get_defaultsetting() {
  7392. return true;
  7393. }
  7394. /**
  7395. * Always returns '' and doesn't write anything
  7396. *
  7397. * @param mixed $data string or array, must not be NULL
  7398. * @return string Always returns ''
  7399. */
  7400. public function write_setting($data) {
  7401. // Do not write any setting.
  7402. return '';
  7403. }
  7404. /**
  7405. * Search to find if Query is related to content bank plugin
  7406. *
  7407. * @param string $query The string to search for
  7408. * @return bool true for related false for not
  7409. */
  7410. public function is_related($query) {
  7411. if (parent::is_related($query)) {
  7412. return true;
  7413. }
  7414. $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
  7415. foreach ($types as $type) {
  7416. if (strpos($type->component, $query) !== false ||
  7417. strpos(core_text::strtolower($type->displayname), $query) !== false) {
  7418. return true;
  7419. }
  7420. }
  7421. return false;
  7422. }
  7423. /**
  7424. * Return XHTML to display control
  7425. *
  7426. * @param mixed $data Unused
  7427. * @param string $query
  7428. * @return string highlight
  7429. */
  7430. public function output_html($data, $query='') {
  7431. global $CFG, $OUTPUT;
  7432. $return = '';
  7433. $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
  7434. $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
  7435. $txt->uninstall = get_string('uninstallplugin', 'core_admin');
  7436. $table = new html_table();
  7437. $table->head = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
  7438. $table->align = array('left', 'center', 'center', 'center', 'center');
  7439. $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
  7440. $table->data = array();
  7441. $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
  7442. $totalenabled = 0;
  7443. $count = 0;
  7444. foreach ($types as $type) {
  7445. if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
  7446. $totalenabled++;
  7447. }
  7448. }
  7449. foreach ($types as $type) {
  7450. $url = new moodle_url('/admin/contentbank.php',
  7451. array('sesskey' => sesskey(), 'name' => $type->name));
  7452. $class = '';
  7453. $strtypename = $type->displayname;
  7454. if ($type->is_enabled()) {
  7455. $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
  7456. $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
  7457. } else {
  7458. $class = 'dimmed_text';
  7459. $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
  7460. $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
  7461. }
  7462. $updown = '';
  7463. if ($count) {
  7464. $updown .= html_writer::link($url->out(false, array('action' => 'up')),
  7465. $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
  7466. } else {
  7467. $updown .= $spacer;
  7468. }
  7469. if ($count < count($types) - 1) {
  7470. $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
  7471. $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
  7472. } else {
  7473. $updown .= $spacer;
  7474. }
  7475. $settings = '';
  7476. if ($type->get_settings_url()) {
  7477. $settings = html_writer::link($type->get_settings_url(), $txt->settings);
  7478. }
  7479. $uninstall = '';
  7480. if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
  7481. $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
  7482. }
  7483. $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
  7484. if ($class) {
  7485. $row->attributes['class'] = $class;
  7486. }
  7487. $table->data[] = $row;
  7488. $count++;
  7489. }
  7490. $return .= html_writer::table($table);
  7491. return highlight($query, $return);
  7492. }
  7493. }
  7494. /**
  7495. * Initialise admin page - this function does require login and permission
  7496. * checks specified in page definition.
  7497. *
  7498. * This function must be called on each admin page before other code.
  7499. *
  7500. * @global moodle_page $PAGE
  7501. *
  7502. * @param string $section name of page
  7503. * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
  7504. * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
  7505. * added to the turn blocks editing on/off form, so this page reloads correctly.
  7506. * @param string $actualurl if the actual page being viewed is not the normal one for this
  7507. * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
  7508. * @param array $options Additional options that can be specified for page setup.
  7509. * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
  7510. */
  7511. function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
  7512. global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
  7513. $PAGE->set_context(null); // hack - set context to something, by default to system context
  7514. $site = get_site();
  7515. require_login(null, false);
  7516. if (!empty($options['pagelayout'])) {
  7517. // A specific page layout has been requested.
  7518. $PAGE->set_pagelayout($options['pagelayout']);
  7519. } else if ($section === 'upgradesettings') {
  7520. $PAGE->set_pagelayout('maintenance');
  7521. } else {
  7522. $PAGE->set_pagelayout('admin');
  7523. }
  7524. $adminroot = admin_get_root(false, false); // settings not required for external pages
  7525. $extpage = $adminroot->locate($section, true);
  7526. if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
  7527. // The requested section isn't in the admin tree
  7528. // It could be because the user has inadequate capapbilities or because the section doesn't exist
  7529. if (!has_capability('moodle/site:config', context_system::instance())) {
  7530. // The requested section could depend on a different capability
  7531. // but most likely the user has inadequate capabilities
  7532. print_error('accessdenied', 'admin');
  7533. } else {
  7534. print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
  7535. }
  7536. }
  7537. // this eliminates our need to authenticate on the actual pages
  7538. if (!$extpage->check_access()) {
  7539. print_error('accessdenied', 'admin');
  7540. die;
  7541. }
  7542. navigation_node::require_admin_tree();
  7543. // $PAGE->set_extra_button($extrabutton); TODO
  7544. if (!$actualurl) {
  7545. $actualurl = $extpage->url;
  7546. }
  7547. $PAGE->set_url($actualurl, $extraurlparams);
  7548. if (strpos($PAGE->pagetype, 'admin-') !== 0) {
  7549. $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
  7550. }
  7551. if (empty($SITE->fullname) || empty($SITE->shortname)) {
  7552. // During initial install.
  7553. $strinstallation = get_string('installation', 'install');
  7554. $strsettings = get_string('settings');
  7555. $PAGE->navbar->add($strsettings);
  7556. $PAGE->set_title($strinstallation);
  7557. $PAGE->set_heading($strinstallation);
  7558. $PAGE->set_cacheable(false);
  7559. return;
  7560. }
  7561. // Locate the current item on the navigation and make it active when found.
  7562. $path = $extpage->path;
  7563. $node = $PAGE->settingsnav;
  7564. while ($node && count($path) > 0) {
  7565. $node = $node->get(array_pop($path));
  7566. }
  7567. if ($node) {
  7568. $node->make_active();
  7569. }
  7570. // Normal case.
  7571. $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
  7572. if ($PAGE->user_allowed_editing() && $adminediting != -1) {
  7573. $USER->editing = $adminediting;
  7574. }
  7575. $visiblepathtosection = array_reverse($extpage->visiblepath);
  7576. if ($PAGE->user_allowed_editing()) {
  7577. if ($PAGE->user_is_editing()) {
  7578. $caption = get_string('blockseditoff');
  7579. $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
  7580. } else {
  7581. $caption = get_string('blocksediton');
  7582. $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
  7583. }
  7584. $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
  7585. }
  7586. $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
  7587. $PAGE->set_heading($SITE->fullname);
  7588. // prevent caching in nav block
  7589. $PAGE->navigation->clear_cache();
  7590. }
  7591. /**
  7592. * Returns the reference to admin tree root
  7593. *
  7594. * @return object admin_root object
  7595. */
  7596. function admin_get_root($reload=false, $requirefulltree=true) {
  7597. global $CFG, $DB, $OUTPUT, $ADMIN;
  7598. if (is_null($ADMIN)) {
  7599. // create the admin tree!
  7600. $ADMIN = new admin_root($requirefulltree);
  7601. }
  7602. if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
  7603. $ADMIN->purge_children($requirefulltree);
  7604. }
  7605. if (!$ADMIN->loaded) {
  7606. // we process this file first to create categories first and in correct order
  7607. require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
  7608. // now we process all other files in admin/settings to build the admin tree
  7609. foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
  7610. if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
  7611. continue;
  7612. }
  7613. if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
  7614. // plugins are loaded last - they may insert pages anywhere
  7615. continue;
  7616. }
  7617. require($file);
  7618. }
  7619. require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
  7620. $ADMIN->loaded = true;
  7621. }
  7622. return $ADMIN;
  7623. }
  7624. /// settings utility functions
  7625. /**
  7626. * This function applies default settings.
  7627. * Because setting the defaults of some settings can enable other settings,
  7628. * this function is called recursively until no more new settings are found.
  7629. *
  7630. * @param object $node, NULL means complete tree, null by default
  7631. * @param bool $unconditional if true overrides all values with defaults, true by default
  7632. * @param array $admindefaultsettings default admin settings to apply. Used recursively
  7633. * @param array $settingsoutput The names and values of the changed settings. Used recursively
  7634. * @return array $settingsoutput The names and values of the changed settings
  7635. */
  7636. function admin_apply_default_settings($node=null, $unconditional=true, $admindefaultsettings=array(), $settingsoutput=array()) {
  7637. $counter = 0;
  7638. if (is_null($node)) {
  7639. core_plugin_manager::reset_caches();
  7640. $node = admin_get_root(true, true);
  7641. $counter = count($settingsoutput);
  7642. }
  7643. if ($node instanceof admin_category) {
  7644. $entries = array_keys($node->children);
  7645. foreach ($entries as $entry) {
  7646. $settingsoutput = admin_apply_default_settings(
  7647. $node->children[$entry], $unconditional, $admindefaultsettings, $settingsoutput
  7648. );
  7649. }
  7650. } else if ($node instanceof admin_settingpage) {
  7651. foreach ($node->settings as $setting) {
  7652. if (!$unconditional && !is_null($setting->get_setting())) {
  7653. // Do not override existing defaults.
  7654. continue;
  7655. }
  7656. $defaultsetting = $setting->get_defaultsetting();
  7657. if (is_null($defaultsetting)) {
  7658. // No value yet - default maybe applied after admin user creation or in upgradesettings.
  7659. continue;
  7660. }
  7661. $settingname = $node->name . '_' . $setting->name; // Get a unique name for the setting.
  7662. if (!array_key_exists($settingname, $admindefaultsettings)) { // Only update a setting if not already processed.
  7663. $admindefaultsettings[$settingname] = $settingname;
  7664. $settingsoutput[$settingname] = $defaultsetting;
  7665. // Set the default for this setting.
  7666. $setting->write_setting($defaultsetting);
  7667. $setting->write_setting_flags(null);
  7668. } else {
  7669. unset($admindefaultsettings[$settingname]); // Remove processed settings.
  7670. }
  7671. }
  7672. }
  7673. // Call this function recursively until all settings are processed.
  7674. if (($node instanceof admin_root) && ($counter != count($settingsoutput))) {
  7675. $settingsoutput = admin_apply_default_settings(null, $unconditional, $admindefaultsettings, $settingsoutput);
  7676. }
  7677. // Just in case somebody modifies the list of active plugins directly.
  7678. core_plugin_manager::reset_caches();
  7679. return $settingsoutput;
  7680. }
  7681. /**
  7682. * Store changed settings, this function updates the errors variable in $ADMIN
  7683. *
  7684. * @param object $formdata from form
  7685. * @return int number of changed settings
  7686. */
  7687. function admin_write_settings($formdata) {
  7688. global $CFG, $SITE, $DB;
  7689. $olddbsessions = !empty($CFG->dbsessions);
  7690. $formdata = (array)$formdata;
  7691. $data = array();
  7692. foreach ($formdata as $fullname=>$value) {
  7693. if (strpos($fullname, 's_') !== 0) {
  7694. continue; // not a config value
  7695. }
  7696. $data[$fullname] = $value;
  7697. }
  7698. $adminroot = admin_get_root();
  7699. $settings = admin_find_write_settings($adminroot, $data);
  7700. $count = 0;
  7701. foreach ($settings as $fullname=>$setting) {
  7702. /** @var $setting admin_setting */
  7703. $original = $setting->get_setting();
  7704. $error = $setting->write_setting($data[$fullname]);
  7705. if ($error !== '') {
  7706. $adminroot->errors[$fullname] = new stdClass();
  7707. $adminroot->errors[$fullname]->data = $data[$fullname];
  7708. $adminroot->errors[$fullname]->id = $setting->get_id();
  7709. $adminroot->errors[$fullname]->error = $error;
  7710. } else {
  7711. $setting->write_setting_flags($data);
  7712. }
  7713. if ($setting->post_write_settings($original)) {
  7714. $count++;
  7715. }
  7716. }
  7717. if ($olddbsessions != !empty($CFG->dbsessions)) {
  7718. require_logout();
  7719. }
  7720. // Now update $SITE - just update the fields, in case other people have a
  7721. // a reference to it (e.g. $PAGE, $COURSE).
  7722. $newsite = $DB->get_record('course', array('id'=>$SITE->id));
  7723. foreach (get_object_vars($newsite) as $field => $value) {
  7724. $SITE->$field = $value;
  7725. }
  7726. // now reload all settings - some of them might depend on the changed
  7727. admin_get_root(true);
  7728. return $count;
  7729. }
  7730. /**
  7731. * Internal recursive function - finds all settings from submitted form
  7732. *
  7733. * @param object $node Instance of admin_category, or admin_settingpage
  7734. * @param array $data
  7735. * @return array
  7736. */
  7737. function admin_find_write_settings($node, $data) {
  7738. $return = array();
  7739. if (empty($data)) {
  7740. return $return;
  7741. }
  7742. if ($node instanceof admin_category) {
  7743. if ($node->check_access()) {
  7744. $entries = array_keys($node->children);
  7745. foreach ($entries as $entry) {
  7746. $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
  7747. }
  7748. }
  7749. } else if ($node instanceof admin_settingpage) {
  7750. if ($node->check_access()) {
  7751. foreach ($node->settings as $setting) {
  7752. $fullname = $setting->get_full_name();
  7753. if (array_key_exists($fullname, $data)) {
  7754. $return[$fullname] = $setting;
  7755. }
  7756. }
  7757. }
  7758. }
  7759. return $return;
  7760. }
  7761. /**
  7762. * Internal function - prints the search results
  7763. *
  7764. * @param string $query String to search for
  7765. * @return string empty or XHTML
  7766. */
  7767. function admin_search_settings_html($query) {
  7768. global $CFG, $OUTPUT, $PAGE;
  7769. if (core_text::strlen($query) < 2) {
  7770. return '';
  7771. }
  7772. $query = core_text::strtolower($query);
  7773. $adminroot = admin_get_root();
  7774. $findings = $adminroot->search($query);
  7775. $savebutton = false;
  7776. $tpldata = (object) [
  7777. 'actionurl' => $PAGE->url->out(false),
  7778. 'results' => [],
  7779. 'sesskey' => sesskey(),
  7780. ];
  7781. foreach ($findings as $found) {
  7782. $page = $found->page;
  7783. $settings = $found->settings;
  7784. if ($page->is_hidden()) {
  7785. // hidden pages are not displayed in search results
  7786. continue;
  7787. }
  7788. $heading = highlight($query, $page->visiblename);
  7789. $headingurl = null;
  7790. if ($page instanceof admin_externalpage) {
  7791. $headingurl = new moodle_url($page->url);
  7792. } else if ($page instanceof admin_settingpage) {
  7793. $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
  7794. } else {
  7795. continue;
  7796. }
  7797. // Locate the page in the admin root and populate its visiblepath attribute.
  7798. $path = array();
  7799. $located = $adminroot->locate($page->name, true);
  7800. if ($located) {
  7801. foreach ($located->visiblepath as $pathitem) {
  7802. array_unshift($path, (string) $pathitem);
  7803. }
  7804. }
  7805. $sectionsettings = [];
  7806. if (!empty($settings)) {
  7807. foreach ($settings as $setting) {
  7808. if (empty($setting->nosave)) {
  7809. $savebutton = true;
  7810. }
  7811. $fullname = $setting->get_full_name();
  7812. if (array_key_exists($fullname, $adminroot->errors)) {
  7813. $data = $adminroot->errors[$fullname]->data;
  7814. } else {
  7815. $data = $setting->get_setting();
  7816. // do not use defaults if settings not available - upgradesettings handles the defaults!
  7817. }
  7818. $sectionsettings[] = $setting->output_html($data, $query);
  7819. }
  7820. }
  7821. $tpldata->results[] = (object) [
  7822. 'title' => $heading,
  7823. 'path' => $path,
  7824. 'url' => $headingurl->out(false),
  7825. 'settings' => $sectionsettings
  7826. ];
  7827. }
  7828. $tpldata->showsave = $savebutton;
  7829. $tpldata->hasresults = !empty($tpldata->results);
  7830. return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
  7831. }
  7832. /**
  7833. * Internal function - returns arrays of html pages with uninitialised settings
  7834. *
  7835. * @param object $node Instance of admin_category or admin_settingpage
  7836. * @return array
  7837. */
  7838. function admin_output_new_settings_by_page($node) {
  7839. global $OUTPUT;
  7840. $return = array();
  7841. if ($node instanceof admin_category) {
  7842. $entries = array_keys($node->children);
  7843. foreach ($entries as $entry) {
  7844. $return += admin_output_new_settings_by_page($node->children[$entry]);
  7845. }
  7846. } else if ($node instanceof admin_settingpage) {
  7847. $newsettings = array();
  7848. foreach ($node->settings as $setting) {
  7849. if (is_null($setting->get_setting())) {
  7850. $newsettings[] = $setting;
  7851. }
  7852. }
  7853. if (count($newsettings) > 0) {
  7854. $adminroot = admin_get_root();
  7855. $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
  7856. $page .= '<fieldset class="adminsettings">'."\n";
  7857. foreach ($newsettings as $setting) {
  7858. $fullname = $setting->get_full_name();
  7859. if (array_key_exists($fullname, $adminroot->errors)) {
  7860. $data = $adminroot->errors[$fullname]->data;
  7861. } else {
  7862. $data = $setting->get_setting();
  7863. if (is_null($data)) {
  7864. $data = $setting->get_defaultsetting();
  7865. }
  7866. }
  7867. $page .= '<div class="clearer"><!-- --></div>'."\n";
  7868. $page .= $setting->output_html($data);
  7869. }
  7870. $page .= '</fieldset>';
  7871. $return[$node->name] = $page;
  7872. }
  7873. }
  7874. return $return;
  7875. }
  7876. /**
  7877. * Format admin settings
  7878. *
  7879. * @param object $setting
  7880. * @param string $title label element
  7881. * @param string $form form fragment, html code - not highlighted automatically
  7882. * @param string $description
  7883. * @param mixed $label link label to id, true by default or string being the label to connect it to
  7884. * @param string $warning warning text
  7885. * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
  7886. * @param string $query search query to be highlighted
  7887. * @return string XHTML
  7888. */
  7889. function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
  7890. global $CFG, $OUTPUT;
  7891. $context = (object) [
  7892. 'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
  7893. 'fullname' => $setting->get_full_name(),
  7894. ];
  7895. // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
  7896. if ($label === true) {
  7897. $context->labelfor = $setting->get_id();
  7898. } else if ($label === false) {
  7899. $context->labelfor = '';
  7900. } else {
  7901. $context->labelfor = $label;
  7902. }
  7903. $form .= $setting->output_setting_flags();
  7904. $context->warning = $warning;
  7905. $context->override = '';
  7906. if (empty($setting->plugin)) {
  7907. if (array_key_exists($setting->name, $CFG->config_php_settings)) {
  7908. $context->override = get_string('configoverride', 'admin');
  7909. }
  7910. } else {
  7911. if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
  7912. $context->override = get_string('configoverride', 'admin');
  7913. }
  7914. }
  7915. $defaults = array();
  7916. if (!is_null($defaultinfo)) {
  7917. if ($defaultinfo === '') {
  7918. $defaultinfo = get_string('emptysettingvalue', 'admin');
  7919. }
  7920. $defaults[] = $defaultinfo;
  7921. }
  7922. $context->default = null;
  7923. $setting->get_setting_flag_defaults($defaults);
  7924. if (!empty($defaults)) {
  7925. $defaultinfo = implode(', ', $defaults);
  7926. $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
  7927. $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
  7928. }
  7929. $context->error = '';
  7930. $adminroot = admin_get_root();
  7931. if (array_key_exists($context->fullname, $adminroot->errors)) {
  7932. $context->error = $adminroot->errors[$context->fullname]->error;
  7933. }
  7934. if ($dependenton = $setting->get_dependent_on()) {
  7935. $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
  7936. }
  7937. $context->id = 'admin-' . $setting->name;
  7938. $context->title = highlightfast($query, $title);
  7939. $context->name = highlightfast($query, $context->name);
  7940. $context->description = highlight($query, markdown_to_html($description));
  7941. $context->element = $form;
  7942. $context->forceltr = $setting->get_force_ltr();
  7943. return $OUTPUT->render_from_template('core_admin/setting', $context);
  7944. }
  7945. /**
  7946. * Based on find_new_settings{@link ()} in upgradesettings.php
  7947. * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
  7948. *
  7949. * @param object $node Instance of admin_category, or admin_settingpage
  7950. * @return boolean true if any settings haven't been initialised, false if they all have
  7951. */
  7952. function any_new_admin_settings($node) {
  7953. if ($node instanceof admin_category) {
  7954. $entries = array_keys($node->children);
  7955. foreach ($entries as $entry) {
  7956. if (any_new_admin_settings($node->children[$entry])) {
  7957. return true;
  7958. }
  7959. }
  7960. } else if ($node instanceof admin_settingpage) {
  7961. foreach ($node->settings as $setting) {
  7962. if ($setting->get_setting() === NULL) {
  7963. return true;
  7964. }
  7965. }
  7966. }
  7967. return false;
  7968. }
  7969. /**
  7970. * Moved from admin/replace.php so that we can use this in cron
  7971. *
  7972. * @param string $search string to look for
  7973. * @param string $replace string to replace
  7974. * @return bool success or fail
  7975. */
  7976. function db_replace($search, $replace) {
  7977. global $DB, $CFG, $OUTPUT;
  7978. // TODO: this is horrible hack, we should do whitelisting and each plugin should be responsible for proper replacing...
  7979. $skiptables = array('config', 'config_plugins', 'config_log', 'upgrade_log', 'log',
  7980. 'filter_config', 'sessions', 'events_queue', 'repository_instance_config',
  7981. 'block_instances', '');
  7982. // Turn off time limits, sometimes upgrades can be slow.
  7983. core_php_time_limit::raise();
  7984. if (!$tables = $DB->get_tables() ) { // No tables yet at all.
  7985. return false;
  7986. }
  7987. foreach ($tables as $table) {
  7988. if (in_array($table, $skiptables)) { // Don't process these
  7989. continue;
  7990. }
  7991. if ($columns = $DB->get_columns($table)) {
  7992. $DB->set_debug(true);
  7993. foreach ($columns as $column) {
  7994. $DB->replace_all_text($table, $column, $search, $replace);
  7995. }
  7996. $DB->set_debug(false);
  7997. }
  7998. }
  7999. // delete modinfo caches
  8000. rebuild_course_cache(0, true);
  8001. // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
  8002. $blocks = core_component::get_plugin_list('block');
  8003. foreach ($blocks as $blockname=>$fullblock) {
  8004. if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
  8005. continue;
  8006. }
  8007. if (!is_readable($fullblock.'/lib.php')) {
  8008. continue;
  8009. }
  8010. $function = 'block_'.$blockname.'_global_db_replace';
  8011. include_once($fullblock.'/lib.php');
  8012. if (!function_exists($function)) {
  8013. continue;
  8014. }
  8015. echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
  8016. $function($search, $replace);
  8017. echo $OUTPUT->notification("...finished", 'notifysuccess');
  8018. }
  8019. // Trigger an event.
  8020. $eventargs = [
  8021. 'context' => context_system::instance(),
  8022. 'other' => [
  8023. 'search' => $search,
  8024. 'replace' => $replace
  8025. ]
  8026. ];
  8027. $event = \core\event\database_text_field_content_replaced::create($eventargs);
  8028. $event->trigger();
  8029. purge_all_caches();
  8030. return true;
  8031. }
  8032. /**
  8033. * Manage repository settings
  8034. *
  8035. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  8036. */
  8037. class admin_setting_managerepository extends admin_setting {
  8038. /** @var string */
  8039. private $baseurl;
  8040. /**
  8041. * calls parent::__construct with specific arguments
  8042. */
  8043. public function __construct() {
  8044. global $CFG;
  8045. parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
  8046. $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
  8047. }
  8048. /**
  8049. * Always returns true, does nothing
  8050. *
  8051. * @return true
  8052. */
  8053. public function get_setting() {
  8054. return true;
  8055. }
  8056. /**
  8057. * Always returns true does nothing
  8058. *
  8059. * @return true
  8060. */
  8061. public function get_defaultsetting() {
  8062. return true;
  8063. }
  8064. /**
  8065. * Always returns s_managerepository
  8066. *
  8067. * @return string Always return 's_managerepository'
  8068. */
  8069. public function get_full_name() {
  8070. return 's_managerepository';
  8071. }
  8072. /**
  8073. * Always returns '' doesn't do anything
  8074. */
  8075. public function write_setting($data) {
  8076. $url = $this->baseurl . '&amp;new=' . $data;
  8077. return '';
  8078. // TODO
  8079. // Should not use redirect and exit here
  8080. // Find a better way to do this.
  8081. // redirect($url);
  8082. // exit;
  8083. }
  8084. /**
  8085. * Searches repository plugins for one that matches $query
  8086. *
  8087. * @param string $query The string to search for
  8088. * @return bool true if found, false if not
  8089. */
  8090. public function is_related($query) {
  8091. if (parent::is_related($query)) {
  8092. return true;
  8093. }
  8094. $repositories= core_component::get_plugin_list('repository');
  8095. foreach ($repositories as $p => $dir) {
  8096. if (strpos($p, $query) !== false) {
  8097. return true;
  8098. }
  8099. }
  8100. foreach (repository::get_types() as $instance) {
  8101. $title = $instance->get_typename();
  8102. if (strpos(core_text::strtolower($title), $query) !== false) {
  8103. return true;
  8104. }
  8105. }
  8106. return false;
  8107. }
  8108. /**
  8109. * Helper function that generates a moodle_url object
  8110. * relevant to the repository
  8111. */
  8112. function repository_action_url($repository) {
  8113. return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
  8114. }
  8115. /**
  8116. * Builds XHTML to display the control
  8117. *
  8118. * @param string $data Unused
  8119. * @param string $query
  8120. * @return string XHTML
  8121. */
  8122. public function output_html($data, $query='') {
  8123. global $CFG, $USER, $OUTPUT;
  8124. // Get strings that are used
  8125. $strshow = get_string('on', 'repository');
  8126. $strhide = get_string('off', 'repository');
  8127. $strdelete = get_string('disabled', 'repository');
  8128. $actionchoicesforexisting = array(
  8129. 'show' => $strshow,
  8130. 'hide' => $strhide,
  8131. 'delete' => $strdelete
  8132. );
  8133. $actionchoicesfornew = array(
  8134. 'newon' => $strshow,
  8135. 'newoff' => $strhide,
  8136. 'delete' => $strdelete
  8137. );
  8138. $return = '';
  8139. $return .= $OUTPUT->box_start('generalbox');
  8140. // Set strings that are used multiple times
  8141. $settingsstr = get_string('settings');
  8142. $disablestr = get_string('disable');
  8143. // Table to list plug-ins
  8144. $table = new html_table();
  8145. $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
  8146. $table->align = array('left', 'center', 'center', 'center', 'center');
  8147. $table->data = array();
  8148. // Get list of used plug-ins
  8149. $repositorytypes = repository::get_types();
  8150. if (!empty($repositorytypes)) {
  8151. // Array to store plugins being used
  8152. $alreadyplugins = array();
  8153. $totalrepositorytypes = count($repositorytypes);
  8154. $updowncount = 1;
  8155. foreach ($repositorytypes as $i) {
  8156. $settings = '';
  8157. $typename = $i->get_typename();
  8158. // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
  8159. $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
  8160. $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
  8161. if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
  8162. // Calculate number of instances in order to display them for the Moodle administrator
  8163. if (!empty($instanceoptionnames)) {
  8164. $params = array();
  8165. $params['context'] = array(context_system::instance());
  8166. $params['onlyvisible'] = false;
  8167. $params['type'] = $typename;
  8168. $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
  8169. // site instances
  8170. $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
  8171. $params['context'] = array();
  8172. $instances = repository::static_function($typename, 'get_instances', $params);
  8173. $courseinstances = array();
  8174. $userinstances = array();
  8175. foreach ($instances as $instance) {
  8176. $repocontext = context::instance_by_id($instance->instance->contextid);
  8177. if ($repocontext->contextlevel == CONTEXT_COURSE) {
  8178. $courseinstances[] = $instance;
  8179. } else if ($repocontext->contextlevel == CONTEXT_USER) {
  8180. $userinstances[] = $instance;
  8181. }
  8182. }
  8183. // course instances
  8184. $instancenumber = count($courseinstances);
  8185. $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
  8186. // user private instances
  8187. $instancenumber = count($userinstances);
  8188. $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
  8189. } else {
  8190. $admininstancenumbertext = "";
  8191. $courseinstancenumbertext = "";
  8192. $userinstancenumbertext = "";
  8193. }
  8194. $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
  8195. $settings .= $OUTPUT->container_start('mdl-left');
  8196. $settings .= '<br/>';
  8197. $settings .= $admininstancenumbertext;
  8198. $settings .= '<br/>';
  8199. $settings .= $courseinstancenumbertext;
  8200. $settings .= '<br/>';
  8201. $settings .= $userinstancenumbertext;
  8202. $settings .= $OUTPUT->container_end();
  8203. }
  8204. // Get the current visibility
  8205. if ($i->get_visible()) {
  8206. $currentaction = 'show';
  8207. } else {
  8208. $currentaction = 'hide';
  8209. }
  8210. $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
  8211. // Display up/down link
  8212. $updown = '';
  8213. // Should be done with CSS instead.
  8214. $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
  8215. if ($updowncount > 1) {
  8216. $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
  8217. $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
  8218. }
  8219. else {
  8220. $updown .= $spacer;
  8221. }
  8222. if ($updowncount < $totalrepositorytypes) {
  8223. $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
  8224. $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
  8225. }
  8226. else {
  8227. $updown .= $spacer;
  8228. }
  8229. $updowncount++;
  8230. $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
  8231. if (!in_array($typename, $alreadyplugins)) {
  8232. $alreadyplugins[] = $typename;
  8233. }
  8234. }
  8235. }
  8236. // Get all the plugins that exist on disk
  8237. $plugins = core_component::get_plugin_list('repository');
  8238. if (!empty($plugins)) {
  8239. foreach ($plugins as $plugin => $dir) {
  8240. // Check that it has not already been listed
  8241. if (!in_array($plugin, $alreadyplugins)) {
  8242. $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
  8243. $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
  8244. }
  8245. }
  8246. }
  8247. $return .= html_writer::table($table);
  8248. $return .= $OUTPUT->box_end();
  8249. return highlight($query, $return);
  8250. }
  8251. }
  8252. /**
  8253. * Special checkbox for enable mobile web service
  8254. * If enable then we store the service id of the mobile service into config table
  8255. * If disable then we unstore the service id from the config table
  8256. */
  8257. class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
  8258. /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
  8259. private $restuse;
  8260. /**
  8261. * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
  8262. *
  8263. * @return boolean
  8264. */
  8265. private function is_protocol_cap_allowed() {
  8266. global $DB, $CFG;
  8267. // If the $this->restuse variable is not set, it needs to be set.
  8268. if (empty($this->restuse) and $this->restuse!==false) {
  8269. $params = array();
  8270. $params['permission'] = CAP_ALLOW;
  8271. $params['roleid'] = $CFG->defaultuserroleid;
  8272. $params['capability'] = 'webservice/rest:use';
  8273. $this->restuse = $DB->record_exists('role_capabilities', $params);
  8274. }
  8275. return $this->restuse;
  8276. }
  8277. /**
  8278. * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
  8279. * @param type $status true to allow, false to not set
  8280. */
  8281. private function set_protocol_cap($status) {
  8282. global $CFG;
  8283. if ($status and !$this->is_protocol_cap_allowed()) {
  8284. //need to allow the cap
  8285. $permission = CAP_ALLOW;
  8286. $assign = true;
  8287. } else if (!$status and $this->is_protocol_cap_allowed()){
  8288. //need to disallow the cap
  8289. $permission = CAP_INHERIT;
  8290. $assign = true;
  8291. }
  8292. if (!empty($assign)) {
  8293. $systemcontext = context_system::instance();
  8294. assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
  8295. }
  8296. }
  8297. /**
  8298. * Builds XHTML to display the control.
  8299. * The main purpose of this overloading is to display a warning when https
  8300. * is not supported by the server
  8301. * @param string $data Unused
  8302. * @param string $query
  8303. * @return string XHTML
  8304. */
  8305. public function output_html($data, $query='') {
  8306. global $OUTPUT;
  8307. $html = parent::output_html($data, $query);
  8308. if ((string)$data === $this->yes) {
  8309. $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
  8310. foreach ($notifications as $notification) {
  8311. $message = get_string($notification[0], $notification[1]);
  8312. $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
  8313. }
  8314. }
  8315. return $html;
  8316. }
  8317. /**
  8318. * Retrieves the current setting using the objects name
  8319. *
  8320. * @return string
  8321. */
  8322. public function get_setting() {
  8323. global $CFG;
  8324. // First check if is not set.
  8325. $result = $this->config_read($this->name);
  8326. if (is_null($result)) {
  8327. return null;
  8328. }
  8329. // For install cli script, $CFG->defaultuserroleid is not set so return 0
  8330. // Or if web services aren't enabled this can't be,
  8331. if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
  8332. return 0;
  8333. }
  8334. require_once($CFG->dirroot . '/webservice/lib.php');
  8335. $webservicemanager = new webservice();
  8336. $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
  8337. if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
  8338. return $result;
  8339. } else {
  8340. return 0;
  8341. }
  8342. }
  8343. /**
  8344. * Save the selected setting
  8345. *
  8346. * @param string $data The selected site
  8347. * @return string empty string or error message
  8348. */
  8349. public function write_setting($data) {
  8350. global $DB, $CFG;
  8351. //for install cli script, $CFG->defaultuserroleid is not set so do nothing
  8352. if (empty($CFG->defaultuserroleid)) {
  8353. return '';
  8354. }
  8355. $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
  8356. require_once($CFG->dirroot . '/webservice/lib.php');
  8357. $webservicemanager = new webservice();
  8358. $updateprotocol = false;
  8359. if ((string)$data === $this->yes) {
  8360. //code run when enable mobile web service
  8361. //enable web service systeme if necessary
  8362. set_config('enablewebservices', true);
  8363. //enable mobile service
  8364. $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
  8365. $mobileservice->enabled = 1;
  8366. $webservicemanager->update_external_service($mobileservice);
  8367. // Enable REST server.
  8368. $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
  8369. if (!in_array('rest', $activeprotocols)) {
  8370. $activeprotocols[] = 'rest';
  8371. $updateprotocol = true;
  8372. }
  8373. if ($updateprotocol) {
  8374. set_config('webserviceprotocols', implode(',', $activeprotocols));
  8375. }
  8376. // Allow rest:use capability for authenticated user.
  8377. $this->set_protocol_cap(true);
  8378. } else {
  8379. //disable web service system if no other services are enabled
  8380. $otherenabledservices = $DB->get_records_select('external_services',
  8381. 'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
  8382. 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
  8383. if (empty($otherenabledservices)) {
  8384. set_config('enablewebservices', false);
  8385. // Also disable REST server.
  8386. $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
  8387. $protocolkey = array_search('rest', $activeprotocols);
  8388. if ($protocolkey !== false) {
  8389. unset($activeprotocols[$protocolkey]);
  8390. $updateprotocol = true;
  8391. }
  8392. if ($updateprotocol) {
  8393. set_config('webserviceprotocols', implode(',', $activeprotocols));
  8394. }
  8395. // Disallow rest:use capability for authenticated user.
  8396. $this->set_protocol_cap(false);
  8397. }
  8398. //disable the mobile service
  8399. $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
  8400. $mobileservice->enabled = 0;
  8401. $webservicemanager->update_external_service($mobileservice);
  8402. }
  8403. return (parent::write_setting($data));
  8404. }
  8405. }
  8406. /**
  8407. * Special class for management of external services
  8408. *
  8409. * @author Petr Skoda (skodak)
  8410. */
  8411. class admin_setting_manageexternalservices extends admin_setting {
  8412. /**
  8413. * Calls parent::__construct with specific arguments
  8414. */
  8415. public function __construct() {
  8416. $this->nosave = true;
  8417. parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
  8418. }
  8419. /**
  8420. * Always returns true, does nothing
  8421. *
  8422. * @return true
  8423. */
  8424. public function get_setting() {
  8425. return true;
  8426. }
  8427. /**
  8428. * Always returns true, does nothing
  8429. *
  8430. * @return true
  8431. */
  8432. public function get_defaultsetting() {
  8433. return true;
  8434. }
  8435. /**
  8436. * Always returns '', does not write anything
  8437. *
  8438. * @return string Always returns ''
  8439. */
  8440. public function write_setting($data) {
  8441. // do not write any setting
  8442. return '';
  8443. }
  8444. /**
  8445. * Checks if $query is one of the available external services
  8446. *
  8447. * @param string $query The string to search for
  8448. * @return bool Returns true if found, false if not
  8449. */
  8450. public function is_related($query) {
  8451. global $DB;
  8452. if (parent::is_related($query)) {
  8453. return true;
  8454. }
  8455. $services = $DB->get_records('external_services', array(), 'id, name');
  8456. foreach ($services as $service) {
  8457. if (strpos(core_text::strtolower($service->name), $query) !== false) {
  8458. return true;
  8459. }
  8460. }
  8461. return false;
  8462. }
  8463. /**
  8464. * Builds the XHTML to display the control
  8465. *
  8466. * @param string $data Unused
  8467. * @param string $query
  8468. * @return string
  8469. */
  8470. public function output_html($data, $query='') {
  8471. global $CFG, $OUTPUT, $DB;
  8472. // display strings
  8473. $stradministration = get_string('administration');
  8474. $stredit = get_string('edit');
  8475. $strservice = get_string('externalservice', 'webservice');
  8476. $strdelete = get_string('delete');
  8477. $strplugin = get_string('plugin', 'admin');
  8478. $stradd = get_string('add');
  8479. $strfunctions = get_string('functions', 'webservice');
  8480. $strusers = get_string('users');
  8481. $strserviceusers = get_string('serviceusers', 'webservice');
  8482. $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
  8483. $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
  8484. $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
  8485. // built in services
  8486. $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
  8487. $return = "";
  8488. if (!empty($services)) {
  8489. $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
  8490. $table = new html_table();
  8491. $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
  8492. $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
  8493. $table->id = 'builtinservices';
  8494. $table->attributes['class'] = 'admintable externalservices generaltable';
  8495. $table->data = array();
  8496. // iterate through auth plugins and add to the display table
  8497. foreach ($services as $service) {
  8498. $name = $service->name;
  8499. // hide/show link
  8500. if ($service->enabled) {
  8501. $displayname = "<span>$name</span>";
  8502. } else {
  8503. $displayname = "<span class=\"dimmed_text\">$name</span>";
  8504. }
  8505. $plugin = $service->component;
  8506. $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
  8507. if ($service->restrictedusers) {
  8508. $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
  8509. } else {
  8510. $users = get_string('allusers', 'webservice');
  8511. }
  8512. $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
  8513. // add a row to the table
  8514. $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
  8515. }
  8516. $return .= html_writer::table($table);
  8517. }
  8518. // Custom services
  8519. $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
  8520. $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
  8521. $table = new html_table();
  8522. $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
  8523. $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
  8524. $table->id = 'customservices';
  8525. $table->attributes['class'] = 'admintable externalservices generaltable';
  8526. $table->data = array();
  8527. // iterate through auth plugins and add to the display table
  8528. foreach ($services as $service) {
  8529. $name = $service->name;
  8530. // hide/show link
  8531. if ($service->enabled) {
  8532. $displayname = "<span>$name</span>";
  8533. } else {
  8534. $displayname = "<span class=\"dimmed_text\">$name</span>";
  8535. }
  8536. // delete link
  8537. $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
  8538. $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
  8539. if ($service->restrictedusers) {
  8540. $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
  8541. } else {
  8542. $users = get_string('allusers', 'webservice');
  8543. }
  8544. $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
  8545. // add a row to the table
  8546. $table->data[] = array($displayname, $delete, $functions, $users, $edit);
  8547. }
  8548. // add new custom service option
  8549. $return .= html_writer::table($table);
  8550. $return .= '<br />';
  8551. // add a token to the table
  8552. $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
  8553. return highlight($query, $return);
  8554. }
  8555. }
  8556. /**
  8557. * Special class for overview of external services
  8558. *
  8559. * @author Jerome Mouneyrac
  8560. */
  8561. class admin_setting_webservicesoverview extends admin_setting {
  8562. /**
  8563. * Calls parent::__construct with specific arguments
  8564. */
  8565. public function __construct() {
  8566. $this->nosave = true;
  8567. parent::__construct('webservicesoverviewui',
  8568. get_string('webservicesoverview', 'webservice'), '', '');
  8569. }
  8570. /**
  8571. * Always returns true, does nothing
  8572. *
  8573. * @return true
  8574. */
  8575. public function get_setting() {
  8576. return true;
  8577. }
  8578. /**
  8579. * Always returns true, does nothing
  8580. *
  8581. * @return true
  8582. */
  8583. public function get_defaultsetting() {
  8584. return true;
  8585. }
  8586. /**
  8587. * Always returns '', does not write anything
  8588. *
  8589. * @return string Always returns ''
  8590. */
  8591. public function write_setting($data) {
  8592. // do not write any setting
  8593. return '';
  8594. }
  8595. /**
  8596. * Builds the XHTML to display the control
  8597. *
  8598. * @param string $data Unused
  8599. * @param string $query
  8600. * @return string
  8601. */
  8602. public function output_html($data, $query='') {
  8603. global $CFG, $OUTPUT;
  8604. $return = "";
  8605. $brtag = html_writer::empty_tag('br');
  8606. /// One system controlling Moodle with Token
  8607. $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
  8608. $table = new html_table();
  8609. $table->head = array(get_string('step', 'webservice'), get_string('status'),
  8610. get_string('description'));
  8611. $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
  8612. $table->id = 'onesystemcontrol';
  8613. $table->attributes['class'] = 'admintable wsoverview generaltable';
  8614. $table->data = array();
  8615. $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
  8616. . $brtag . $brtag;
  8617. /// 1. Enable Web Services
  8618. $row = array();
  8619. $url = new moodle_url("/admin/search.php?query=enablewebservices");
  8620. $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
  8621. array('href' => $url));
  8622. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  8623. if ($CFG->enablewebservices) {
  8624. $status = get_string('yes');
  8625. }
  8626. $row[1] = $status;
  8627. $row[2] = get_string('enablewsdescription', 'webservice');
  8628. $table->data[] = $row;
  8629. /// 2. Enable protocols
  8630. $row = array();
  8631. $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
  8632. $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
  8633. array('href' => $url));
  8634. $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
  8635. //retrieve activated protocol
  8636. $active_protocols = empty($CFG->webserviceprotocols) ?
  8637. array() : explode(',', $CFG->webserviceprotocols);
  8638. if (!empty($active_protocols)) {
  8639. $status = "";
  8640. foreach ($active_protocols as $protocol) {
  8641. $status .= $protocol . $brtag;
  8642. }
  8643. }
  8644. $row[1] = $status;
  8645. $row[2] = get_string('enableprotocolsdescription', 'webservice');
  8646. $table->data[] = $row;
  8647. /// 3. Create user account
  8648. $row = array();
  8649. $url = new moodle_url("/user/editadvanced.php?id=-1");
  8650. $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
  8651. array('href' => $url));
  8652. $row[1] = "";
  8653. $row[2] = get_string('createuserdescription', 'webservice');
  8654. $table->data[] = $row;
  8655. /// 4. Add capability to users
  8656. $row = array();
  8657. $url = new moodle_url("/admin/roles/check.php?contextid=1");
  8658. $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
  8659. array('href' => $url));
  8660. $row[1] = "";
  8661. $row[2] = get_string('checkusercapabilitydescription', 'webservice');
  8662. $table->data[] = $row;
  8663. /// 5. Select a web service
  8664. $row = array();
  8665. $url = new moodle_url("/admin/settings.php?section=externalservices");
  8666. $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
  8667. array('href' => $url));
  8668. $row[1] = "";
  8669. $row[2] = get_string('createservicedescription', 'webservice');
  8670. $table->data[] = $row;
  8671. /// 6. Add functions
  8672. $row = array();
  8673. $url = new moodle_url("/admin/settings.php?section=externalservices");
  8674. $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
  8675. array('href' => $url));
  8676. $row[1] = "";
  8677. $row[2] = get_string('addfunctionsdescription', 'webservice');
  8678. $table->data[] = $row;
  8679. /// 7. Add the specific user
  8680. $row = array();
  8681. $url = new moodle_url("/admin/settings.php?section=externalservices");
  8682. $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
  8683. array('href' => $url));
  8684. $row[1] = "";
  8685. $row[2] = get_string('selectspecificuserdescription', 'webservice');
  8686. $table->data[] = $row;
  8687. /// 8. Create token for the specific user
  8688. $row = array();
  8689. $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
  8690. $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
  8691. array('href' => $url));
  8692. $row[1] = "";
  8693. $row[2] = get_string('createtokenforuserdescription', 'webservice');
  8694. $table->data[] = $row;
  8695. /// 9. Enable the documentation
  8696. $row = array();
  8697. $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
  8698. $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
  8699. array('href' => $url));
  8700. $status = '<span class="warning">' . get_string('no') . '</span>';
  8701. if ($CFG->enablewsdocumentation) {
  8702. $status = get_string('yes');
  8703. }
  8704. $row[1] = $status;
  8705. $row[2] = get_string('enabledocumentationdescription', 'webservice');
  8706. $table->data[] = $row;
  8707. /// 10. Test the service
  8708. $row = array();
  8709. $url = new moodle_url("/admin/webservice/testclient.php");
  8710. $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
  8711. array('href' => $url));
  8712. $row[1] = "";
  8713. $row[2] = get_string('testwithtestclientdescription', 'webservice');
  8714. $table->data[] = $row;
  8715. $return .= html_writer::table($table);
  8716. /// Users as clients with token
  8717. $return .= $brtag . $brtag . $brtag;
  8718. $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
  8719. $table = new html_table();
  8720. $table->head = array(get_string('step', 'webservice'), get_string('status'),
  8721. get_string('description'));
  8722. $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
  8723. $table->id = 'userasclients';
  8724. $table->attributes['class'] = 'admintable wsoverview generaltable';
  8725. $table->data = array();
  8726. $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
  8727. $brtag . $brtag;
  8728. /// 1. Enable Web Services
  8729. $row = array();
  8730. $url = new moodle_url("/admin/search.php?query=enablewebservices");
  8731. $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
  8732. array('href' => $url));
  8733. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  8734. if ($CFG->enablewebservices) {
  8735. $status = get_string('yes');
  8736. }
  8737. $row[1] = $status;
  8738. $row[2] = get_string('enablewsdescription', 'webservice');
  8739. $table->data[] = $row;
  8740. /// 2. Enable protocols
  8741. $row = array();
  8742. $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
  8743. $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
  8744. array('href' => $url));
  8745. $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
  8746. //retrieve activated protocol
  8747. $active_protocols = empty($CFG->webserviceprotocols) ?
  8748. array() : explode(',', $CFG->webserviceprotocols);
  8749. if (!empty($active_protocols)) {
  8750. $status = "";
  8751. foreach ($active_protocols as $protocol) {
  8752. $status .= $protocol . $brtag;
  8753. }
  8754. }
  8755. $row[1] = $status;
  8756. $row[2] = get_string('enableprotocolsdescription', 'webservice');
  8757. $table->data[] = $row;
  8758. /// 3. Select a web service
  8759. $row = array();
  8760. $url = new moodle_url("/admin/settings.php?section=externalservices");
  8761. $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
  8762. array('href' => $url));
  8763. $row[1] = "";
  8764. $row[2] = get_string('createserviceforusersdescription', 'webservice');
  8765. $table->data[] = $row;
  8766. /// 4. Add functions
  8767. $row = array();
  8768. $url = new moodle_url("/admin/settings.php?section=externalservices");
  8769. $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
  8770. array('href' => $url));
  8771. $row[1] = "";
  8772. $row[2] = get_string('addfunctionsdescription', 'webservice');
  8773. $table->data[] = $row;
  8774. /// 5. Add capability to users
  8775. $row = array();
  8776. $url = new moodle_url("/admin/roles/check.php?contextid=1");
  8777. $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
  8778. array('href' => $url));
  8779. $row[1] = "";
  8780. $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
  8781. $table->data[] = $row;
  8782. /// 6. Test the service
  8783. $row = array();
  8784. $url = new moodle_url("/admin/webservice/testclient.php");
  8785. $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
  8786. array('href' => $url));
  8787. $row[1] = "";
  8788. $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
  8789. $table->data[] = $row;
  8790. $return .= html_writer::table($table);
  8791. return highlight($query, $return);
  8792. }
  8793. }
  8794. /**
  8795. * Special class for web service protocol administration.
  8796. *
  8797. * @author Petr Skoda (skodak)
  8798. */
  8799. class admin_setting_managewebserviceprotocols extends admin_setting {
  8800. /**
  8801. * Calls parent::__construct with specific arguments
  8802. */
  8803. public function __construct() {
  8804. $this->nosave = true;
  8805. parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
  8806. }
  8807. /**
  8808. * Always returns true, does nothing
  8809. *
  8810. * @return true
  8811. */
  8812. public function get_setting() {
  8813. return true;
  8814. }
  8815. /**
  8816. * Always returns true, does nothing
  8817. *
  8818. * @return true
  8819. */
  8820. public function get_defaultsetting() {
  8821. return true;
  8822. }
  8823. /**
  8824. * Always returns '', does not write anything
  8825. *
  8826. * @return string Always returns ''
  8827. */
  8828. public function write_setting($data) {
  8829. // do not write any setting
  8830. return '';
  8831. }
  8832. /**
  8833. * Checks if $query is one of the available webservices
  8834. *
  8835. * @param string $query The string to search for
  8836. * @return bool Returns true if found, false if not
  8837. */
  8838. public function is_related($query) {
  8839. if (parent::is_related($query)) {
  8840. return true;
  8841. }
  8842. $protocols = core_component::get_plugin_list('webservice');
  8843. foreach ($protocols as $protocol=>$location) {
  8844. if (strpos($protocol, $query) !== false) {
  8845. return true;
  8846. }
  8847. $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
  8848. if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
  8849. return true;
  8850. }
  8851. }
  8852. return false;
  8853. }
  8854. /**
  8855. * Builds the XHTML to display the control
  8856. *
  8857. * @param string $data Unused
  8858. * @param string $query
  8859. * @return string
  8860. */
  8861. public function output_html($data, $query='') {
  8862. global $CFG, $OUTPUT;
  8863. // display strings
  8864. $stradministration = get_string('administration');
  8865. $strsettings = get_string('settings');
  8866. $stredit = get_string('edit');
  8867. $strprotocol = get_string('protocol', 'webservice');
  8868. $strenable = get_string('enable');
  8869. $strdisable = get_string('disable');
  8870. $strversion = get_string('version');
  8871. $protocols_available = core_component::get_plugin_list('webservice');
  8872. $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
  8873. ksort($protocols_available);
  8874. foreach ($active_protocols as $key=>$protocol) {
  8875. if (empty($protocols_available[$protocol])) {
  8876. unset($active_protocols[$key]);
  8877. }
  8878. }
  8879. $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
  8880. $return .= $OUTPUT->box_start('generalbox webservicesui');
  8881. $table = new html_table();
  8882. $table->head = array($strprotocol, $strversion, $strenable, $strsettings);
  8883. $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
  8884. $table->id = 'webserviceprotocols';
  8885. $table->attributes['class'] = 'admintable generaltable';
  8886. $table->data = array();
  8887. // iterate through auth plugins and add to the display table
  8888. $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
  8889. foreach ($protocols_available as $protocol => $location) {
  8890. $name = get_string('pluginname', 'webservice_'.$protocol);
  8891. $plugin = new stdClass();
  8892. if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
  8893. include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
  8894. }
  8895. $version = isset($plugin->version) ? $plugin->version : '';
  8896. // hide/show link
  8897. if (in_array($protocol, $active_protocols)) {
  8898. $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
  8899. $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
  8900. $displayname = "<span>$name</span>";
  8901. } else {
  8902. $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
  8903. $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
  8904. $displayname = "<span class=\"dimmed_text\">$name</span>";
  8905. }
  8906. // settings link
  8907. if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
  8908. $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
  8909. } else {
  8910. $settings = '';
  8911. }
  8912. // add a row to the table
  8913. $table->data[] = array($displayname, $version, $hideshow, $settings);
  8914. }
  8915. $return .= html_writer::table($table);
  8916. $return .= get_string('configwebserviceplugins', 'webservice');
  8917. $return .= $OUTPUT->box_end();
  8918. return highlight($query, $return);
  8919. }
  8920. }
  8921. /**
  8922. * Special class for web service token administration.
  8923. *
  8924. * @author Jerome Mouneyrac
  8925. */
  8926. class admin_setting_managewebservicetokens extends admin_setting {
  8927. /**
  8928. * Calls parent::__construct with specific arguments
  8929. */
  8930. public function __construct() {
  8931. $this->nosave = true;
  8932. parent::__construct('webservicestokenui', get_string('managetokens', 'webservice'), '', '');
  8933. }
  8934. /**
  8935. * Always returns true, does nothing
  8936. *
  8937. * @return true
  8938. */
  8939. public function get_setting() {
  8940. return true;
  8941. }
  8942. /**
  8943. * Always returns true, does nothing
  8944. *
  8945. * @return true
  8946. */
  8947. public function get_defaultsetting() {
  8948. return true;
  8949. }
  8950. /**
  8951. * Always returns '', does not write anything
  8952. *
  8953. * @return string Always returns ''
  8954. */
  8955. public function write_setting($data) {
  8956. // do not write any setting
  8957. return '';
  8958. }
  8959. /**
  8960. * Builds the XHTML to display the control
  8961. *
  8962. * @param string $data Unused
  8963. * @param string $query
  8964. * @return string
  8965. */
  8966. public function output_html($data, $query='') {
  8967. global $CFG, $OUTPUT;
  8968. require_once($CFG->dirroot . '/webservice/classes/token_table.php');
  8969. $baseurl = new moodle_url('/' . $CFG->admin . '/settings.php?section=webservicetokens');
  8970. $return = $OUTPUT->box_start('generalbox webservicestokenui');
  8971. if (has_capability('moodle/webservice:managealltokens', context_system::instance())) {
  8972. $return .= \html_writer::div(get_string('onlyseecreatedtokens', 'webservice'));
  8973. }
  8974. $table = new \webservice\token_table('webservicetokens');
  8975. $table->define_baseurl($baseurl);
  8976. $table->attributes['class'] = 'admintable generaltable'; // Any need changing?
  8977. $table->data = array();
  8978. ob_start();
  8979. $table->out(10, false);
  8980. $tablehtml = ob_get_contents();
  8981. ob_end_clean();
  8982. $return .= $tablehtml;
  8983. $tokenpageurl = "$CFG->wwwroot/$CFG->admin/webservice/tokens.php?sesskey=" . sesskey();
  8984. $return .= $OUTPUT->box_end();
  8985. // add a token to the table
  8986. $return .= "<a href=\"".$tokenpageurl."&amp;action=create\">";
  8987. $return .= get_string('add')."</a>";
  8988. return highlight($query, $return);
  8989. }
  8990. }
  8991. /**
  8992. * Colour picker
  8993. *
  8994. * @copyright 2010 Sam Hemelryk
  8995. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  8996. */
  8997. class admin_setting_configcolourpicker extends admin_setting {
  8998. /**
  8999. * Information for previewing the colour
  9000. *
  9001. * @var array|null
  9002. */
  9003. protected $previewconfig = null;
  9004. /**
  9005. * Use default when empty.
  9006. */
  9007. protected $usedefaultwhenempty = true;
  9008. /**
  9009. *
  9010. * @param string $name
  9011. * @param string $visiblename
  9012. * @param string $description
  9013. * @param string $defaultsetting
  9014. * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
  9015. */
  9016. public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null,
  9017. $usedefaultwhenempty = true) {
  9018. $this->previewconfig = $previewconfig;
  9019. $this->usedefaultwhenempty = $usedefaultwhenempty;
  9020. parent::__construct($name, $visiblename, $description, $defaultsetting);
  9021. $this->set_force_ltr(true);
  9022. }
  9023. /**
  9024. * Return the setting
  9025. *
  9026. * @return mixed returns config if successful else null
  9027. */
  9028. public function get_setting() {
  9029. return $this->config_read($this->name);
  9030. }
  9031. /**
  9032. * Saves the setting
  9033. *
  9034. * @param string $data
  9035. * @return bool
  9036. */
  9037. public function write_setting($data) {
  9038. $data = $this->validate($data);
  9039. if ($data === false) {
  9040. return get_string('validateerror', 'admin');
  9041. }
  9042. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  9043. }
  9044. /**
  9045. * Validates the colour that was entered by the user
  9046. *
  9047. * @param string $data
  9048. * @return string|false
  9049. */
  9050. protected function validate($data) {
  9051. /**
  9052. * List of valid HTML colour names
  9053. *
  9054. * @var array
  9055. */
  9056. $colornames = array(
  9057. 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
  9058. 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
  9059. 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
  9060. 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
  9061. 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
  9062. 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
  9063. 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
  9064. 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
  9065. 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
  9066. 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
  9067. 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
  9068. 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
  9069. 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
  9070. 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
  9071. 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
  9072. 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
  9073. 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
  9074. 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
  9075. 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
  9076. 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
  9077. 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
  9078. 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
  9079. 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
  9080. 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
  9081. 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
  9082. 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
  9083. 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
  9084. 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
  9085. 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
  9086. 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
  9087. 'whitesmoke', 'yellow', 'yellowgreen'
  9088. );
  9089. if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
  9090. if (strpos($data, '#')!==0) {
  9091. $data = '#'.$data;
  9092. }
  9093. return $data;
  9094. } else if (in_array(strtolower($data), $colornames)) {
  9095. return $data;
  9096. } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
  9097. return $data;
  9098. } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
  9099. return $data;
  9100. } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
  9101. return $data;
  9102. } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
  9103. return $data;
  9104. } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
  9105. return $data;
  9106. } else if (empty($data)) {
  9107. if ($this->usedefaultwhenempty){
  9108. return $this->defaultsetting;
  9109. } else {
  9110. return '';
  9111. }
  9112. } else {
  9113. return false;
  9114. }
  9115. }
  9116. /**
  9117. * Generates the HTML for the setting
  9118. *
  9119. * @global moodle_page $PAGE
  9120. * @global core_renderer $OUTPUT
  9121. * @param string $data
  9122. * @param string $query
  9123. */
  9124. public function output_html($data, $query = '') {
  9125. global $PAGE, $OUTPUT;
  9126. $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
  9127. $context = (object) [
  9128. 'id' => $this->get_id(),
  9129. 'name' => $this->get_full_name(),
  9130. 'value' => $data,
  9131. 'icon' => $icon->export_for_template($OUTPUT),
  9132. 'haspreviewconfig' => !empty($this->previewconfig),
  9133. 'forceltr' => $this->get_force_ltr()
  9134. ];
  9135. $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
  9136. $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
  9137. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
  9138. $this->get_defaultsetting(), $query);
  9139. }
  9140. }
  9141. /**
  9142. * Class used for uploading of one file into file storage,
  9143. * the file name is stored in config table.
  9144. *
  9145. * Please note you need to implement your own '_pluginfile' callback function,
  9146. * this setting only stores the file, it does not deal with file serving.
  9147. *
  9148. * @copyright 2013 Petr Skoda {@link http://skodak.org}
  9149. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9150. */
  9151. class admin_setting_configstoredfile extends admin_setting {
  9152. /** @var array file area options - should be one file only */
  9153. protected $options;
  9154. /** @var string name of the file area */
  9155. protected $filearea;
  9156. /** @var int intemid */
  9157. protected $itemid;
  9158. /** @var string used for detection of changes */
  9159. protected $oldhashes;
  9160. /**
  9161. * Create new stored file setting.
  9162. *
  9163. * @param string $name low level setting name
  9164. * @param string $visiblename human readable setting name
  9165. * @param string $description description of setting
  9166. * @param mixed $filearea file area for file storage
  9167. * @param int $itemid itemid for file storage
  9168. * @param array $options file area options
  9169. */
  9170. public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
  9171. parent::__construct($name, $visiblename, $description, '');
  9172. $this->filearea = $filearea;
  9173. $this->itemid = $itemid;
  9174. $this->options = (array)$options;
  9175. }
  9176. /**
  9177. * Applies defaults and returns all options.
  9178. * @return array
  9179. */
  9180. protected function get_options() {
  9181. global $CFG;
  9182. require_once("$CFG->libdir/filelib.php");
  9183. require_once("$CFG->dirroot/repository/lib.php");
  9184. $defaults = array(
  9185. 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
  9186. 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
  9187. 'context' => context_system::instance());
  9188. foreach($this->options as $k => $v) {
  9189. $defaults[$k] = $v;
  9190. }
  9191. return $defaults;
  9192. }
  9193. public function get_setting() {
  9194. return $this->config_read($this->name);
  9195. }
  9196. public function write_setting($data) {
  9197. global $USER;
  9198. // Let's not deal with validation here, this is for admins only.
  9199. $current = $this->get_setting();
  9200. if (empty($data) && $current === null) {
  9201. // This will be the case when applying default settings (installation).
  9202. return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
  9203. } else if (!is_number($data)) {
  9204. // Draft item id is expected here!
  9205. return get_string('errorsetting', 'admin');
  9206. }
  9207. $options = $this->get_options();
  9208. $fs = get_file_storage();
  9209. $component = is_null($this->plugin) ? 'core' : $this->plugin;
  9210. $this->oldhashes = null;
  9211. if ($current) {
  9212. $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
  9213. if ($file = $fs->get_file_by_hash($hash)) {
  9214. $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
  9215. }
  9216. unset($file);
  9217. }
  9218. if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
  9219. // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
  9220. // But we can safely ignore that if the destination area is empty, so that the user is not prompt
  9221. // with an error because the draft area does not exist, as he did not use it.
  9222. $usercontext = context_user::instance($USER->id);
  9223. if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
  9224. return get_string('errorsetting', 'admin');
  9225. }
  9226. }
  9227. file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
  9228. $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
  9229. $filepath = '';
  9230. if ($files) {
  9231. /** @var stored_file $file */
  9232. $file = reset($files);
  9233. $filepath = $file->get_filepath().$file->get_filename();
  9234. }
  9235. return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
  9236. }
  9237. public function post_write_settings($original) {
  9238. $options = $this->get_options();
  9239. $fs = get_file_storage();
  9240. $component = is_null($this->plugin) ? 'core' : $this->plugin;
  9241. $current = $this->get_setting();
  9242. $newhashes = null;
  9243. if ($current) {
  9244. $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
  9245. if ($file = $fs->get_file_by_hash($hash)) {
  9246. $newhashes = $file->get_contenthash().$file->get_pathnamehash();
  9247. }
  9248. unset($file);
  9249. }
  9250. if ($this->oldhashes === $newhashes) {
  9251. $this->oldhashes = null;
  9252. return false;
  9253. }
  9254. $this->oldhashes = null;
  9255. $callbackfunction = $this->updatedcallback;
  9256. if (!empty($callbackfunction) and function_exists($callbackfunction)) {
  9257. $callbackfunction($this->get_full_name());
  9258. }
  9259. return true;
  9260. }
  9261. public function output_html($data, $query = '') {
  9262. global $PAGE, $CFG;
  9263. $options = $this->get_options();
  9264. $id = $this->get_id();
  9265. $elname = $this->get_full_name();
  9266. $draftitemid = file_get_submitted_draft_itemid($elname);
  9267. $component = is_null($this->plugin) ? 'core' : $this->plugin;
  9268. file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
  9269. // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
  9270. require_once("$CFG->dirroot/lib/form/filemanager.php");
  9271. $fmoptions = new stdClass();
  9272. $fmoptions->mainfile = $options['mainfile'];
  9273. $fmoptions->maxbytes = $options['maxbytes'];
  9274. $fmoptions->maxfiles = $options['maxfiles'];
  9275. $fmoptions->client_id = uniqid();
  9276. $fmoptions->itemid = $draftitemid;
  9277. $fmoptions->subdirs = $options['subdirs'];
  9278. $fmoptions->target = $id;
  9279. $fmoptions->accepted_types = $options['accepted_types'];
  9280. $fmoptions->return_types = $options['return_types'];
  9281. $fmoptions->context = $options['context'];
  9282. $fmoptions->areamaxbytes = $options['areamaxbytes'];
  9283. $fm = new form_filemanager($fmoptions);
  9284. $output = $PAGE->get_renderer('core', 'files');
  9285. $html = $output->render($fm);
  9286. $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
  9287. $html .= '<input value="" id="'.$id.'" type="hidden" />';
  9288. return format_admin_setting($this, $this->visiblename,
  9289. '<div class="form-filemanager" data-fieldtype="filemanager">'.$html.'</div>',
  9290. $this->description, true, '', '', $query);
  9291. }
  9292. }
  9293. /**
  9294. * Administration interface for user specified regular expressions for device detection.
  9295. *
  9296. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9297. */
  9298. class admin_setting_devicedetectregex extends admin_setting {
  9299. /**
  9300. * Calls parent::__construct with specific args
  9301. *
  9302. * @param string $name
  9303. * @param string $visiblename
  9304. * @param string $description
  9305. * @param mixed $defaultsetting
  9306. */
  9307. public function __construct($name, $visiblename, $description, $defaultsetting = '') {
  9308. global $CFG;
  9309. parent::__construct($name, $visiblename, $description, $defaultsetting);
  9310. }
  9311. /**
  9312. * Return the current setting(s)
  9313. *
  9314. * @return array Current settings array
  9315. */
  9316. public function get_setting() {
  9317. global $CFG;
  9318. $config = $this->config_read($this->name);
  9319. if (is_null($config)) {
  9320. return null;
  9321. }
  9322. return $this->prepare_form_data($config);
  9323. }
  9324. /**
  9325. * Save selected settings
  9326. *
  9327. * @param array $data Array of settings to save
  9328. * @return bool
  9329. */
  9330. public function write_setting($data) {
  9331. if (empty($data)) {
  9332. $data = array();
  9333. }
  9334. if ($this->config_write($this->name, $this->process_form_data($data))) {
  9335. return ''; // success
  9336. } else {
  9337. return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
  9338. }
  9339. }
  9340. /**
  9341. * Return XHTML field(s) for regexes
  9342. *
  9343. * @param array $data Array of options to set in HTML
  9344. * @return string XHTML string for the fields and wrapping div(s)
  9345. */
  9346. public function output_html($data, $query='') {
  9347. global $OUTPUT;
  9348. $context = (object) [
  9349. 'expressions' => [],
  9350. 'name' => $this->get_full_name()
  9351. ];
  9352. if (empty($data)) {
  9353. $looplimit = 1;
  9354. } else {
  9355. $looplimit = (count($data)/2)+1;
  9356. }
  9357. for ($i=0; $i<$looplimit; $i++) {
  9358. $expressionname = 'expression'.$i;
  9359. if (!empty($data[$expressionname])){
  9360. $expression = $data[$expressionname];
  9361. } else {
  9362. $expression = '';
  9363. }
  9364. $valuename = 'value'.$i;
  9365. if (!empty($data[$valuename])){
  9366. $value = $data[$valuename];
  9367. } else {
  9368. $value= '';
  9369. }
  9370. $context->expressions[] = [
  9371. 'index' => $i,
  9372. 'expression' => $expression,
  9373. 'value' => $value
  9374. ];
  9375. }
  9376. $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
  9377. return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
  9378. }
  9379. /**
  9380. * Converts the string of regexes
  9381. *
  9382. * @see self::process_form_data()
  9383. * @param $regexes string of regexes
  9384. * @return array of form fields and their values
  9385. */
  9386. protected function prepare_form_data($regexes) {
  9387. $regexes = json_decode($regexes);
  9388. $form = array();
  9389. $i = 0;
  9390. foreach ($regexes as $value => $regex) {
  9391. $expressionname = 'expression'.$i;
  9392. $valuename = 'value'.$i;
  9393. $form[$expressionname] = $regex;
  9394. $form[$valuename] = $value;
  9395. $i++;
  9396. }
  9397. return $form;
  9398. }
  9399. /**
  9400. * Converts the data from admin settings form into a string of regexes
  9401. *
  9402. * @see self::prepare_form_data()
  9403. * @param array $data array of admin form fields and values
  9404. * @return false|string of regexes
  9405. */
  9406. protected function process_form_data(array $form) {
  9407. $count = count($form); // number of form field values
  9408. if ($count % 2) {
  9409. // we must get five fields per expression
  9410. return false;
  9411. }
  9412. $regexes = array();
  9413. for ($i = 0; $i < $count / 2; $i++) {
  9414. $expressionname = "expression".$i;
  9415. $valuename = "value".$i;
  9416. $expression = trim($form['expression'.$i]);
  9417. $value = trim($form['value'.$i]);
  9418. if (empty($expression)){
  9419. continue;
  9420. }
  9421. $regexes[$value] = $expression;
  9422. }
  9423. $regexes = json_encode($regexes);
  9424. return $regexes;
  9425. }
  9426. }
  9427. /**
  9428. * Multiselect for current modules
  9429. *
  9430. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9431. */
  9432. class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
  9433. private $excludesystem;
  9434. /**
  9435. * Calls parent::__construct - note array $choices is not required
  9436. *
  9437. * @param string $name setting name
  9438. * @param string $visiblename localised setting name
  9439. * @param string $description setting description
  9440. * @param array $defaultsetting a plain array of default module ids
  9441. * @param bool $excludesystem If true, excludes modules with 'system' archetype
  9442. */
  9443. public function __construct($name, $visiblename, $description, $defaultsetting = array(),
  9444. $excludesystem = true) {
  9445. parent::__construct($name, $visiblename, $description, $defaultsetting, null);
  9446. $this->excludesystem = $excludesystem;
  9447. }
  9448. /**
  9449. * Loads an array of current module choices
  9450. *
  9451. * @return bool always return true
  9452. */
  9453. public function load_choices() {
  9454. if (is_array($this->choices)) {
  9455. return true;
  9456. }
  9457. $this->choices = array();
  9458. global $CFG, $DB;
  9459. $records = $DB->get_records('modules', array('visible'=>1), 'name');
  9460. foreach ($records as $record) {
  9461. // Exclude modules if the code doesn't exist
  9462. if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
  9463. // Also exclude system modules (if specified)
  9464. if (!($this->excludesystem &&
  9465. plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
  9466. MOD_ARCHETYPE_SYSTEM)) {
  9467. $this->choices[$record->id] = $record->name;
  9468. }
  9469. }
  9470. }
  9471. return true;
  9472. }
  9473. }
  9474. /**
  9475. * Admin setting to show if a php extension is enabled or not.
  9476. *
  9477. * @copyright 2013 Damyon Wiese
  9478. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9479. */
  9480. class admin_setting_php_extension_enabled extends admin_setting {
  9481. /** @var string The name of the extension to check for */
  9482. private $extension;
  9483. /**
  9484. * Calls parent::__construct with specific arguments
  9485. */
  9486. public function __construct($name, $visiblename, $description, $extension) {
  9487. $this->extension = $extension;
  9488. $this->nosave = true;
  9489. parent::__construct($name, $visiblename, $description, '');
  9490. }
  9491. /**
  9492. * Always returns true, does nothing
  9493. *
  9494. * @return true
  9495. */
  9496. public function get_setting() {
  9497. return true;
  9498. }
  9499. /**
  9500. * Always returns true, does nothing
  9501. *
  9502. * @return true
  9503. */
  9504. public function get_defaultsetting() {
  9505. return true;
  9506. }
  9507. /**
  9508. * Always returns '', does not write anything
  9509. *
  9510. * @return string Always returns ''
  9511. */
  9512. public function write_setting($data) {
  9513. // Do not write any setting.
  9514. return '';
  9515. }
  9516. /**
  9517. * Outputs the html for this setting.
  9518. * @return string Returns an XHTML string
  9519. */
  9520. public function output_html($data, $query='') {
  9521. global $OUTPUT;
  9522. $o = '';
  9523. if (!extension_loaded($this->extension)) {
  9524. $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description;
  9525. $o .= format_admin_setting($this, $this->visiblename, $warning);
  9526. }
  9527. return $o;
  9528. }
  9529. }
  9530. /**
  9531. * Server timezone setting.
  9532. *
  9533. * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
  9534. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9535. * @author Petr Skoda <petr.skoda@totaralms.com>
  9536. */
  9537. class admin_setting_servertimezone extends admin_setting_configselect {
  9538. /**
  9539. * Constructor.
  9540. */
  9541. public function __construct() {
  9542. $default = core_date::get_default_php_timezone();
  9543. if ($default === 'UTC') {
  9544. // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
  9545. $default = 'Europe/London';
  9546. }
  9547. parent::__construct('timezone',
  9548. new lang_string('timezone', 'core_admin'),
  9549. new lang_string('configtimezone', 'core_admin'), $default, null);
  9550. }
  9551. /**
  9552. * Lazy load timezone options.
  9553. * @return bool true if loaded, false if error
  9554. */
  9555. public function load_choices() {
  9556. global $CFG;
  9557. if (is_array($this->choices)) {
  9558. return true;
  9559. }
  9560. $current = isset($CFG->timezone) ? $CFG->timezone : null;
  9561. $this->choices = core_date::get_list_of_timezones($current, false);
  9562. if ($current == 99) {
  9563. // Do not show 99 unless it is current value, we want to get rid of it over time.
  9564. $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
  9565. core_date::get_default_php_timezone());
  9566. }
  9567. return true;
  9568. }
  9569. }
  9570. /**
  9571. * Forced user timezone setting.
  9572. *
  9573. * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
  9574. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9575. * @author Petr Skoda <petr.skoda@totaralms.com>
  9576. */
  9577. class admin_setting_forcetimezone extends admin_setting_configselect {
  9578. /**
  9579. * Constructor.
  9580. */
  9581. public function __construct() {
  9582. parent::__construct('forcetimezone',
  9583. new lang_string('forcetimezone', 'core_admin'),
  9584. new lang_string('helpforcetimezone', 'core_admin'), '99', null);
  9585. }
  9586. /**
  9587. * Lazy load timezone options.
  9588. * @return bool true if loaded, false if error
  9589. */
  9590. public function load_choices() {
  9591. global $CFG;
  9592. if (is_array($this->choices)) {
  9593. return true;
  9594. }
  9595. $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
  9596. $this->choices = core_date::get_list_of_timezones($current, true);
  9597. $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
  9598. return true;
  9599. }
  9600. }
  9601. /**
  9602. * Search setup steps info.
  9603. *
  9604. * @package core
  9605. * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
  9606. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9607. */
  9608. class admin_setting_searchsetupinfo extends admin_setting {
  9609. /**
  9610. * Calls parent::__construct with specific arguments
  9611. */
  9612. public function __construct() {
  9613. $this->nosave = true;
  9614. parent::__construct('searchsetupinfo', '', '', '');
  9615. }
  9616. /**
  9617. * Always returns true, does nothing
  9618. *
  9619. * @return true
  9620. */
  9621. public function get_setting() {
  9622. return true;
  9623. }
  9624. /**
  9625. * Always returns true, does nothing
  9626. *
  9627. * @return true
  9628. */
  9629. public function get_defaultsetting() {
  9630. return true;
  9631. }
  9632. /**
  9633. * Always returns '', does not write anything
  9634. *
  9635. * @param array $data
  9636. * @return string Always returns ''
  9637. */
  9638. public function write_setting($data) {
  9639. // Do not write any setting.
  9640. return '';
  9641. }
  9642. /**
  9643. * Builds the HTML to display the control
  9644. *
  9645. * @param string $data Unused
  9646. * @param string $query
  9647. * @return string
  9648. */
  9649. public function output_html($data, $query='') {
  9650. global $CFG, $OUTPUT;
  9651. $return = '';
  9652. $brtag = html_writer::empty_tag('br');
  9653. $searchareas = \core_search\manager::get_search_areas_list();
  9654. $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
  9655. $anyindexed = false;
  9656. foreach ($searchareas as $areaid => $searcharea) {
  9657. list($componentname, $varname) = $searcharea->get_config_var_name();
  9658. if (get_config($componentname, $varname . '_indexingstart')) {
  9659. $anyindexed = true;
  9660. break;
  9661. }
  9662. }
  9663. $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
  9664. $table = new html_table();
  9665. $table->head = array(get_string('step', 'search'), get_string('status'));
  9666. $table->colclasses = array('leftalign step', 'leftalign status');
  9667. $table->id = 'searchsetup';
  9668. $table->attributes['class'] = 'admintable generaltable';
  9669. $table->data = array();
  9670. $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
  9671. // Select a search engine.
  9672. $row = array();
  9673. $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
  9674. $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
  9675. array('href' => $url));
  9676. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9677. if (!empty($CFG->searchengine)) {
  9678. $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
  9679. array('class' => 'badge badge-success'));
  9680. }
  9681. $row[1] = $status;
  9682. $table->data[] = $row;
  9683. // Available areas.
  9684. $row = array();
  9685. $url = new moodle_url('/admin/searchareas.php');
  9686. $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
  9687. array('href' => $url));
  9688. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9689. if ($anyenabled) {
  9690. $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
  9691. }
  9692. $row[1] = $status;
  9693. $table->data[] = $row;
  9694. // Setup search engine.
  9695. $row = array();
  9696. if (empty($CFG->searchengine)) {
  9697. $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
  9698. $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9699. } else {
  9700. $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
  9701. $row[0] = '3. ' . html_writer::tag('a', get_string('setupsearchengine', 'admin'),
  9702. array('href' => $url));
  9703. // Check the engine status.
  9704. $searchengine = \core_search\manager::search_engine_instance();
  9705. try {
  9706. $serverstatus = $searchengine->is_server_ready();
  9707. } catch (\moodle_exception $e) {
  9708. $serverstatus = $e->getMessage();
  9709. }
  9710. if ($serverstatus === true) {
  9711. $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
  9712. } else {
  9713. $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger'));
  9714. }
  9715. $row[1] = $status;
  9716. }
  9717. $table->data[] = $row;
  9718. // Indexed data.
  9719. $row = array();
  9720. $url = new moodle_url('/admin/searchareas.php');
  9721. $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
  9722. if ($anyindexed) {
  9723. $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
  9724. } else {
  9725. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9726. }
  9727. $row[1] = $status;
  9728. $table->data[] = $row;
  9729. // Enable global search.
  9730. $row = array();
  9731. $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
  9732. $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
  9733. array('href' => $url));
  9734. $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
  9735. if (\core_search\manager::is_global_search_enabled()) {
  9736. $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
  9737. }
  9738. $row[1] = $status;
  9739. $table->data[] = $row;
  9740. $return .= html_writer::table($table);
  9741. return highlight($query, $return);
  9742. }
  9743. }
  9744. /**
  9745. * Used to validate the contents of SCSS code and ensuring they are parsable.
  9746. *
  9747. * It does not attempt to detect undefined SCSS variables because it is designed
  9748. * to be used without knowledge of other config/scss included.
  9749. *
  9750. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9751. * @copyright 2016 Dan Poltawski <dan@moodle.com>
  9752. */
  9753. class admin_setting_scsscode extends admin_setting_configtextarea {
  9754. /**
  9755. * Validate the contents of the SCSS to ensure its parsable. Does not
  9756. * attempt to detect undefined scss variables.
  9757. *
  9758. * @param string $data The scss code from text field.
  9759. * @return mixed bool true for success or string:error on failure.
  9760. */
  9761. public function validate($data) {
  9762. if (empty($data)) {
  9763. return true;
  9764. }
  9765. $scss = new core_scss();
  9766. try {
  9767. $scss->compile($data);
  9768. } catch (ScssPhp\ScssPhp\Exception\ParserException $e) {
  9769. return get_string('scssinvalid', 'admin', $e->getMessage());
  9770. } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) {
  9771. // Silently ignore this - it could be a scss variable defined from somewhere
  9772. // else which we are not examining here.
  9773. return true;
  9774. }
  9775. return true;
  9776. }
  9777. }
  9778. /**
  9779. * Administration setting to define a list of file types.
  9780. *
  9781. * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
  9782. * @copyright 2017 David Mudrák <david@moodle.com>
  9783. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9784. */
  9785. class admin_setting_filetypes extends admin_setting_configtext {
  9786. /** @var array Allow selection from these file types only. */
  9787. protected $onlytypes = [];
  9788. /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
  9789. protected $allowall = true;
  9790. /** @var core_form\filetypes_util instance to use as a helper. */
  9791. protected $util = null;
  9792. /**
  9793. * Constructor.
  9794. *
  9795. * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
  9796. * @param string $visiblename Localised label of the setting
  9797. * @param string $description Localised description of the setting
  9798. * @param string $defaultsetting Default setting value.
  9799. * @param array $options Setting widget options, an array with optional keys:
  9800. * 'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
  9801. * 'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
  9802. */
  9803. public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
  9804. parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
  9805. if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
  9806. $this->onlytypes = $options['onlytypes'];
  9807. }
  9808. if (!$this->onlytypes && array_key_exists('allowall', $options)) {
  9809. $this->allowall = (bool)$options['allowall'];
  9810. }
  9811. $this->util = new \core_form\filetypes_util();
  9812. }
  9813. /**
  9814. * Normalize the user's input and write it to the database as comma separated list.
  9815. *
  9816. * Comma separated list as a text representation of the array was chosen to
  9817. * make this compatible with how the $CFG->courseoverviewfilesext values are stored.
  9818. *
  9819. * @param string $data Value submitted by the admin.
  9820. * @return string Epty string if all good, error message otherwise.
  9821. */
  9822. public function write_setting($data) {
  9823. return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
  9824. }
  9825. /**
  9826. * Validate data before storage
  9827. *
  9828. * @param string $data The setting values provided by the admin
  9829. * @return bool|string True if ok, the string if error found
  9830. */
  9831. public function validate($data) {
  9832. // No need to call parent's validation here as we are PARAM_RAW.
  9833. if ($this->util->is_whitelisted($data, $this->onlytypes)) {
  9834. return true;
  9835. } else {
  9836. $troublemakers = $this->util->get_not_whitelisted($data, $this->onlytypes);
  9837. return get_string('filetypesnotwhitelisted', 'core_form', implode(' ', $troublemakers));
  9838. }
  9839. }
  9840. /**
  9841. * Return an HTML string for the setting element.
  9842. *
  9843. * @param string $data The current setting value
  9844. * @param string $query Admin search query to be highlighted
  9845. * @return string HTML to be displayed
  9846. */
  9847. public function output_html($data, $query='') {
  9848. global $OUTPUT, $PAGE;
  9849. $default = $this->get_defaultsetting();
  9850. $context = (object) [
  9851. 'id' => $this->get_id(),
  9852. 'name' => $this->get_full_name(),
  9853. 'value' => $data,
  9854. 'descriptions' => $this->util->describe_file_types($data),
  9855. ];
  9856. $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
  9857. $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
  9858. $this->get_id(),
  9859. $this->visiblename->out(),
  9860. $this->onlytypes,
  9861. $this->allowall,
  9862. ]);
  9863. return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
  9864. }
  9865. /**
  9866. * Should the values be always displayed in LTR mode?
  9867. *
  9868. * We always return true here because these values are not RTL compatible.
  9869. *
  9870. * @return bool True because these values are not RTL compatible.
  9871. */
  9872. public function get_force_ltr() {
  9873. return true;
  9874. }
  9875. }
  9876. /**
  9877. * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
  9878. *
  9879. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9880. * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
  9881. */
  9882. class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
  9883. /**
  9884. * Constructor.
  9885. *
  9886. * @param string $name
  9887. * @param string $visiblename
  9888. * @param string $description
  9889. * @param mixed $defaultsetting string or array
  9890. * @param mixed $paramtype
  9891. * @param string $cols
  9892. * @param string $rows
  9893. */
  9894. public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
  9895. $cols = '60', $rows = '8') {
  9896. parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
  9897. // Pre-set force LTR to false.
  9898. $this->set_force_ltr(false);
  9899. }
  9900. /**
  9901. * Validate the content and format of the age of digital consent map to ensure it is parsable.
  9902. *
  9903. * @param string $data The age of digital consent map from text field.
  9904. * @return mixed bool true for success or string:error on failure.
  9905. */
  9906. public function validate($data) {
  9907. if (empty($data)) {
  9908. return true;
  9909. }
  9910. try {
  9911. \core_auth\digital_consent::parse_age_digital_consent_map($data);
  9912. } catch (\moodle_exception $e) {
  9913. return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
  9914. }
  9915. return true;
  9916. }
  9917. }
  9918. /**
  9919. * Selection of plugins that can work as site policy handlers
  9920. *
  9921. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9922. * @copyright 2018 Marina Glancy
  9923. */
  9924. class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
  9925. /**
  9926. * Constructor
  9927. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
  9928. * for ones in config_plugins.
  9929. * @param string $visiblename localised
  9930. * @param string $description long localised info
  9931. * @param string $defaultsetting
  9932. */
  9933. public function __construct($name, $visiblename, $description, $defaultsetting = '') {
  9934. parent::__construct($name, $visiblename, $description, $defaultsetting, null);
  9935. }
  9936. /**
  9937. * Lazy-load the available choices for the select box
  9938. */
  9939. public function load_choices() {
  9940. if (during_initial_install()) {
  9941. return false;
  9942. }
  9943. if (is_array($this->choices)) {
  9944. return true;
  9945. }
  9946. $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
  9947. $manager = new \core_privacy\local\sitepolicy\manager();
  9948. $plugins = $manager->get_all_handlers();
  9949. foreach ($plugins as $pname => $unused) {
  9950. $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
  9951. ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
  9952. }
  9953. return true;
  9954. }
  9955. }
  9956. /**
  9957. * Used to validate theme presets code and ensuring they compile well.
  9958. *
  9959. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  9960. * @copyright 2019 Bas Brands <bas@moodle.com>
  9961. */
  9962. class admin_setting_configthemepreset extends admin_setting_configselect {
  9963. /** @var string The name of the theme to check for */
  9964. private $themename;
  9965. /**
  9966. * Constructor
  9967. * @param string $name unique ascii name, either 'mysetting' for settings that in config,
  9968. * or 'myplugin/mysetting' for ones in config_plugins.
  9969. * @param string $visiblename localised
  9970. * @param string $description long localised info
  9971. * @param string|int $defaultsetting
  9972. * @param array $choices array of $value=>$label for each selection
  9973. * @param string $themename name of theme to check presets for.
  9974. */
  9975. public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
  9976. $this->themename = $themename;
  9977. parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
  9978. }
  9979. /**
  9980. * Write settings if validated
  9981. *
  9982. * @param string $data
  9983. * @return string
  9984. */
  9985. public function write_setting($data) {
  9986. $validated = $this->validate($data);
  9987. if ($validated !== true) {
  9988. return $validated;
  9989. }
  9990. return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
  9991. }
  9992. /**
  9993. * Validate the preset file to ensure its parsable.
  9994. *
  9995. * @param string $data The preset file chosen.
  9996. * @return mixed bool true for success or string:error on failure.
  9997. */
  9998. public function validate($data) {
  9999. if (in_array($data, ['default.scss', 'plain.scss'])) {
  10000. return true;
  10001. }
  10002. $fs = get_file_storage();
  10003. $theme = theme_config::load($this->themename);
  10004. $context = context_system::instance();
  10005. // If the preset has not changed there is no need to validate it.
  10006. if ($theme->settings->preset == $data) {
  10007. return true;
  10008. }
  10009. if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
  10010. // This operation uses a lot of resources.
  10011. raise_memory_limit(MEMORY_EXTRA);
  10012. core_php_time_limit::raise(300);
  10013. // TODO: MDL-62757 When changing anything in this method please do not forget to check
  10014. // if the get_css_content_from_scss() method in class theme_config needs updating too.
  10015. $compiler = new core_scss();
  10016. $compiler->prepend_raw_scss($theme->get_pre_scss_code());
  10017. $compiler->append_raw_scss($presetfile->get_content());
  10018. if ($scssproperties = $theme->get_scss_property()) {
  10019. $compiler->setImportPaths($scssproperties[0]);
  10020. }
  10021. $compiler->append_raw_scss($theme->get_extra_scss_code());
  10022. try {
  10023. $compiler->to_css();
  10024. } catch (Exception $e) {
  10025. return get_string('invalidthemepreset', 'admin', $e->getMessage());
  10026. }
  10027. // Try to save memory.
  10028. $compiler = null;
  10029. unset($compiler);
  10030. }
  10031. return true;
  10032. }
  10033. }
  10034. /**
  10035. * Selection of plugins that can work as H5P libraries handlers
  10036. *
  10037. * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  10038. * @copyright 2020 Sara Arjona <sara@moodle.com>
  10039. */
  10040. class admin_settings_h5plib_handler_select extends admin_setting_configselect {
  10041. /**
  10042. * Constructor
  10043. * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
  10044. * for ones in config_plugins.
  10045. * @param string $visiblename localised
  10046. * @param string $description long localised info
  10047. * @param string $defaultsetting
  10048. */
  10049. public function __construct($name, $visiblename, $description, $defaultsetting = '') {
  10050. parent::__construct($name, $visiblename, $description, $defaultsetting, null);
  10051. }
  10052. /**
  10053. * Lazy-load the available choices for the select box
  10054. */
  10055. public function load_choices() {
  10056. if (during_initial_install()) {
  10057. return false;
  10058. }
  10059. if (is_array($this->choices)) {
  10060. return true;
  10061. }
  10062. $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
  10063. foreach ($this->choices as $name => $class) {
  10064. $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
  10065. ['name' => new lang_string('pluginname', $name), 'component' => $name]);
  10066. }
  10067. return true;
  10068. }
  10069. }