Merge branch 'MDL-70099-311' of git://github.com/paulholden/moodle into MOODLE_311_STABLE
[moodle.git] / lib / adminlib.php
blob0c2eac25a2dd0cbd0bcd482d79fe5e61ce0d824a
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.
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/>.
17 /**
18 * Functions and classes used during installation, upgrades and for admin settings.
20 * ADMIN SETTINGS TREE INTRODUCTION
22 * This file performs the following tasks:
23 * -it defines the necessary objects and interfaces to build the Moodle
24 * admin hierarchy
25 * -it defines the admin_externalpage_setup()
27 * ADMIN_SETTING OBJECTS
29 * Moodle settings are represented by objects that inherit from the admin_setting
30 * class. These objects encapsulate how to read a setting, how to write a new value
31 * to a setting, and how to appropriately display the HTML to modify the setting.
33 * ADMIN_SETTINGPAGE OBJECTS
35 * The admin_setting objects are then grouped into admin_settingpages. The latter
36 * appear in the Moodle admin tree block. All interaction with admin_settingpage
37 * objects is handled by the admin/settings.php file.
39 * ADMIN_EXTERNALPAGE OBJECTS
41 * There are some settings in Moodle that are too complex to (efficiently) handle
42 * with admin_settingpages. (Consider, for example, user management and displaying
43 * lists of users.) In this case, we use the admin_externalpage object. This object
44 * places a link to an external PHP file in the admin tree block.
46 * If you're using an admin_externalpage object for some settings, you can take
47 * advantage of the admin_externalpage_* functions. For example, suppose you wanted
48 * to add a foo.php file into admin. First off, you add the following line to
49 * admin/settings/first.php (at the end of the file) or to some other file in
50 * admin/settings:
51 * <code>
52 * $ADMIN->add('userinterface', new admin_externalpage('foo', get_string('foo'),
53 * $CFG->wwwdir . '/' . '$CFG->admin . '/foo.php', 'some_role_permission'));
54 * </code>
56 * Next, in foo.php, your file structure would resemble the following:
57 * <code>
58 * require(__DIR__.'/../../config.php');
59 * require_once($CFG->libdir.'/adminlib.php');
60 * admin_externalpage_setup('foo');
61 * // functionality like processing form submissions goes here
62 * echo $OUTPUT->header();
63 * // your HTML goes here
64 * echo $OUTPUT->footer();
65 * </code>
67 * The admin_externalpage_setup() function call ensures the user is logged in,
68 * and makes sure that they have the proper role permission to access the page.
69 * It also configures all $PAGE properties needed for navigation.
71 * ADMIN_CATEGORY OBJECTS
73 * Above and beyond all this, we have admin_category objects. These objects
74 * appear as folders in the admin tree block. They contain admin_settingpage's,
75 * admin_externalpage's, and other admin_category's.
77 * OTHER NOTES
79 * admin_settingpage's, admin_externalpage's, and admin_category's all inherit
80 * from part_of_admin_tree (a pseudointerface). This interface insists that
81 * a class has a check_access method for access permissions, a locate method
82 * used to find a specific node in the admin tree and find parent path.
84 * admin_category's inherit from parentable_part_of_admin_tree. This pseudo-
85 * interface ensures that the class implements a recursive add function which
86 * accepts a part_of_admin_tree object and searches for the proper place to
87 * put it. parentable_part_of_admin_tree implies part_of_admin_tree.
89 * Please note that the $this->name field of any part_of_admin_tree must be
90 * UNIQUE throughout the ENTIRE admin tree.
92 * The $this->name field of an admin_setting object (which is *not* part_of_
93 * admin_tree) must be unique on the respective admin_settingpage where it is
94 * used.
96 * Original author: Vincenzo K. Marcovecchio
97 * Maintainer: Petr Skoda
99 * @package core
100 * @subpackage admin
101 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
102 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
105 defined('MOODLE_INTERNAL') || die();
107 /// Add libraries
108 require_once($CFG->libdir.'/ddllib.php');
109 require_once($CFG->libdir.'/xmlize.php');
110 require_once($CFG->libdir.'/messagelib.php');
112 define('INSECURE_DATAROOT_WARNING', 1);
113 define('INSECURE_DATAROOT_ERROR', 2);
116 * Automatically clean-up all plugin data and remove the plugin DB tables
118 * NOTE: do not call directly, use new /admin/plugins.php?uninstall=component instead!
120 * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
121 * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
122 * @uses global $OUTPUT to produce notices and other messages
123 * @return void
125 function uninstall_plugin($type, $name) {
126 global $CFG, $DB, $OUTPUT;
128 // This may take a long time.
129 core_php_time_limit::raise();
131 // Recursively uninstall all subplugins first.
132 $subplugintypes = core_component::get_plugin_types_with_subplugins();
133 if (isset($subplugintypes[$type])) {
134 $base = core_component::get_plugin_directory($type, $name);
136 $subpluginsfile = "{$base}/db/subplugins.json";
137 if (file_exists($subpluginsfile)) {
138 $subplugins = (array) json_decode(file_get_contents($subpluginsfile))->plugintypes;
139 } else if (file_exists("{$base}/db/subplugins.php")) {
140 debugging('Use of subplugins.php has been deprecated. ' .
141 'Please update your plugin to provide a subplugins.json file instead.',
142 DEBUG_DEVELOPER);
143 $subplugins = [];
144 include("{$base}/db/subplugins.php");
147 if (!empty($subplugins)) {
148 foreach (array_keys($subplugins) as $subplugintype) {
149 $instances = core_component::get_plugin_list($subplugintype);
150 foreach ($instances as $subpluginname => $notusedpluginpath) {
151 uninstall_plugin($subplugintype, $subpluginname);
157 $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
159 if ($type === 'mod') {
160 $pluginname = $name; // eg. 'forum'
161 if (get_string_manager()->string_exists('modulename', $component)) {
162 $strpluginname = get_string('modulename', $component);
163 } else {
164 $strpluginname = $component;
167 } else {
168 $pluginname = $component;
169 if (get_string_manager()->string_exists('pluginname', $component)) {
170 $strpluginname = get_string('pluginname', $component);
171 } else {
172 $strpluginname = $component;
176 echo $OUTPUT->heading($pluginname);
178 // Delete all tag areas, collections and instances associated with this plugin.
179 core_tag_area::uninstall($component);
181 // Custom plugin uninstall.
182 $plugindirectory = core_component::get_plugin_directory($type, $name);
183 $uninstalllib = $plugindirectory . '/db/uninstall.php';
184 if (file_exists($uninstalllib)) {
185 require_once($uninstalllib);
186 $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
187 if (function_exists($uninstallfunction)) {
188 // Do not verify result, let plugin complain if necessary.
189 $uninstallfunction();
193 // Specific plugin type cleanup.
194 $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
195 if ($plugininfo) {
196 $plugininfo->uninstall_cleanup();
197 core_plugin_manager::reset_caches();
199 $plugininfo = null;
201 // perform clean-up task common for all the plugin/subplugin types
203 //delete the web service functions and pre-built services
204 require_once($CFG->dirroot.'/lib/externallib.php');
205 external_delete_descriptions($component);
207 // delete calendar events
208 $DB->delete_records('event', array('modulename' => $pluginname));
209 $DB->delete_records('event', ['component' => $component]);
211 // Delete scheduled tasks.
212 $DB->delete_records('task_scheduled', array('component' => $component));
214 // Delete Inbound Message datakeys.
215 $DB->delete_records_select('messageinbound_datakeys',
216 'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
218 // Delete Inbound Message handlers.
219 $DB->delete_records('messageinbound_handlers', array('component' => $component));
221 // delete all the logs
222 $DB->delete_records('log', array('module' => $pluginname));
224 // delete log_display information
225 $DB->delete_records('log_display', array('component' => $component));
227 // delete the module configuration records
228 unset_all_config_for_plugin($component);
229 if ($type === 'mod') {
230 unset_all_config_for_plugin($pluginname);
233 // delete message provider
234 message_provider_uninstall($component);
236 // delete the plugin tables
237 $xmldbfilepath = $plugindirectory . '/db/install.xml';
238 drop_plugin_tables($component, $xmldbfilepath, false);
239 if ($type === 'mod' or $type === 'block') {
240 // non-frankenstyle table prefixes
241 drop_plugin_tables($name, $xmldbfilepath, false);
244 // delete the capabilities that were defined by this module
245 capabilities_cleanup($component);
247 // Delete all remaining files in the filepool owned by the component.
248 $fs = get_file_storage();
249 $fs->delete_component_files($component);
251 // Finally purge all caches.
252 purge_all_caches();
254 // Invalidate the hash used for upgrade detections.
255 set_config('allversionshash', '');
257 echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
261 * Returns the version of installed component
263 * @param string $component component name
264 * @param string $source either 'disk' or 'installed' - where to get the version information from
265 * @return string|bool version number or false if the component is not found
267 function get_component_version($component, $source='installed') {
268 global $CFG, $DB;
270 list($type, $name) = core_component::normalize_component($component);
272 // moodle core or a core subsystem
273 if ($type === 'core') {
274 if ($source === 'installed') {
275 if (empty($CFG->version)) {
276 return false;
277 } else {
278 return $CFG->version;
280 } else {
281 if (!is_readable($CFG->dirroot.'/version.php')) {
282 return false;
283 } else {
284 $version = null; //initialize variable for IDEs
285 include($CFG->dirroot.'/version.php');
286 return $version;
291 // activity module
292 if ($type === 'mod') {
293 if ($source === 'installed') {
294 if ($CFG->version < 2013092001.02) {
295 return $DB->get_field('modules', 'version', array('name'=>$name));
296 } else {
297 return get_config('mod_'.$name, 'version');
300 } else {
301 $mods = core_component::get_plugin_list('mod');
302 if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
303 return false;
304 } else {
305 $plugin = new stdClass();
306 $plugin->version = null;
307 $module = $plugin;
308 include($mods[$name].'/version.php');
309 return $plugin->version;
314 // block
315 if ($type === 'block') {
316 if ($source === 'installed') {
317 if ($CFG->version < 2013092001.02) {
318 return $DB->get_field('block', 'version', array('name'=>$name));
319 } else {
320 return get_config('block_'.$name, 'version');
322 } else {
323 $blocks = core_component::get_plugin_list('block');
324 if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
325 return false;
326 } else {
327 $plugin = new stdclass();
328 include($blocks[$name].'/version.php');
329 return $plugin->version;
334 // all other plugin types
335 if ($source === 'installed') {
336 return get_config($type.'_'.$name, 'version');
337 } else {
338 $plugins = core_component::get_plugin_list($type);
339 if (empty($plugins[$name])) {
340 return false;
341 } else {
342 $plugin = new stdclass();
343 include($plugins[$name].'/version.php');
344 return $plugin->version;
350 * Delete all plugin tables
352 * @param string $name Name of plugin, used as table prefix
353 * @param string $file Path to install.xml file
354 * @param bool $feedback defaults to true
355 * @return bool Always returns true
357 function drop_plugin_tables($name, $file, $feedback=true) {
358 global $CFG, $DB;
360 // first try normal delete
361 if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
362 return true;
365 // then try to find all tables that start with name and are not in any xml file
366 $used_tables = get_used_table_names();
368 $tables = $DB->get_tables();
370 /// Iterate over, fixing id fields as necessary
371 foreach ($tables as $table) {
372 if (in_array($table, $used_tables)) {
373 continue;
376 if (strpos($table, $name) !== 0) {
377 continue;
380 // found orphan table --> delete it
381 if ($DB->get_manager()->table_exists($table)) {
382 $xmldb_table = new xmldb_table($table);
383 $DB->get_manager()->drop_table($xmldb_table);
387 return true;
391 * Returns names of all known tables == tables that moodle knows about.
393 * @return array Array of lowercase table names
395 function get_used_table_names() {
396 $table_names = array();
397 $dbdirs = get_db_directories();
399 foreach ($dbdirs as $dbdir) {
400 $file = $dbdir.'/install.xml';
402 $xmldb_file = new xmldb_file($file);
404 if (!$xmldb_file->fileExists()) {
405 continue;
408 $loaded = $xmldb_file->loadXMLStructure();
409 $structure = $xmldb_file->getStructure();
411 if ($loaded and $tables = $structure->getTables()) {
412 foreach($tables as $table) {
413 $table_names[] = strtolower($table->getName());
418 return $table_names;
422 * Returns list of all directories where we expect install.xml files
423 * @return array Array of paths
425 function get_db_directories() {
426 global $CFG;
428 $dbdirs = array();
430 /// First, the main one (lib/db)
431 $dbdirs[] = $CFG->libdir.'/db';
433 /// Then, all the ones defined by core_component::get_plugin_types()
434 $plugintypes = core_component::get_plugin_types();
435 foreach ($plugintypes as $plugintype => $pluginbasedir) {
436 if ($plugins = core_component::get_plugin_list($plugintype)) {
437 foreach ($plugins as $plugin => $plugindir) {
438 $dbdirs[] = $plugindir.'/db';
443 return $dbdirs;
447 * Try to obtain or release the cron lock.
448 * @param string $name name of lock
449 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
450 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
451 * @return bool true if lock obtained
453 function set_cron_lock($name, $until, $ignorecurrent=false) {
454 global $DB;
455 if (empty($name)) {
456 debugging("Tried to get a cron lock for a null fieldname");
457 return false;
460 // remove lock by force == remove from config table
461 if (is_null($until)) {
462 set_config($name, null);
463 return true;
466 if (!$ignorecurrent) {
467 // read value from db - other processes might have changed it
468 $value = $DB->get_field('config', 'value', array('name'=>$name));
470 if ($value and $value > time()) {
471 //lock active
472 return false;
476 set_config($name, $until);
477 return true;
481 * Test if and critical warnings are present
482 * @return bool
484 function admin_critical_warnings_present() {
485 global $SESSION;
487 if (!has_capability('moodle/site:config', context_system::instance())) {
488 return 0;
491 if (!isset($SESSION->admin_critical_warning)) {
492 $SESSION->admin_critical_warning = 0;
493 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
494 $SESSION->admin_critical_warning = 1;
498 return $SESSION->admin_critical_warning;
502 * Detects if float supports at least 10 decimal digits
504 * Detects if float supports at least 10 decimal digits
505 * and also if float-->string conversion works as expected.
507 * @return bool true if problem found
509 function is_float_problem() {
510 $num1 = 2009010200.01;
511 $num2 = 2009010200.02;
513 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
517 * Try to verify that dataroot is not accessible from web.
519 * Try to verify that dataroot is not accessible from web.
520 * It is not 100% correct but might help to reduce number of vulnerable sites.
521 * Protection from httpd.conf and .htaccess is not detected properly.
523 * @uses INSECURE_DATAROOT_WARNING
524 * @uses INSECURE_DATAROOT_ERROR
525 * @param bool $fetchtest try to test public access by fetching file, default false
526 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
528 function is_dataroot_insecure($fetchtest=false) {
529 global $CFG;
531 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
533 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
534 $rp = strrev(trim($rp, '/'));
535 $rp = explode('/', $rp);
536 foreach($rp as $r) {
537 if (strpos($siteroot, '/'.$r.'/') === 0) {
538 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
539 } else {
540 break; // probably alias root
544 $siteroot = strrev($siteroot);
545 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
547 if (strpos($dataroot, $siteroot) !== 0) {
548 return false;
551 if (!$fetchtest) {
552 return INSECURE_DATAROOT_WARNING;
555 // now try all methods to fetch a test file using http protocol
557 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
558 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
559 $httpdocroot = $matches[1];
560 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
561 make_upload_directory('diag');
562 $testfile = $CFG->dataroot.'/diag/public.txt';
563 if (!file_exists($testfile)) {
564 file_put_contents($testfile, 'test file, do not delete');
565 @chmod($testfile, $CFG->filepermissions);
567 $teststr = trim(file_get_contents($testfile));
568 if (empty($teststr)) {
569 // hmm, strange
570 return INSECURE_DATAROOT_WARNING;
573 $testurl = $datarooturl.'/diag/public.txt';
574 if (extension_loaded('curl') and
575 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
576 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
577 ($ch = @curl_init($testurl)) !== false) {
578 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
579 curl_setopt($ch, CURLOPT_HEADER, false);
580 $data = curl_exec($ch);
581 if (!curl_errno($ch)) {
582 $data = trim($data);
583 if ($data === $teststr) {
584 curl_close($ch);
585 return INSECURE_DATAROOT_ERROR;
588 curl_close($ch);
591 if ($data = @file_get_contents($testurl)) {
592 $data = trim($data);
593 if ($data === $teststr) {
594 return INSECURE_DATAROOT_ERROR;
598 preg_match('|https?://([^/]+)|i', $testurl, $matches);
599 $sitename = $matches[1];
600 $error = 0;
601 if ($fp = @fsockopen($sitename, 80, $error)) {
602 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
603 $localurl = $matches[1];
604 $out = "GET $localurl HTTP/1.1\r\n";
605 $out .= "Host: $sitename\r\n";
606 $out .= "Connection: Close\r\n\r\n";
607 fwrite($fp, $out);
608 $data = '';
609 $incoming = false;
610 while (!feof($fp)) {
611 if ($incoming) {
612 $data .= fgets($fp, 1024);
613 } else if (@fgets($fp, 1024) === "\r\n") {
614 $incoming = true;
617 fclose($fp);
618 $data = trim($data);
619 if ($data === $teststr) {
620 return INSECURE_DATAROOT_ERROR;
624 return INSECURE_DATAROOT_WARNING;
628 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
630 function enable_cli_maintenance_mode() {
631 global $CFG, $SITE;
633 if (file_exists("$CFG->dataroot/climaintenance.html")) {
634 unlink("$CFG->dataroot/climaintenance.html");
637 if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
638 $data = $CFG->maintenance_message;
639 $data = bootstrap_renderer::early_error_content($data, null, null, null);
640 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
642 } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
643 $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
645 } else {
646 $data = get_string('sitemaintenance', 'admin');
647 $data = bootstrap_renderer::early_error_content($data, null, null, null);
648 $data = bootstrap_renderer::plain_page(get_string('sitemaintenancetitle', 'admin', $SITE->fullname), $data);
651 file_put_contents("$CFG->dataroot/climaintenance.html", $data);
652 chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
655 /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
659 * Interface for anything appearing in the admin tree
661 * The interface that is implemented by anything that appears in the admin tree
662 * block. It forces inheriting classes to define a method for checking user permissions
663 * and methods for finding something in the admin tree.
665 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
667 interface part_of_admin_tree {
670 * Finds a named part_of_admin_tree.
672 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
673 * and not parentable_part_of_admin_tree, then this function should only check if
674 * $this->name matches $name. If it does, it should return a reference to $this,
675 * otherwise, it should return a reference to NULL.
677 * If a class inherits parentable_part_of_admin_tree, this method should be called
678 * recursively on all child objects (assuming, of course, the parent object's name
679 * doesn't match the search criterion).
681 * @param string $name The internal name of the part_of_admin_tree we're searching for.
682 * @return mixed An object reference or a NULL reference.
684 public function locate($name);
687 * Removes named part_of_admin_tree.
689 * @param string $name The internal name of the part_of_admin_tree we want to remove.
690 * @return bool success.
692 public function prune($name);
695 * Search using query
696 * @param string $query
697 * @return mixed array-object structure of found settings and pages
699 public function search($query);
702 * Verifies current user's access to this part_of_admin_tree.
704 * Used to check if the current user has access to this part of the admin tree or
705 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
706 * then this method is usually just a call to has_capability() in the site context.
708 * If a class inherits parentable_part_of_admin_tree, this method should return the
709 * logical OR of the return of check_access() on all child objects.
711 * @return bool True if the user has access, false if she doesn't.
713 public function check_access();
716 * Mostly useful for removing of some parts of the tree in admin tree block.
718 * @return True is hidden from normal list view
720 public function is_hidden();
723 * Show we display Save button at the page bottom?
724 * @return bool
726 public function show_save();
731 * Interface implemented by any part_of_admin_tree that has children.
733 * The interface implemented by any part_of_admin_tree that can be a parent
734 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
735 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
736 * include an add method for adding other part_of_admin_tree objects as children.
738 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
740 interface parentable_part_of_admin_tree extends part_of_admin_tree {
743 * Adds a part_of_admin_tree object to the admin tree.
745 * Used to add a part_of_admin_tree object to this object or a child of this
746 * object. $something should only be added if $destinationname matches
747 * $this->name. If it doesn't, add should be called on child objects that are
748 * also parentable_part_of_admin_tree's.
750 * $something should be appended as the last child in the $destinationname. If the
751 * $beforesibling is specified, $something should be prepended to it. If the given
752 * sibling is not found, $something should be appended to the end of $destinationname
753 * and a developer debugging message should be displayed.
755 * @param string $destinationname The internal name of the new parent for $something.
756 * @param part_of_admin_tree $something The object to be added.
757 * @return bool True on success, false on failure.
759 public function add($destinationname, $something, $beforesibling = null);
765 * The object used to represent folders (a.k.a. categories) in the admin tree block.
767 * Each admin_category object contains a number of part_of_admin_tree objects.
769 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
771 class admin_category implements parentable_part_of_admin_tree {
773 /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
774 protected $children;
775 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
776 public $name;
777 /** @var string The displayed name for this category. Usually obtained through get_string() */
778 public $visiblename;
779 /** @var bool Should this category be hidden in admin tree block? */
780 public $hidden;
781 /** @var mixed Either a string or an array or strings */
782 public $path;
783 /** @var mixed Either a string or an array or strings */
784 public $visiblepath;
786 /** @var array fast lookup category cache, all categories of one tree point to one cache */
787 protected $category_cache;
789 /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
790 protected $sort = false;
791 /** @var bool If set to true children will be sorted in ascending order. */
792 protected $sortasc = true;
793 /** @var bool If set to true sub categories and pages will be split and then sorted.. */
794 protected $sortsplit = true;
795 /** @var bool $sorted True if the children have been sorted and don't need resorting */
796 protected $sorted = false;
799 * Constructor for an empty admin category
801 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
802 * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
803 * @param bool $hidden hide category in admin tree block, defaults to false
805 public function __construct($name, $visiblename, $hidden=false) {
806 $this->children = array();
807 $this->name = $name;
808 $this->visiblename = $visiblename;
809 $this->hidden = $hidden;
813 * Returns a reference to the part_of_admin_tree object with internal name $name.
815 * @param string $name The internal name of the object we want.
816 * @param bool $findpath initialize path and visiblepath arrays
817 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
818 * defaults to false
820 public function locate($name, $findpath=false) {
821 if (!isset($this->category_cache[$this->name])) {
822 // somebody much have purged the cache
823 $this->category_cache[$this->name] = $this;
826 if ($this->name == $name) {
827 if ($findpath) {
828 $this->visiblepath[] = $this->visiblename;
829 $this->path[] = $this->name;
831 return $this;
834 // quick category lookup
835 if (!$findpath and isset($this->category_cache[$name])) {
836 return $this->category_cache[$name];
839 $return = NULL;
840 foreach($this->children as $childid=>$unused) {
841 if ($return = $this->children[$childid]->locate($name, $findpath)) {
842 break;
846 if (!is_null($return) and $findpath) {
847 $return->visiblepath[] = $this->visiblename;
848 $return->path[] = $this->name;
851 return $return;
855 * Search using query
857 * @param string query
858 * @return mixed array-object structure of found settings and pages
860 public function search($query) {
861 $result = array();
862 foreach ($this->get_children() as $child) {
863 $subsearch = $child->search($query);
864 if (!is_array($subsearch)) {
865 debugging('Incorrect search result from '.$child->name);
866 continue;
868 $result = array_merge($result, $subsearch);
870 return $result;
874 * Removes part_of_admin_tree object with internal name $name.
876 * @param string $name The internal name of the object we want to remove.
877 * @return bool success
879 public function prune($name) {
881 if ($this->name == $name) {
882 return false; //can not remove itself
885 foreach($this->children as $precedence => $child) {
886 if ($child->name == $name) {
887 // clear cache and delete self
888 while($this->category_cache) {
889 // delete the cache, but keep the original array address
890 array_pop($this->category_cache);
892 unset($this->children[$precedence]);
893 return true;
894 } else if ($this->children[$precedence]->prune($name)) {
895 return true;
898 return false;
902 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
904 * By default the new part of the tree is appended as the last child of the parent. You
905 * can specify a sibling node that the new part should be prepended to. If the given
906 * sibling is not found, the part is appended to the end (as it would be by default) and
907 * a developer debugging message is displayed.
909 * @throws coding_exception if the $beforesibling is empty string or is not string at all.
910 * @param string $destinationame The internal name of the immediate parent that we want for $something.
911 * @param mixed $something A part_of_admin_tree or setting instance to be added.
912 * @param string $beforesibling The name of the parent's child the $something should be prepended to.
913 * @return bool True if successfully added, false if $something can not be added.
915 public function add($parentname, $something, $beforesibling = null) {
916 global $CFG;
918 $parent = $this->locate($parentname);
919 if (is_null($parent)) {
920 debugging('parent does not exist!');
921 return false;
924 if ($something instanceof part_of_admin_tree) {
925 if (!($parent instanceof parentable_part_of_admin_tree)) {
926 debugging('error - parts of tree can be inserted only into parentable parts');
927 return false;
929 if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
930 // The name of the node is already used, simply warn the developer that this should not happen.
931 // It is intentional to check for the debug level before performing the check.
932 debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
934 if (is_null($beforesibling)) {
935 // Append $something as the parent's last child.
936 $parent->children[] = $something;
937 } else {
938 if (!is_string($beforesibling) or trim($beforesibling) === '') {
939 throw new coding_exception('Unexpected value of the beforesibling parameter');
941 // Try to find the position of the sibling.
942 $siblingposition = null;
943 foreach ($parent->children as $childposition => $child) {
944 if ($child->name === $beforesibling) {
945 $siblingposition = $childposition;
946 break;
949 if (is_null($siblingposition)) {
950 debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
951 $parent->children[] = $something;
952 } else {
953 $parent->children = array_merge(
954 array_slice($parent->children, 0, $siblingposition),
955 array($something),
956 array_slice($parent->children, $siblingposition)
960 if ($something instanceof admin_category) {
961 if (isset($this->category_cache[$something->name])) {
962 debugging('Duplicate admin category name: '.$something->name);
963 } else {
964 $this->category_cache[$something->name] = $something;
965 $something->category_cache =& $this->category_cache;
966 foreach ($something->children as $child) {
967 // just in case somebody already added subcategories
968 if ($child instanceof admin_category) {
969 if (isset($this->category_cache[$child->name])) {
970 debugging('Duplicate admin category name: '.$child->name);
971 } else {
972 $this->category_cache[$child->name] = $child;
973 $child->category_cache =& $this->category_cache;
979 return true;
981 } else {
982 debugging('error - can not add this element');
983 return false;
989 * Checks if the user has access to anything in this category.
991 * @return bool True if the user has access to at least one child in this category, false otherwise.
993 public function check_access() {
994 foreach ($this->children as $child) {
995 if ($child->check_access()) {
996 return true;
999 return false;
1003 * Is this category hidden in admin tree block?
1005 * @return bool True if hidden
1007 public function is_hidden() {
1008 return $this->hidden;
1012 * Show we display Save button at the page bottom?
1013 * @return bool
1015 public function show_save() {
1016 foreach ($this->children as $child) {
1017 if ($child->show_save()) {
1018 return true;
1021 return false;
1025 * Sets sorting on this category.
1027 * Please note this function doesn't actually do the sorting.
1028 * It can be called anytime.
1029 * Sorting occurs when the user calls get_children.
1030 * Code using the children array directly won't see the sorted results.
1032 * @param bool $sort If set to true children will be sorted, if false they won't be.
1033 * @param bool $asc If true sorting will be ascending, otherwise descending.
1034 * @param bool $split If true we sort pages and sub categories separately.
1036 public function set_sorting($sort, $asc = true, $split = true) {
1037 $this->sort = (bool)$sort;
1038 $this->sortasc = (bool)$asc;
1039 $this->sortsplit = (bool)$split;
1043 * Returns the children associated with this category.
1045 * @return part_of_admin_tree[]
1047 public function get_children() {
1048 // If we should sort and it hasn't already been sorted.
1049 if ($this->sort && !$this->sorted) {
1050 if ($this->sortsplit) {
1051 $categories = array();
1052 $pages = array();
1053 foreach ($this->children as $child) {
1054 if ($child instanceof admin_category) {
1055 $categories[] = $child;
1056 } else {
1057 $pages[] = $child;
1060 core_collator::asort_objects_by_property($categories, 'visiblename');
1061 core_collator::asort_objects_by_property($pages, 'visiblename');
1062 if (!$this->sortasc) {
1063 $categories = array_reverse($categories);
1064 $pages = array_reverse($pages);
1066 $this->children = array_merge($pages, $categories);
1067 } else {
1068 core_collator::asort_objects_by_property($this->children, 'visiblename');
1069 if (!$this->sortasc) {
1070 $this->children = array_reverse($this->children);
1073 $this->sorted = true;
1075 return $this->children;
1079 * Magically gets a property from this object.
1081 * @param $property
1082 * @return part_of_admin_tree[]
1083 * @throws coding_exception
1085 public function __get($property) {
1086 if ($property === 'children') {
1087 return $this->get_children();
1089 throw new coding_exception('Invalid property requested.');
1093 * Magically sets a property against this object.
1095 * @param string $property
1096 * @param mixed $value
1097 * @throws coding_exception
1099 public function __set($property, $value) {
1100 if ($property === 'children') {
1101 $this->sorted = false;
1102 $this->children = $value;
1103 } else {
1104 throw new coding_exception('Invalid property requested.');
1109 * Checks if an inaccessible property is set.
1111 * @param string $property
1112 * @return bool
1113 * @throws coding_exception
1115 public function __isset($property) {
1116 if ($property === 'children') {
1117 return isset($this->children);
1119 throw new coding_exception('Invalid property requested.');
1125 * Root of admin settings tree, does not have any parent.
1127 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1129 class admin_root extends admin_category {
1130 /** @var array List of errors */
1131 public $errors;
1132 /** @var string search query */
1133 public $search;
1134 /** @var bool full tree flag - true means all settings required, false only pages required */
1135 public $fulltree;
1136 /** @var bool flag indicating loaded tree */
1137 public $loaded;
1138 /** @var mixed site custom defaults overriding defaults in settings files*/
1139 public $custom_defaults;
1142 * @param bool $fulltree true means all settings required,
1143 * false only pages required
1145 public function __construct($fulltree) {
1146 global $CFG;
1148 parent::__construct('root', get_string('administration'), false);
1149 $this->errors = array();
1150 $this->search = '';
1151 $this->fulltree = $fulltree;
1152 $this->loaded = false;
1154 $this->category_cache = array();
1156 // load custom defaults if found
1157 $this->custom_defaults = null;
1158 $defaultsfile = "$CFG->dirroot/local/defaults.php";
1159 if (is_readable($defaultsfile)) {
1160 $defaults = array();
1161 include($defaultsfile);
1162 if (is_array($defaults) and count($defaults)) {
1163 $this->custom_defaults = $defaults;
1169 * Empties children array, and sets loaded to false
1171 * @param bool $requirefulltree
1173 public function purge_children($requirefulltree) {
1174 $this->children = array();
1175 $this->fulltree = ($requirefulltree || $this->fulltree);
1176 $this->loaded = false;
1177 //break circular dependencies - this helps PHP 5.2
1178 while($this->category_cache) {
1179 array_pop($this->category_cache);
1181 $this->category_cache = array();
1187 * Links external PHP pages into the admin tree.
1189 * See detailed usage example at the top of this document (adminlib.php)
1191 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1193 class admin_externalpage implements part_of_admin_tree {
1195 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1196 public $name;
1198 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1199 public $visiblename;
1201 /** @var string The external URL that we should link to when someone requests this external page. */
1202 public $url;
1204 /** @var array The role capability/permission a user must have to access this external page. */
1205 public $req_capability;
1207 /** @var object The context in which capability/permission should be checked, default is site context. */
1208 public $context;
1210 /** @var bool hidden in admin tree block. */
1211 public $hidden;
1213 /** @var mixed either string or array of string */
1214 public $path;
1216 /** @var array list of visible names of page parents */
1217 public $visiblepath;
1220 * Constructor for adding an external page into the admin tree.
1222 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1223 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1224 * @param string $url The external URL that we should link to when someone requests this external page.
1225 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1226 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1227 * @param stdClass $context The context the page relates to. Not sure what happens
1228 * if you specify something other than system or front page. Defaults to system.
1230 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1231 $this->name = $name;
1232 $this->visiblename = $visiblename;
1233 $this->url = $url;
1234 if (is_array($req_capability)) {
1235 $this->req_capability = $req_capability;
1236 } else {
1237 $this->req_capability = array($req_capability);
1239 $this->hidden = $hidden;
1240 $this->context = $context;
1244 * Returns a reference to the part_of_admin_tree object with internal name $name.
1246 * @param string $name The internal name of the object we want.
1247 * @param bool $findpath defaults to false
1248 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1250 public function locate($name, $findpath=false) {
1251 if ($this->name == $name) {
1252 if ($findpath) {
1253 $this->visiblepath = array($this->visiblename);
1254 $this->path = array($this->name);
1256 return $this;
1257 } else {
1258 $return = NULL;
1259 return $return;
1264 * This function always returns false, required function by interface
1266 * @param string $name
1267 * @return false
1269 public function prune($name) {
1270 return false;
1274 * Search using query
1276 * @param string $query
1277 * @return mixed array-object structure of found settings and pages
1279 public function search($query) {
1280 $found = false;
1281 if (strpos(strtolower($this->name), $query) !== false) {
1282 $found = true;
1283 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1284 $found = true;
1286 if ($found) {
1287 $result = new stdClass();
1288 $result->page = $this;
1289 $result->settings = array();
1290 return array($this->name => $result);
1291 } else {
1292 return array();
1297 * Determines if the current user has access to this external page based on $this->req_capability.
1299 * @return bool True if user has access, false otherwise.
1301 public function check_access() {
1302 global $CFG;
1303 $context = empty($this->context) ? context_system::instance() : $this->context;
1304 foreach($this->req_capability as $cap) {
1305 if (has_capability($cap, $context)) {
1306 return true;
1309 return false;
1313 * Is this external page hidden in admin tree block?
1315 * @return bool True if hidden
1317 public function is_hidden() {
1318 return $this->hidden;
1322 * Show we display Save button at the page bottom?
1323 * @return bool
1325 public function show_save() {
1326 return false;
1331 * Used to store details of the dependency between two settings elements.
1333 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1334 * @copyright 2017 Davo Smith, Synergy Learning
1336 class admin_settingdependency {
1337 /** @var string the name of the setting to be shown/hidden */
1338 public $settingname;
1339 /** @var string the setting this is dependent on */
1340 public $dependenton;
1341 /** @var string the condition to show/hide the element */
1342 public $condition;
1343 /** @var string the value to compare against */
1344 public $value;
1346 /** @var string[] list of valid conditions */
1347 private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
1350 * admin_settingdependency constructor.
1351 * @param string $settingname
1352 * @param string $dependenton
1353 * @param string $condition
1354 * @param string $value
1355 * @throws \coding_exception
1357 public function __construct($settingname, $dependenton, $condition, $value) {
1358 $this->settingname = $this->parse_name($settingname);
1359 $this->dependenton = $this->parse_name($dependenton);
1360 $this->condition = $condition;
1361 $this->value = $value;
1363 if (!in_array($this->condition, self::$validconditions)) {
1364 throw new coding_exception("Invalid condition '$condition'");
1369 * Convert the setting name into the form field name.
1370 * @param string $name
1371 * @return string
1373 private function parse_name($name) {
1374 $bits = explode('/', $name);
1375 $name = array_pop($bits);
1376 $plugin = '';
1377 if ($bits) {
1378 $plugin = array_pop($bits);
1379 if ($plugin === 'moodle') {
1380 $plugin = '';
1383 return 's_'.$plugin.'_'.$name;
1387 * Gather together all the dependencies in a format suitable for initialising javascript
1388 * @param admin_settingdependency[] $dependencies
1389 * @return array
1391 public static function prepare_for_javascript($dependencies) {
1392 $result = [];
1393 foreach ($dependencies as $d) {
1394 if (!isset($result[$d->dependenton])) {
1395 $result[$d->dependenton] = [];
1397 if (!isset($result[$d->dependenton][$d->condition])) {
1398 $result[$d->dependenton][$d->condition] = [];
1400 if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
1401 $result[$d->dependenton][$d->condition][$d->value] = [];
1403 $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
1405 return $result;
1410 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1412 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1414 class admin_settingpage implements part_of_admin_tree {
1416 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1417 public $name;
1419 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1420 public $visiblename;
1422 /** @var mixed An array of admin_setting objects that are part of this setting page. */
1423 public $settings;
1425 /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
1426 protected $dependencies = [];
1428 /** @var array The role capability/permission a user must have to access this external page. */
1429 public $req_capability;
1431 /** @var object The context in which capability/permission should be checked, default is site context. */
1432 public $context;
1434 /** @var bool hidden in admin tree block. */
1435 public $hidden;
1437 /** @var mixed string of paths or array of strings of paths */
1438 public $path;
1440 /** @var array list of visible names of page parents */
1441 public $visiblepath;
1444 * see admin_settingpage for details of this function
1446 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1447 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1448 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1449 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1450 * @param stdClass $context The context the page relates to. Not sure what happens
1451 * if you specify something other than system or front page. Defaults to system.
1453 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1454 $this->settings = new stdClass();
1455 $this->name = $name;
1456 $this->visiblename = $visiblename;
1457 if (is_array($req_capability)) {
1458 $this->req_capability = $req_capability;
1459 } else {
1460 $this->req_capability = array($req_capability);
1462 $this->hidden = $hidden;
1463 $this->context = $context;
1467 * see admin_category
1469 * @param string $name
1470 * @param bool $findpath
1471 * @return mixed Object (this) if name == this->name, else returns null
1473 public function locate($name, $findpath=false) {
1474 if ($this->name == $name) {
1475 if ($findpath) {
1476 $this->visiblepath = array($this->visiblename);
1477 $this->path = array($this->name);
1479 return $this;
1480 } else {
1481 $return = NULL;
1482 return $return;
1487 * Search string in settings page.
1489 * @param string $query
1490 * @return array
1492 public function search($query) {
1493 $found = array();
1495 foreach ($this->settings as $setting) {
1496 if ($setting->is_related($query)) {
1497 $found[] = $setting;
1501 if ($found) {
1502 $result = new stdClass();
1503 $result->page = $this;
1504 $result->settings = $found;
1505 return array($this->name => $result);
1508 $found = false;
1509 if (strpos(strtolower($this->name), $query) !== false) {
1510 $found = true;
1511 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1512 $found = true;
1514 if ($found) {
1515 $result = new stdClass();
1516 $result->page = $this;
1517 $result->settings = array();
1518 return array($this->name => $result);
1519 } else {
1520 return array();
1525 * This function always returns false, required by interface
1527 * @param string $name
1528 * @return bool Always false
1530 public function prune($name) {
1531 return false;
1535 * adds an admin_setting to this admin_settingpage
1537 * 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
1538 * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1540 * @param object $setting is the admin_setting object you want to add
1541 * @return bool true if successful, false if not
1543 public function add($setting) {
1544 if (!($setting instanceof admin_setting)) {
1545 debugging('error - not a setting instance');
1546 return false;
1549 $name = $setting->name;
1550 if ($setting->plugin) {
1551 $name = $setting->plugin . $name;
1553 $this->settings->{$name} = $setting;
1554 return true;
1558 * Hide the named setting if the specified condition is matched.
1560 * @param string $settingname
1561 * @param string $dependenton
1562 * @param string $condition
1563 * @param string $value
1565 public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
1566 $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
1568 // Reformat the dependency name to the plugin | name format used in the display.
1569 $dependenton = str_replace('/', ' | ', $dependenton);
1571 // Let the setting know, so it can be displayed underneath.
1572 $findname = str_replace('/', '', $settingname);
1573 foreach ($this->settings as $name => $setting) {
1574 if ($name === $findname) {
1575 $setting->add_dependent_on($dependenton);
1581 * see admin_externalpage
1583 * @return bool Returns true for yes false for no
1585 public function check_access() {
1586 global $CFG;
1587 $context = empty($this->context) ? context_system::instance() : $this->context;
1588 foreach($this->req_capability as $cap) {
1589 if (has_capability($cap, $context)) {
1590 return true;
1593 return false;
1597 * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1598 * @return string Returns an XHTML string
1600 public function output_html() {
1601 $adminroot = admin_get_root();
1602 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1603 foreach($this->settings as $setting) {
1604 $fullname = $setting->get_full_name();
1605 if (array_key_exists($fullname, $adminroot->errors)) {
1606 $data = $adminroot->errors[$fullname]->data;
1607 } else {
1608 $data = $setting->get_setting();
1609 // do not use defaults if settings not available - upgrade settings handles the defaults!
1611 $return .= $setting->output_html($data);
1613 $return .= '</fieldset>';
1614 return $return;
1618 * Is this settings page hidden in admin tree block?
1620 * @return bool True if hidden
1622 public function is_hidden() {
1623 return $this->hidden;
1627 * Show we display Save button at the page bottom?
1628 * @return bool
1630 public function show_save() {
1631 foreach($this->settings as $setting) {
1632 if (empty($setting->nosave)) {
1633 return true;
1636 return false;
1640 * Should any of the settings on this page be shown / hidden based on conditions?
1641 * @return bool
1643 public function has_dependencies() {
1644 return (bool)$this->dependencies;
1648 * Format the setting show/hide conditions ready to initialise the page javascript
1649 * @return array
1651 public function get_dependencies_for_javascript() {
1652 if (!$this->has_dependencies()) {
1653 return [];
1655 return admin_settingdependency::prepare_for_javascript($this->dependencies);
1661 * Admin settings class. Only exists on setting pages.
1662 * Read & write happens at this level; no authentication.
1664 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1666 abstract class admin_setting {
1667 /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1668 public $name;
1669 /** @var string localised name */
1670 public $visiblename;
1671 /** @var string localised long description in Markdown format */
1672 public $description;
1673 /** @var mixed Can be string or array of string */
1674 public $defaultsetting;
1675 /** @var string */
1676 public $updatedcallback;
1677 /** @var mixed can be String or Null. Null means main config table */
1678 public $plugin; // null means main config table
1679 /** @var bool true indicates this setting does not actually save anything, just information */
1680 public $nosave = false;
1681 /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1682 public $affectsmodinfo = false;
1683 /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1684 private $flags = array();
1685 /** @var bool Whether this field must be forced LTR. */
1686 private $forceltr = null;
1687 /** @var array list of other settings that may cause this setting to be hidden */
1688 private $dependenton = [];
1689 /** @var bool Whether this setting uses a custom form control */
1690 protected $customcontrol = false;
1693 * Constructor
1694 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1695 * or 'myplugin/mysetting' for ones in config_plugins.
1696 * @param string $visiblename localised name
1697 * @param string $description localised long description
1698 * @param mixed $defaultsetting string or array depending on implementation
1700 public function __construct($name, $visiblename, $description, $defaultsetting) {
1701 $this->parse_setting_name($name);
1702 $this->visiblename = $visiblename;
1703 $this->description = $description;
1704 $this->defaultsetting = $defaultsetting;
1708 * Generic function to add a flag to this admin setting.
1710 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1711 * @param bool $default - The default for the flag
1712 * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1713 * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1715 protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1716 if (empty($this->flags[$shortname])) {
1717 $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1718 } else {
1719 $this->flags[$shortname]->set_options($enabled, $default);
1724 * Set the enabled options flag on this admin setting.
1726 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1727 * @param bool $default - The default for the flag
1729 public function set_enabled_flag_options($enabled, $default) {
1730 $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1734 * Set the advanced options flag on this admin setting.
1736 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1737 * @param bool $default - The default for the flag
1739 public function set_advanced_flag_options($enabled, $default) {
1740 $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1745 * Set the locked options flag on this admin setting.
1747 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1748 * @param bool $default - The default for the flag
1750 public function set_locked_flag_options($enabled, $default) {
1751 $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1755 * Set the required options flag on this admin setting.
1757 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
1758 * @param bool $default - The default for the flag.
1760 public function set_required_flag_options($enabled, $default) {
1761 $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
1765 * Is this option forced in config.php?
1767 * @return bool
1769 public function is_readonly(): bool {
1770 global $CFG;
1772 if (empty($this->plugin)) {
1773 if (array_key_exists($this->name, $CFG->config_php_settings)) {
1774 return true;
1776 } else {
1777 if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
1778 and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
1779 return true;
1782 return false;
1786 * Get the currently saved value for a setting flag
1788 * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1789 * @return bool
1791 public function get_setting_flag_value(admin_setting_flag $flag) {
1792 $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1793 if (!isset($value)) {
1794 $value = $flag->get_default();
1797 return !empty($value);
1801 * Get the list of defaults for the flags on this setting.
1803 * @param array of strings describing the defaults for this setting. This is appended to by this function.
1805 public function get_setting_flag_defaults(& $defaults) {
1806 foreach ($this->flags as $flag) {
1807 if ($flag->is_enabled() && $flag->get_default()) {
1808 $defaults[] = $flag->get_displayname();
1814 * Output the input fields for the advanced and locked flags on this setting.
1816 * @param bool $adv - The current value of the advanced flag.
1817 * @param bool $locked - The current value of the locked flag.
1818 * @return string $output - The html for the flags.
1820 public function output_setting_flags() {
1821 $output = '';
1823 foreach ($this->flags as $flag) {
1824 if ($flag->is_enabled()) {
1825 $output .= $flag->output_setting_flag($this);
1829 if (!empty($output)) {
1830 return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1832 return $output;
1836 * Write the values of the flags for this admin setting.
1838 * @param array $data - The data submitted from the form or null to set the default value for new installs.
1839 * @return bool - true if successful.
1841 public function write_setting_flags($data) {
1842 $result = true;
1843 foreach ($this->flags as $flag) {
1844 $result = $result && $flag->write_setting_flag($this, $data);
1846 return $result;
1850 * Set up $this->name and potentially $this->plugin
1852 * Set up $this->name and possibly $this->plugin based on whether $name looks
1853 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1854 * on the names, that is, output a developer debug warning if the name
1855 * contains anything other than [a-zA-Z0-9_]+.
1857 * @param string $name the setting name passed in to the constructor.
1859 private function parse_setting_name($name) {
1860 $bits = explode('/', $name);
1861 if (count($bits) > 2) {
1862 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1864 $this->name = array_pop($bits);
1865 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1866 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1868 if (!empty($bits)) {
1869 $this->plugin = array_pop($bits);
1870 if ($this->plugin === 'moodle') {
1871 $this->plugin = null;
1872 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1873 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1879 * Returns the fullname prefixed by the plugin
1880 * @return string
1882 public function get_full_name() {
1883 return 's_'.$this->plugin.'_'.$this->name;
1887 * Returns the ID string based on plugin and name
1888 * @return string
1890 public function get_id() {
1891 return 'id_s_'.$this->plugin.'_'.$this->name;
1895 * @param bool $affectsmodinfo If true, changes to this setting will
1896 * cause the course cache to be rebuilt
1898 public function set_affects_modinfo($affectsmodinfo) {
1899 $this->affectsmodinfo = $affectsmodinfo;
1903 * Returns the config if possible
1905 * @return mixed returns config if successful else null
1907 public function config_read($name) {
1908 global $CFG;
1909 if (!empty($this->plugin)) {
1910 $value = get_config($this->plugin, $name);
1911 return $value === false ? NULL : $value;
1913 } else {
1914 if (isset($CFG->$name)) {
1915 return $CFG->$name;
1916 } else {
1917 return NULL;
1923 * Used to set a config pair and log change
1925 * @param string $name
1926 * @param mixed $value Gets converted to string if not null
1927 * @return bool Write setting to config table
1929 public function config_write($name, $value) {
1930 global $DB, $USER, $CFG;
1932 if ($this->nosave) {
1933 return true;
1936 // make sure it is a real change
1937 $oldvalue = get_config($this->plugin, $name);
1938 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1939 $value = is_null($value) ? null : (string)$value;
1941 if ($oldvalue === $value) {
1942 return true;
1945 // store change
1946 set_config($name, $value, $this->plugin);
1948 // Some admin settings affect course modinfo
1949 if ($this->affectsmodinfo) {
1950 // Clear course cache for all courses
1951 rebuild_course_cache(0, true);
1954 $this->add_to_config_log($name, $oldvalue, $value);
1956 return true; // BC only
1960 * Log config changes if necessary.
1961 * @param string $name
1962 * @param string $oldvalue
1963 * @param string $value
1965 protected function add_to_config_log($name, $oldvalue, $value) {
1966 add_to_config_log($name, $oldvalue, $value, $this->plugin);
1970 * Returns current value of this setting
1971 * @return mixed array or string depending on instance, NULL means not set yet
1973 public abstract function get_setting();
1976 * Returns default setting if exists
1977 * @return mixed array or string depending on instance; NULL means no default, user must supply
1979 public function get_defaultsetting() {
1980 $adminroot = admin_get_root(false, false);
1981 if (!empty($adminroot->custom_defaults)) {
1982 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1983 if (isset($adminroot->custom_defaults[$plugin])) {
1984 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
1985 return $adminroot->custom_defaults[$plugin][$this->name];
1989 return $this->defaultsetting;
1993 * Store new setting
1995 * @param mixed $data string or array, must not be NULL
1996 * @return string empty string if ok, string error message otherwise
1998 public abstract function write_setting($data);
2001 * Return part of form with setting
2002 * This function should always be overwritten
2004 * @param mixed $data array or string depending on setting
2005 * @param string $query
2006 * @return string
2008 public function output_html($data, $query='') {
2009 // should be overridden
2010 return;
2014 * Function called if setting updated - cleanup, cache reset, etc.
2015 * @param string $functionname Sets the function name
2016 * @return void
2018 public function set_updatedcallback($functionname) {
2019 $this->updatedcallback = $functionname;
2023 * Execute postupdatecallback if necessary.
2024 * @param mixed $original original value before write_setting()
2025 * @return bool true if changed, false if not.
2027 public function post_write_settings($original) {
2028 // Comparison must work for arrays too.
2029 if (serialize($original) === serialize($this->get_setting())) {
2030 return false;
2033 $callbackfunction = $this->updatedcallback;
2034 if (!empty($callbackfunction) and is_callable($callbackfunction)) {
2035 $callbackfunction($this->get_full_name());
2037 return true;
2041 * Is setting related to query text - used when searching
2042 * @param string $query
2043 * @return bool
2045 public function is_related($query) {
2046 if (strpos(strtolower($this->name), $query) !== false) {
2047 return true;
2049 if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
2050 return true;
2052 if (strpos(core_text::strtolower($this->description), $query) !== false) {
2053 return true;
2055 $current = $this->get_setting();
2056 if (!is_null($current)) {
2057 if (is_string($current)) {
2058 if (strpos(core_text::strtolower($current), $query) !== false) {
2059 return true;
2063 $default = $this->get_defaultsetting();
2064 if (!is_null($default)) {
2065 if (is_string($default)) {
2066 if (strpos(core_text::strtolower($default), $query) !== false) {
2067 return true;
2071 return false;
2075 * Get whether this should be displayed in LTR mode.
2077 * @return bool|null
2079 public function get_force_ltr() {
2080 return $this->forceltr;
2084 * Set whether to force LTR or not.
2086 * @param bool $value True when forced, false when not force, null when unknown.
2088 public function set_force_ltr($value) {
2089 $this->forceltr = $value;
2093 * Add a setting to the list of those that could cause this one to be hidden
2094 * @param string $dependenton
2096 public function add_dependent_on($dependenton) {
2097 $this->dependenton[] = $dependenton;
2101 * Get a list of the settings that could cause this one to be hidden.
2102 * @return array
2104 public function get_dependent_on() {
2105 return $this->dependenton;
2109 * Whether this setting uses a custom form control.
2110 * This function is especially useful to decide if we should render a label element for this setting or not.
2112 * @return bool
2114 public function has_custom_form_control(): bool {
2115 return $this->customcontrol;
2120 * An additional option that can be applied to an admin setting.
2121 * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
2123 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2125 class admin_setting_flag {
2126 /** @var bool Flag to indicate if this option can be toggled for this setting */
2127 private $enabled = false;
2128 /** @var bool Flag to indicate if this option defaults to true or false */
2129 private $default = false;
2130 /** @var string Short string used to create setting name - e.g. 'adv' */
2131 private $shortname = '';
2132 /** @var string String used as the label for this flag */
2133 private $displayname = '';
2134 /** @const Checkbox for this flag is displayed in admin page */
2135 const ENABLED = true;
2136 /** @const Checkbox for this flag is not displayed in admin page */
2137 const DISABLED = false;
2140 * Constructor
2142 * @param bool $enabled Can this option can be toggled.
2143 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2144 * @param bool $default The default checked state for this setting option.
2145 * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
2146 * @param string $displayname The displayname of this flag. Used as a label for the flag.
2148 public function __construct($enabled, $default, $shortname, $displayname) {
2149 $this->shortname = $shortname;
2150 $this->displayname = $displayname;
2151 $this->set_options($enabled, $default);
2155 * Update the values of this setting options class
2157 * @param bool $enabled Can this option can be toggled.
2158 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2159 * @param bool $default The default checked state for this setting option.
2161 public function set_options($enabled, $default) {
2162 $this->enabled = $enabled;
2163 $this->default = $default;
2167 * Should this option appear in the interface and be toggleable?
2169 * @return bool Is it enabled?
2171 public function is_enabled() {
2172 return $this->enabled;
2176 * Should this option be checked by default?
2178 * @return bool Is it on by default?
2180 public function get_default() {
2181 return $this->default;
2185 * Return the short name for this flag. e.g. 'adv' or 'locked'
2187 * @return string
2189 public function get_shortname() {
2190 return $this->shortname;
2194 * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
2196 * @return string
2198 public function get_displayname() {
2199 return $this->displayname;
2203 * Save the submitted data for this flag - or set it to the default if $data is null.
2205 * @param admin_setting $setting - The admin setting for this flag
2206 * @param array $data - The data submitted from the form or null to set the default value for new installs.
2207 * @return bool
2209 public function write_setting_flag(admin_setting $setting, $data) {
2210 $result = true;
2211 if ($this->is_enabled()) {
2212 if (!isset($data)) {
2213 $value = $this->get_default();
2214 } else {
2215 $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
2217 $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
2220 return $result;
2225 * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
2227 * @param admin_setting $setting - The admin setting for this flag
2228 * @return string - The html for the checkbox.
2230 public function output_setting_flag(admin_setting $setting) {
2231 global $OUTPUT;
2233 $value = $setting->get_setting_flag_value($this);
2235 $context = new stdClass();
2236 $context->id = $setting->get_id() . '_' . $this->get_shortname();
2237 $context->name = $setting->get_full_name() . '_' . $this->get_shortname();
2238 $context->value = 1;
2239 $context->checked = $value ? true : false;
2240 $context->label = $this->get_displayname();
2242 return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
2248 * No setting - just heading and text.
2250 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2252 class admin_setting_heading extends admin_setting {
2255 * not a setting, just text
2256 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2257 * @param string $heading heading
2258 * @param string $information text in box
2260 public function __construct($name, $heading, $information) {
2261 $this->nosave = true;
2262 parent::__construct($name, $heading, $information, '');
2266 * Always returns true
2267 * @return bool Always returns true
2269 public function get_setting() {
2270 return true;
2274 * Always returns true
2275 * @return bool Always returns true
2277 public function get_defaultsetting() {
2278 return true;
2282 * Never write settings
2283 * @return string Always returns an empty string
2285 public function write_setting($data) {
2286 // do not write any setting
2287 return '';
2291 * Returns an HTML string
2292 * @return string Returns an HTML string
2294 public function output_html($data, $query='') {
2295 global $OUTPUT;
2296 $context = new stdClass();
2297 $context->title = $this->visiblename;
2298 $context->description = $this->description;
2299 $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
2300 return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
2305 * No setting - just name and description in same row.
2307 * @copyright 2018 onwards Amaia Anabitarte
2308 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2310 class admin_setting_description extends admin_setting {
2313 * Not a setting, just text
2315 * @param string $name
2316 * @param string $visiblename
2317 * @param string $description
2319 public function __construct($name, $visiblename, $description) {
2320 $this->nosave = true;
2321 parent::__construct($name, $visiblename, $description, '');
2325 * Always returns true
2327 * @return bool Always returns true
2329 public function get_setting() {
2330 return true;
2334 * Always returns true
2336 * @return bool Always returns true
2338 public function get_defaultsetting() {
2339 return true;
2343 * Never write settings
2345 * @param mixed $data Gets converted to str for comparison against yes value
2346 * @return string Always returns an empty string
2348 public function write_setting($data) {
2349 // Do not write any setting.
2350 return '';
2354 * Returns an HTML string
2356 * @param string $data
2357 * @param string $query
2358 * @return string Returns an HTML string
2360 public function output_html($data, $query='') {
2361 global $OUTPUT;
2363 $context = new stdClass();
2364 $context->title = $this->visiblename;
2365 $context->description = $this->description;
2367 return $OUTPUT->render_from_template('core_admin/setting_description', $context);
2374 * The most flexible setting, the user enters text.
2376 * This type of field should be used for config settings which are using
2377 * English words and are not localised (passwords, database name, list of values, ...).
2379 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2381 class admin_setting_configtext extends admin_setting {
2383 /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
2384 public $paramtype;
2385 /** @var int default field size */
2386 public $size;
2389 * Config text constructor
2391 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2392 * @param string $visiblename localised
2393 * @param string $description long localised info
2394 * @param string $defaultsetting
2395 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2396 * @param int $size default field size
2398 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
2399 $this->paramtype = $paramtype;
2400 if (!is_null($size)) {
2401 $this->size = $size;
2402 } else {
2403 $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
2405 parent::__construct($name, $visiblename, $description, $defaultsetting);
2409 * Get whether this should be displayed in LTR mode.
2411 * Try to guess from the PARAM type unless specifically set.
2413 public function get_force_ltr() {
2414 $forceltr = parent::get_force_ltr();
2415 if ($forceltr === null) {
2416 return !is_rtl_compatible($this->paramtype);
2418 return $forceltr;
2422 * Return the setting
2424 * @return mixed returns config if successful else null
2426 public function get_setting() {
2427 return $this->config_read($this->name);
2430 public function write_setting($data) {
2431 if ($this->paramtype === PARAM_INT and $data === '') {
2432 // do not complain if '' used instead of 0
2433 $data = 0;
2435 // $data is a string
2436 $validated = $this->validate($data);
2437 if ($validated !== true) {
2438 return $validated;
2440 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2444 * Validate data before storage
2445 * @param string data
2446 * @return mixed true if ok string if error found
2448 public function validate($data) {
2449 // allow paramtype to be a custom regex if it is the form of /pattern/
2450 if (preg_match('#^/.*/$#', $this->paramtype)) {
2451 if (preg_match($this->paramtype, $data)) {
2452 return true;
2453 } else {
2454 return get_string('validateerror', 'admin');
2457 } else if ($this->paramtype === PARAM_RAW) {
2458 return true;
2460 } else {
2461 $cleaned = clean_param($data, $this->paramtype);
2462 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2463 return true;
2464 } else {
2465 return get_string('validateerror', 'admin');
2471 * Return an XHTML string for the setting
2472 * @return string Returns an XHTML string
2474 public function output_html($data, $query='') {
2475 global $OUTPUT;
2477 $default = $this->get_defaultsetting();
2478 $context = (object) [
2479 'size' => $this->size,
2480 'id' => $this->get_id(),
2481 'name' => $this->get_full_name(),
2482 'value' => $data,
2483 'forceltr' => $this->get_force_ltr(),
2484 'readonly' => $this->is_readonly(),
2486 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
2488 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2493 * Text input with a maximum length constraint.
2495 * @copyright 2015 onwards Ankit Agarwal
2496 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2498 class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
2500 /** @var int maximum number of chars allowed. */
2501 protected $maxlength;
2504 * Config text constructor
2506 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
2507 * or 'myplugin/mysetting' for ones in config_plugins.
2508 * @param string $visiblename localised
2509 * @param string $description long localised info
2510 * @param string $defaultsetting
2511 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2512 * @param int $size default field size
2513 * @param mixed $maxlength int maxlength allowed, 0 for infinite.
2515 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
2516 $size=null, $maxlength = 0) {
2517 $this->maxlength = $maxlength;
2518 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
2522 * Validate data before storage
2524 * @param string $data data
2525 * @return mixed true if ok string if error found
2527 public function validate($data) {
2528 $parentvalidation = parent::validate($data);
2529 if ($parentvalidation === true) {
2530 if ($this->maxlength > 0) {
2531 // Max length check.
2532 $length = core_text::strlen($data);
2533 if ($length > $this->maxlength) {
2534 return get_string('maximumchars', 'moodle', $this->maxlength);
2536 return true;
2537 } else {
2538 return true; // No max length check needed.
2540 } else {
2541 return $parentvalidation;
2547 * General text area without html editor.
2549 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2551 class admin_setting_configtextarea extends admin_setting_configtext {
2552 private $rows;
2553 private $cols;
2556 * @param string $name
2557 * @param string $visiblename
2558 * @param string $description
2559 * @param mixed $defaultsetting string or array
2560 * @param mixed $paramtype
2561 * @param string $cols The number of columns to make the editor
2562 * @param string $rows The number of rows to make the editor
2564 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2565 $this->rows = $rows;
2566 $this->cols = $cols;
2567 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2571 * Returns an XHTML string for the editor
2573 * @param string $data
2574 * @param string $query
2575 * @return string XHTML string for the editor
2577 public function output_html($data, $query='') {
2578 global $OUTPUT;
2580 $default = $this->get_defaultsetting();
2581 $defaultinfo = $default;
2582 if (!is_null($default) and $default !== '') {
2583 $defaultinfo = "\n".$default;
2586 $context = (object) [
2587 'cols' => $this->cols,
2588 'rows' => $this->rows,
2589 'id' => $this->get_id(),
2590 'name' => $this->get_full_name(),
2591 'value' => $data,
2592 'forceltr' => $this->get_force_ltr(),
2593 'readonly' => $this->is_readonly(),
2595 $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
2597 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
2602 * General text area with html editor.
2604 class admin_setting_confightmleditor extends admin_setting_configtextarea {
2607 * @param string $name
2608 * @param string $visiblename
2609 * @param string $description
2610 * @param mixed $defaultsetting string or array
2611 * @param mixed $paramtype
2613 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2614 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
2615 $this->set_force_ltr(false);
2616 editors_head_setup();
2620 * Returns an XHTML string for the editor
2622 * @param string $data
2623 * @param string $query
2624 * @return string XHTML string for the editor
2626 public function output_html($data, $query='') {
2627 $editor = editors_get_preferred_editor(FORMAT_HTML);
2628 $editor->set_text($data);
2629 $editor->use_editor($this->get_id(), array('noclean'=>true));
2630 return parent::output_html($data, $query);
2634 * Checks if data has empty html.
2636 * @param string $data
2637 * @return string Empty when no errors.
2639 public function write_setting($data) {
2640 if (trim(html_to_text($data)) === '') {
2641 $data = '';
2643 return parent::write_setting($data);
2649 * Password field, allows unmasking of password
2651 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2653 class admin_setting_configpasswordunmask extends admin_setting_configtext {
2656 * Constructor
2657 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2658 * @param string $visiblename localised
2659 * @param string $description long localised info
2660 * @param string $defaultsetting default password
2662 public function __construct($name, $visiblename, $description, $defaultsetting) {
2663 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2667 * Log config changes if necessary.
2668 * @param string $name
2669 * @param string $oldvalue
2670 * @param string $value
2672 protected function add_to_config_log($name, $oldvalue, $value) {
2673 if ($value !== '') {
2674 $value = '********';
2676 if ($oldvalue !== '' and $oldvalue !== null) {
2677 $oldvalue = '********';
2679 parent::add_to_config_log($name, $oldvalue, $value);
2683 * Returns HTML for the field.
2685 * @param string $data Value for the field
2686 * @param string $query Passed as final argument for format_admin_setting
2687 * @return string Rendered HTML
2689 public function output_html($data, $query='') {
2690 global $OUTPUT;
2692 $context = (object) [
2693 'id' => $this->get_id(),
2694 'name' => $this->get_full_name(),
2695 'size' => $this->size,
2696 'value' => $this->is_readonly() ? null : $data,
2697 'forceltr' => $this->get_force_ltr(),
2698 'readonly' => $this->is_readonly(),
2700 $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
2701 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
2706 * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
2708 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2709 * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
2711 class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
2714 * Constructor
2716 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2717 * @param string $visiblename localised
2718 * @param string $description long localised info
2719 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
2721 public function __construct($name, $visiblename, $description, $defaultsetting) {
2722 parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
2723 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
2728 * Admin setting class for encrypted values using secure encryption.
2730 * @copyright 2019 The Open University
2731 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2733 class admin_setting_encryptedpassword extends admin_setting {
2736 * Constructor. Same as parent except that the default value is always an empty string.
2738 * @param string $name Internal name used in config table
2739 * @param string $visiblename Name shown on form
2740 * @param string $description Description that appears below field
2742 public function __construct(string $name, string $visiblename, string $description) {
2743 parent::__construct($name, $visiblename, $description, '');
2746 public function get_setting() {
2747 return $this->config_read($this->name);
2750 public function write_setting($data) {
2751 $data = trim($data);
2752 if ($data === '') {
2753 // Value can really be set to nothing.
2754 $savedata = '';
2755 } else {
2756 // Encrypt value before saving it.
2757 $savedata = \core\encryption::encrypt($data);
2759 return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin'));
2762 public function output_html($data, $query='') {
2763 global $OUTPUT;
2765 $default = $this->get_defaultsetting();
2766 $context = (object) [
2767 'id' => $this->get_id(),
2768 'name' => $this->get_full_name(),
2769 'set' => $data !== '',
2770 'novalue' => $this->get_setting() === null
2772 $element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context);
2774 return format_admin_setting($this, $this->visiblename, $element, $this->description,
2775 true, '', $default, $query);
2780 * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2781 * Note: Only advanced makes sense right now - locked does not.
2783 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2785 class admin_setting_configempty extends admin_setting_configtext {
2788 * @param string $name
2789 * @param string $visiblename
2790 * @param string $description
2792 public function __construct($name, $visiblename, $description) {
2793 parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2797 * Returns an XHTML string for the hidden field
2799 * @param string $data
2800 * @param string $query
2801 * @return string XHTML string for the editor
2803 public function output_html($data, $query='') {
2804 global $OUTPUT;
2806 $context = (object) [
2807 'id' => $this->get_id(),
2808 'name' => $this->get_full_name()
2810 $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
2812 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
2818 * Path to directory
2820 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2822 class admin_setting_configfile extends admin_setting_configtext {
2824 * Constructor
2825 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2826 * @param string $visiblename localised
2827 * @param string $description long localised info
2828 * @param string $defaultdirectory default directory location
2830 public function __construct($name, $visiblename, $description, $defaultdirectory) {
2831 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2835 * Returns XHTML for the field
2837 * Returns XHTML for the field and also checks whether the file
2838 * specified in $data exists using file_exists()
2840 * @param string $data File name and path to use in value attr
2841 * @param string $query
2842 * @return string XHTML field
2844 public function output_html($data, $query='') {
2845 global $CFG, $OUTPUT;
2847 $default = $this->get_defaultsetting();
2848 $context = (object) [
2849 'id' => $this->get_id(),
2850 'name' => $this->get_full_name(),
2851 'size' => $this->size,
2852 'value' => $data,
2853 'showvalidity' => !empty($data),
2854 'valid' => $data && file_exists($data),
2855 'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(),
2856 'forceltr' => $this->get_force_ltr(),
2859 if ($context->readonly) {
2860 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2863 $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
2865 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2869 * Checks if execpatch has been disabled in config.php
2871 public function write_setting($data) {
2872 global $CFG;
2873 if (!empty($CFG->preventexecpath)) {
2874 if ($this->get_setting() === null) {
2875 // Use default during installation.
2876 $data = $this->get_defaultsetting();
2877 if ($data === null) {
2878 $data = '';
2880 } else {
2881 return '';
2884 return parent::write_setting($data);
2891 * Path to executable file
2893 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2895 class admin_setting_configexecutable extends admin_setting_configfile {
2898 * Returns an XHTML field
2900 * @param string $data This is the value for the field
2901 * @param string $query
2902 * @return string XHTML field
2904 public function output_html($data, $query='') {
2905 global $CFG, $OUTPUT;
2906 $default = $this->get_defaultsetting();
2907 require_once("$CFG->libdir/filelib.php");
2909 $context = (object) [
2910 'id' => $this->get_id(),
2911 'name' => $this->get_full_name(),
2912 'size' => $this->size,
2913 'value' => $data,
2914 'showvalidity' => !empty($data),
2915 'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
2916 'readonly' => !empty($CFG->preventexecpath),
2917 'forceltr' => $this->get_force_ltr()
2920 if (!empty($CFG->preventexecpath)) {
2921 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2924 $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
2926 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2932 * Path to directory
2934 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2936 class admin_setting_configdirectory extends admin_setting_configfile {
2939 * Returns an XHTML field
2941 * @param string $data This is the value for the field
2942 * @param string $query
2943 * @return string XHTML
2945 public function output_html($data, $query='') {
2946 global $CFG, $OUTPUT;
2947 $default = $this->get_defaultsetting();
2949 $context = (object) [
2950 'id' => $this->get_id(),
2951 'name' => $this->get_full_name(),
2952 'size' => $this->size,
2953 'value' => $data,
2954 'showvalidity' => !empty($data),
2955 'valid' => $data && file_exists($data) && is_dir($data),
2956 'readonly' => !empty($CFG->preventexecpath),
2957 'forceltr' => $this->get_force_ltr()
2960 if (!empty($CFG->preventexecpath)) {
2961 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2964 $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
2966 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2972 * Checkbox
2974 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2976 class admin_setting_configcheckbox extends admin_setting {
2977 /** @var string Value used when checked */
2978 public $yes;
2979 /** @var string Value used when not checked */
2980 public $no;
2983 * Constructor
2984 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2985 * @param string $visiblename localised
2986 * @param string $description long localised info
2987 * @param string $defaultsetting
2988 * @param string $yes value used when checked
2989 * @param string $no value used when not checked
2991 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
2992 parent::__construct($name, $visiblename, $description, $defaultsetting);
2993 $this->yes = (string)$yes;
2994 $this->no = (string)$no;
2998 * Retrieves the current setting using the objects name
3000 * @return string
3002 public function get_setting() {
3003 return $this->config_read($this->name);
3007 * Sets the value for the setting
3009 * Sets the value for the setting to either the yes or no values
3010 * of the object by comparing $data to yes
3012 * @param mixed $data Gets converted to str for comparison against yes value
3013 * @return string empty string or error
3015 public function write_setting($data) {
3016 if ((string)$data === $this->yes) { // convert to strings before comparison
3017 $data = $this->yes;
3018 } else {
3019 $data = $this->no;
3021 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3025 * Returns an XHTML checkbox field
3027 * @param string $data If $data matches yes then checkbox is checked
3028 * @param string $query
3029 * @return string XHTML field
3031 public function output_html($data, $query='') {
3032 global $OUTPUT;
3034 $context = (object) [
3035 'id' => $this->get_id(),
3036 'name' => $this->get_full_name(),
3037 'no' => $this->no,
3038 'value' => $this->yes,
3039 'checked' => (string) $data === $this->yes,
3040 'readonly' => $this->is_readonly(),
3043 $default = $this->get_defaultsetting();
3044 if (!is_null($default)) {
3045 if ((string)$default === $this->yes) {
3046 $defaultinfo = get_string('checkboxyes', 'admin');
3047 } else {
3048 $defaultinfo = get_string('checkboxno', 'admin');
3050 } else {
3051 $defaultinfo = NULL;
3054 $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
3056 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3062 * Multiple checkboxes, each represents different value, stored in csv format
3064 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3066 class admin_setting_configmulticheckbox extends admin_setting {
3067 /** @var array Array of choices value=>label */
3068 public $choices;
3069 /** @var callable|null Loader function for choices */
3070 protected $choiceloader = null;
3073 * Constructor: uses parent::__construct
3075 * The $choices parameter may be either an array of $value => $label format,
3076 * e.g. [1 => get_string('yes')], or a callback function which takes no parameters and
3077 * returns an array in that format.
3079 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3080 * @param string $visiblename localised
3081 * @param string $description long localised info
3082 * @param array $defaultsetting array of selected
3083 * @param array|callable $choices array of $value => $label for each checkbox, or a callback
3085 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3086 if (is_array($choices)) {
3087 $this->choices = $choices;
3089 if (is_callable($choices)) {
3090 $this->choiceloader = $choices;
3092 parent::__construct($name, $visiblename, $description, $defaultsetting);
3096 * This function may be used in ancestors for lazy loading of choices
3098 * Override this method if loading of choices is expensive, such
3099 * as when it requires multiple db requests.
3101 * @return bool true if loaded, false if error
3103 public function load_choices() {
3104 if ($this->choiceloader) {
3105 if (!is_array($this->choices)) {
3106 $this->choices = call_user_func($this->choiceloader);
3109 return true;
3113 * Is setting related to query text - used when searching
3115 * @param string $query
3116 * @return bool true on related, false on not or failure
3118 public function is_related($query) {
3119 if (!$this->load_choices() or empty($this->choices)) {
3120 return false;
3122 if (parent::is_related($query)) {
3123 return true;
3126 foreach ($this->choices as $desc) {
3127 if (strpos(core_text::strtolower($desc), $query) !== false) {
3128 return true;
3131 return false;
3135 * Returns the current setting if it is set
3137 * @return mixed null if null, else an array
3139 public function get_setting() {
3140 $result = $this->config_read($this->name);
3142 if (is_null($result)) {
3143 return NULL;
3145 if ($result === '') {
3146 return array();
3148 $enabled = explode(',', $result);
3149 $setting = array();
3150 foreach ($enabled as $option) {
3151 $setting[$option] = 1;
3153 return $setting;
3157 * Saves the setting(s) provided in $data
3159 * @param array $data An array of data, if not array returns empty str
3160 * @return mixed empty string on useless data or bool true=success, false=failed
3162 public function write_setting($data) {
3163 if (!is_array($data)) {
3164 return ''; // ignore it
3166 if (!$this->load_choices() or empty($this->choices)) {
3167 return '';
3169 unset($data['xxxxx']);
3170 $result = array();
3171 foreach ($data as $key => $value) {
3172 if ($value and array_key_exists($key, $this->choices)) {
3173 $result[] = $key;
3176 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
3180 * Returns XHTML field(s) as required by choices
3182 * Relies on data being an array should data ever be another valid vartype with
3183 * acceptable value this may cause a warning/error
3184 * if (!is_array($data)) would fix the problem
3186 * @todo Add vartype handling to ensure $data is an array
3188 * @param array $data An array of checked values
3189 * @param string $query
3190 * @return string XHTML field
3192 public function output_html($data, $query='') {
3193 global $OUTPUT;
3195 if (!$this->load_choices() or empty($this->choices)) {
3196 return '';
3199 $default = $this->get_defaultsetting();
3200 if (is_null($default)) {
3201 $default = array();
3203 if (is_null($data)) {
3204 $data = array();
3207 $context = (object) [
3208 'id' => $this->get_id(),
3209 'name' => $this->get_full_name(),
3212 $options = array();
3213 $defaults = array();
3214 foreach ($this->choices as $key => $description) {
3215 if (!empty($default[$key])) {
3216 $defaults[] = $description;
3219 $options[] = [
3220 'key' => $key,
3221 'checked' => !empty($data[$key]),
3222 'label' => highlightfast($query, $description)
3226 if (is_null($default)) {
3227 $defaultinfo = null;
3228 } else if (!empty($defaults)) {
3229 $defaultinfo = implode(', ', $defaults);
3230 } else {
3231 $defaultinfo = get_string('none');
3234 $context->options = $options;
3235 $context->hasoptions = !empty($options);
3237 $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
3239 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
3246 * Multiple checkboxes 2, value stored as string 00101011
3248 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3250 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
3253 * Returns the setting if set
3255 * @return mixed null if not set, else an array of set settings
3257 public function get_setting() {
3258 $result = $this->config_read($this->name);
3259 if (is_null($result)) {
3260 return NULL;
3262 if (!$this->load_choices()) {
3263 return NULL;
3265 $result = str_pad($result, count($this->choices), '0');
3266 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
3267 $setting = array();
3268 foreach ($this->choices as $key=>$unused) {
3269 $value = array_shift($result);
3270 if ($value) {
3271 $setting[$key] = 1;
3274 return $setting;
3278 * Save setting(s) provided in $data param
3280 * @param array $data An array of settings to save
3281 * @return mixed empty string for bad data or bool true=>success, false=>error
3283 public function write_setting($data) {
3284 if (!is_array($data)) {
3285 return ''; // ignore it
3287 if (!$this->load_choices() or empty($this->choices)) {
3288 return '';
3290 $result = '';
3291 foreach ($this->choices as $key=>$unused) {
3292 if (!empty($data[$key])) {
3293 $result .= '1';
3294 } else {
3295 $result .= '0';
3298 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
3304 * Select one value from list
3306 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3308 class admin_setting_configselect extends admin_setting {
3309 /** @var array Array of choices value=>label */
3310 public $choices;
3311 /** @var array Array of choices grouped using optgroups */
3312 public $optgroups;
3313 /** @var callable|null Loader function for choices */
3314 protected $choiceloader = null;
3315 /** @var callable|null Validation function */
3316 protected $validatefunction = null;
3319 * Constructor.
3321 * If you want to lazy-load the choices, pass a callback function that returns a choice
3322 * array for the $choices parameter.
3324 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3325 * @param string $visiblename localised
3326 * @param string $description long localised info
3327 * @param string|int $defaultsetting
3328 * @param array|callable|null $choices array of $value=>$label for each selection, or callback
3330 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3331 // Look for optgroup and single options.
3332 if (is_array($choices)) {
3333 $this->choices = [];
3334 foreach ($choices as $key => $val) {
3335 if (is_array($val)) {
3336 $this->optgroups[$key] = $val;
3337 $this->choices = array_merge($this->choices, $val);
3338 } else {
3339 $this->choices[$key] = $val;
3343 if (is_callable($choices)) {
3344 $this->choiceloader = $choices;
3347 parent::__construct($name, $visiblename, $description, $defaultsetting);
3351 * Sets a validate function.
3353 * The callback will be passed one parameter, the new setting value, and should return either
3354 * an empty string '' if the value is OK, or an error message if not.
3356 * @param callable|null $validatefunction Validate function or null to clear
3357 * @since Moodle 3.10
3359 public function set_validate_function(?callable $validatefunction = null) {
3360 $this->validatefunction = $validatefunction;
3364 * This function may be used in ancestors for lazy loading of choices
3366 * Override this method if loading of choices is expensive, such
3367 * as when it requires multiple db requests.
3369 * @return bool true if loaded, false if error
3371 public function load_choices() {
3372 if ($this->choiceloader) {
3373 if (!is_array($this->choices)) {
3374 $this->choices = call_user_func($this->choiceloader);
3376 return true;
3378 return true;
3382 * Check if this is $query is related to a choice
3384 * @param string $query
3385 * @return bool true if related, false if not
3387 public function is_related($query) {
3388 if (parent::is_related($query)) {
3389 return true;
3391 if (!$this->load_choices()) {
3392 return false;
3394 foreach ($this->choices as $key=>$value) {
3395 if (strpos(core_text::strtolower($key), $query) !== false) {
3396 return true;
3398 if (strpos(core_text::strtolower($value), $query) !== false) {
3399 return true;
3402 return false;
3406 * Return the setting
3408 * @return mixed returns config if successful else null
3410 public function get_setting() {
3411 return $this->config_read($this->name);
3415 * Save a setting
3417 * @param string $data
3418 * @return string empty of error string
3420 public function write_setting($data) {
3421 if (!$this->load_choices() or empty($this->choices)) {
3422 return '';
3424 if (!array_key_exists($data, $this->choices)) {
3425 return ''; // ignore it
3428 // Validate the new setting.
3429 $error = $this->validate_setting($data);
3430 if ($error) {
3431 return $error;
3434 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3438 * Validate the setting. This uses the callback function if provided; subclasses could override
3439 * to carry out validation directly in the class.
3441 * @param string $data New value being set
3442 * @return string Empty string if valid, or error message text
3443 * @since Moodle 3.10
3445 protected function validate_setting(string $data): string {
3446 // If validation function is specified, call it now.
3447 if ($this->validatefunction) {
3448 return call_user_func($this->validatefunction, $data);
3449 } else {
3450 return '';
3455 * Returns XHTML select field
3457 * Ensure the options are loaded, and generate the XHTML for the select
3458 * element and any warning message. Separating this out from output_html
3459 * makes it easier to subclass this class.
3461 * @param string $data the option to show as selected.
3462 * @param string $current the currently selected option in the database, null if none.
3463 * @param string $default the default selected option.
3464 * @return array the HTML for the select element, and a warning message.
3465 * @deprecated since Moodle 3.2
3467 public function output_select_html($data, $current, $default, $extraname = '') {
3468 debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
3472 * Returns XHTML select field and wrapping div(s)
3474 * @see output_select_html()
3476 * @param string $data the option to show as selected
3477 * @param string $query
3478 * @return string XHTML field and wrapping div
3480 public function output_html($data, $query='') {
3481 global $OUTPUT;
3483 $default = $this->get_defaultsetting();
3484 $current = $this->get_setting();
3486 if (!$this->load_choices() || empty($this->choices)) {
3487 return '';
3490 $context = (object) [
3491 'id' => $this->get_id(),
3492 'name' => $this->get_full_name(),
3495 if (!is_null($default) && array_key_exists($default, $this->choices)) {
3496 $defaultinfo = $this->choices[$default];
3497 } else {
3498 $defaultinfo = NULL;
3501 // Warnings.
3502 $warning = '';
3503 if ($current === null) {
3504 // First run.
3505 } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
3506 // No warning.
3507 } else if (!array_key_exists($current, $this->choices)) {
3508 $warning = get_string('warningcurrentsetting', 'admin', $current);
3509 if (!is_null($default) && $data == $current) {
3510 $data = $default; // Use default instead of first value when showing the form.
3514 $options = [];
3515 $template = 'core_admin/setting_configselect';
3517 if (!empty($this->optgroups)) {
3518 $optgroups = [];
3519 foreach ($this->optgroups as $label => $choices) {
3520 $optgroup = array('label' => $label, 'options' => []);
3521 foreach ($choices as $value => $name) {
3522 $optgroup['options'][] = [
3523 'value' => $value,
3524 'name' => $name,
3525 'selected' => (string) $value == $data
3527 unset($this->choices[$value]);
3529 $optgroups[] = $optgroup;
3531 $context->options = $options;
3532 $context->optgroups = $optgroups;
3533 $template = 'core_admin/setting_configselect_optgroup';
3536 foreach ($this->choices as $value => $name) {
3537 $options[] = [
3538 'value' => $value,
3539 'name' => $name,
3540 'selected' => (string) $value == $data
3543 $context->options = $options;
3544 $context->readonly = $this->is_readonly();
3546 $element = $OUTPUT->render_from_template($template, $context);
3548 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
3553 * Select multiple items from list
3555 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3557 class admin_setting_configmultiselect extends admin_setting_configselect {
3559 * Constructor
3560 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3561 * @param string $visiblename localised
3562 * @param string $description long localised info
3563 * @param array $defaultsetting array of selected items
3564 * @param array $choices array of $value=>$label for each list item
3566 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3567 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
3571 * Returns the select setting(s)
3573 * @return mixed null or array. Null if no settings else array of setting(s)
3575 public function get_setting() {
3576 $result = $this->config_read($this->name);
3577 if (is_null($result)) {
3578 return NULL;
3580 if ($result === '') {
3581 return array();
3583 return explode(',', $result);
3587 * Saves setting(s) provided through $data
3589 * Potential bug in the works should anyone call with this function
3590 * using a vartype that is not an array
3592 * @param array $data
3594 public function write_setting($data) {
3595 if (!is_array($data)) {
3596 return ''; //ignore it
3598 if (!$this->load_choices() or empty($this->choices)) {
3599 return '';
3602 unset($data['xxxxx']);
3604 $save = array();
3605 foreach ($data as $value) {
3606 if (!array_key_exists($value, $this->choices)) {
3607 continue; // ignore it
3609 $save[] = $value;
3612 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3616 * Is setting related to query text - used when searching
3618 * @param string $query
3619 * @return bool true if related, false if not
3621 public function is_related($query) {
3622 if (!$this->load_choices() or empty($this->choices)) {
3623 return false;
3625 if (parent::is_related($query)) {
3626 return true;
3629 foreach ($this->choices as $desc) {
3630 if (strpos(core_text::strtolower($desc), $query) !== false) {
3631 return true;
3634 return false;
3638 * Returns XHTML multi-select field
3640 * @todo Add vartype handling to ensure $data is an array
3641 * @param array $data Array of values to select by default
3642 * @param string $query
3643 * @return string XHTML multi-select field
3645 public function output_html($data, $query='') {
3646 global $OUTPUT;
3648 if (!$this->load_choices() or empty($this->choices)) {
3649 return '';
3652 $default = $this->get_defaultsetting();
3653 if (is_null($default)) {
3654 $default = array();
3656 if (is_null($data)) {
3657 $data = array();
3660 $context = (object) [
3661 'id' => $this->get_id(),
3662 'name' => $this->get_full_name(),
3663 'size' => min(10, count($this->choices))
3666 $defaults = [];
3667 $options = [];
3668 $template = 'core_admin/setting_configmultiselect';
3670 if (!empty($this->optgroups)) {
3671 $optgroups = [];
3672 foreach ($this->optgroups as $label => $choices) {
3673 $optgroup = array('label' => $label, 'options' => []);
3674 foreach ($choices as $value => $name) {
3675 if (in_array($value, $default)) {
3676 $defaults[] = $name;
3678 $optgroup['options'][] = [
3679 'value' => $value,
3680 'name' => $name,
3681 'selected' => in_array($value, $data)
3683 unset($this->choices[$value]);
3685 $optgroups[] = $optgroup;
3687 $context->optgroups = $optgroups;
3688 $template = 'core_admin/setting_configmultiselect_optgroup';
3691 foreach ($this->choices as $value => $name) {
3692 if (in_array($value, $default)) {
3693 $defaults[] = $name;
3695 $options[] = [
3696 'value' => $value,
3697 'name' => $name,
3698 'selected' => in_array($value, $data)
3701 $context->options = $options;
3702 $context->readonly = $this->is_readonly();
3704 if (is_null($default)) {
3705 $defaultinfo = NULL;
3706 } if (!empty($defaults)) {
3707 $defaultinfo = implode(', ', $defaults);
3708 } else {
3709 $defaultinfo = get_string('none');
3712 $element = $OUTPUT->render_from_template($template, $context);
3714 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3719 * Time selector
3721 * This is a liiitle bit messy. we're using two selects, but we're returning
3722 * them as an array named after $name (so we only use $name2 internally for the setting)
3724 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3726 class admin_setting_configtime extends admin_setting {
3727 /** @var string Used for setting second select (minutes) */
3728 public $name2;
3731 * Constructor
3732 * @param string $hoursname setting for hours
3733 * @param string $minutesname setting for hours
3734 * @param string $visiblename localised
3735 * @param string $description long localised info
3736 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3738 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3739 $this->name2 = $minutesname;
3740 parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3744 * Get the selected time
3746 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3748 public function get_setting() {
3749 $result1 = $this->config_read($this->name);
3750 $result2 = $this->config_read($this->name2);
3751 if (is_null($result1) or is_null($result2)) {
3752 return NULL;
3755 return array('h' => $result1, 'm' => $result2);
3759 * Store the time (hours and minutes)
3761 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3762 * @return bool true if success, false if not
3764 public function write_setting($data) {
3765 if (!is_array($data)) {
3766 return '';
3769 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3770 return ($result ? '' : get_string('errorsetting', 'admin'));
3774 * Returns XHTML time select fields
3776 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3777 * @param string $query
3778 * @return string XHTML time select fields and wrapping div(s)
3780 public function output_html($data, $query='') {
3781 global $OUTPUT;
3783 $default = $this->get_defaultsetting();
3784 if (is_array($default)) {
3785 $defaultinfo = $default['h'].':'.$default['m'];
3786 } else {
3787 $defaultinfo = NULL;
3790 $context = (object) [
3791 'id' => $this->get_id(),
3792 'name' => $this->get_full_name(),
3793 'readonly' => $this->is_readonly(),
3794 'hours' => array_map(function($i) use ($data) {
3795 return [
3796 'value' => $i,
3797 'name' => $i,
3798 'selected' => $i == $data['h']
3800 }, range(0, 23)),
3801 'minutes' => array_map(function($i) use ($data) {
3802 return [
3803 'value' => $i,
3804 'name' => $i,
3805 'selected' => $i == $data['m']
3807 }, range(0, 59, 5))
3810 $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
3812 return format_admin_setting($this, $this->visiblename, $element, $this->description,
3813 $this->get_id() . 'h', '', $defaultinfo, $query);
3820 * Seconds duration setting.
3822 * @copyright 2012 Petr Skoda (http://skodak.org)
3823 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3825 class admin_setting_configduration extends admin_setting {
3827 /** @var int default duration unit */
3828 protected $defaultunit;
3829 /** @var callable|null Validation function */
3830 protected $validatefunction = null;
3833 * Constructor
3834 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3835 * or 'myplugin/mysetting' for ones in config_plugins.
3836 * @param string $visiblename localised name
3837 * @param string $description localised long description
3838 * @param mixed $defaultsetting string or array depending on implementation
3839 * @param int $defaultunit - day, week, etc. (in seconds)
3841 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3842 if (is_number($defaultsetting)) {
3843 $defaultsetting = self::parse_seconds($defaultsetting);
3845 $units = self::get_units();
3846 if (isset($units[$defaultunit])) {
3847 $this->defaultunit = $defaultunit;
3848 } else {
3849 $this->defaultunit = 86400;
3851 parent::__construct($name, $visiblename, $description, $defaultsetting);
3855 * Sets a validate function.
3857 * The callback will be passed one parameter, the new setting value, and should return either
3858 * an empty string '' if the value is OK, or an error message if not.
3860 * @param callable|null $validatefunction Validate function or null to clear
3861 * @since Moodle 3.10
3863 public function set_validate_function(?callable $validatefunction = null) {
3864 $this->validatefunction = $validatefunction;
3868 * Validate the setting. This uses the callback function if provided; subclasses could override
3869 * to carry out validation directly in the class.
3871 * @param int $data New value being set
3872 * @return string Empty string if valid, or error message text
3873 * @since Moodle 3.10
3875 protected function validate_setting(int $data): string {
3876 // If validation function is specified, call it now.
3877 if ($this->validatefunction) {
3878 return call_user_func($this->validatefunction, $data);
3879 } else {
3880 return '';
3885 * Returns selectable units.
3886 * @static
3887 * @return array
3889 protected static function get_units() {
3890 return array(
3891 604800 => get_string('weeks'),
3892 86400 => get_string('days'),
3893 3600 => get_string('hours'),
3894 60 => get_string('minutes'),
3895 1 => get_string('seconds'),
3900 * Converts seconds to some more user friendly string.
3901 * @static
3902 * @param int $seconds
3903 * @return string
3905 protected static function get_duration_text($seconds) {
3906 if (empty($seconds)) {
3907 return get_string('none');
3909 $data = self::parse_seconds($seconds);
3910 switch ($data['u']) {
3911 case (60*60*24*7):
3912 return get_string('numweeks', '', $data['v']);
3913 case (60*60*24):
3914 return get_string('numdays', '', $data['v']);
3915 case (60*60):
3916 return get_string('numhours', '', $data['v']);
3917 case (60):
3918 return get_string('numminutes', '', $data['v']);
3919 default:
3920 return get_string('numseconds', '', $data['v']*$data['u']);
3925 * Finds suitable units for given duration.
3926 * @static
3927 * @param int $seconds
3928 * @return array
3930 protected static function parse_seconds($seconds) {
3931 foreach (self::get_units() as $unit => $unused) {
3932 if ($seconds % $unit === 0) {
3933 return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
3936 return array('v'=>(int)$seconds, 'u'=>1);
3940 * Get the selected duration as array.
3942 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
3944 public function get_setting() {
3945 $seconds = $this->config_read($this->name);
3946 if (is_null($seconds)) {
3947 return null;
3950 return self::parse_seconds($seconds);
3954 * Store the duration as seconds.
3956 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3957 * @return bool true if success, false if not
3959 public function write_setting($data) {
3960 if (!is_array($data)) {
3961 return '';
3964 $seconds = (int)($data['v']*$data['u']);
3965 if ($seconds < 0) {
3966 return get_string('errorsetting', 'admin');
3969 // Validate the new setting.
3970 $error = $this->validate_setting($seconds);
3971 if ($error) {
3972 return $error;
3975 $result = $this->config_write($this->name, $seconds);
3976 return ($result ? '' : get_string('errorsetting', 'admin'));
3980 * Returns duration text+select fields.
3982 * @param array $data Must be form 'v'=>xx, 'u'=>xx
3983 * @param string $query
3984 * @return string duration text+select fields and wrapping div(s)
3986 public function output_html($data, $query='') {
3987 global $OUTPUT;
3989 $default = $this->get_defaultsetting();
3990 if (is_number($default)) {
3991 $defaultinfo = self::get_duration_text($default);
3992 } else if (is_array($default)) {
3993 $defaultinfo = self::get_duration_text($default['v']*$default['u']);
3994 } else {
3995 $defaultinfo = null;
3998 $inputid = $this->get_id() . 'v';
3999 $units = self::get_units();
4000 $defaultunit = $this->defaultunit;
4002 $context = (object) [
4003 'id' => $this->get_id(),
4004 'name' => $this->get_full_name(),
4005 'value' => $data['v'],
4006 'readonly' => $this->is_readonly(),
4007 'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
4008 return [
4009 'value' => $unit,
4010 'name' => $units[$unit],
4011 'selected' => ($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']
4013 }, array_keys($units))
4016 $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
4018 return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
4024 * Seconds duration setting with an advanced checkbox, that controls a additional
4025 * $name.'_adv' setting.
4027 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4028 * @copyright 2014 The Open University
4030 class admin_setting_configduration_with_advanced extends admin_setting_configduration {
4032 * Constructor
4033 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
4034 * or 'myplugin/mysetting' for ones in config_plugins.
4035 * @param string $visiblename localised name
4036 * @param string $description localised long description
4037 * @param array $defaultsetting array of int value, and bool whether it is
4038 * is advanced by default.
4039 * @param int $defaultunit - day, week, etc. (in seconds)
4041 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
4042 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
4043 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4049 * Used to validate a textarea used for ip addresses
4051 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4052 * @copyright 2011 Petr Skoda (http://skodak.org)
4054 class admin_setting_configiplist extends admin_setting_configtextarea {
4057 * Validate the contents of the textarea as IP addresses
4059 * Used to validate a new line separated list of IP addresses collected from
4060 * a textarea control
4062 * @param string $data A list of IP Addresses separated by new lines
4063 * @return mixed bool true for success or string:error on failure
4065 public function validate($data) {
4066 if(!empty($data)) {
4067 $lines = explode("\n", $data);
4068 } else {
4069 return true;
4071 $result = true;
4072 $badips = array();
4073 foreach ($lines as $line) {
4074 $tokens = explode('#', $line);
4075 $ip = trim($tokens[0]);
4076 if (empty($ip)) {
4077 continue;
4079 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
4080 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
4081 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
4082 } else {
4083 $result = false;
4084 $badips[] = $ip;
4087 if($result) {
4088 return true;
4089 } else {
4090 return get_string('validateiperror', 'admin', join(', ', $badips));
4096 * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
4098 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4099 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4101 class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
4104 * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
4105 * Used to validate a new line separated list of entries collected from a textarea control.
4107 * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
4108 * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
4109 * via the get_setting() method, which has been overriden.
4111 * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
4112 * @return mixed bool true for success or string:error on failure
4114 public function validate($data) {
4115 if (empty($data)) {
4116 return true;
4118 $entries = explode("\n", $data);
4119 $badentries = [];
4121 foreach ($entries as $key => $entry) {
4122 $entry = trim($entry);
4123 if (empty($entry)) {
4124 return get_string('validateemptylineerror', 'admin');
4127 // Validate each string entry against the supported formats.
4128 if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
4129 || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
4130 || \core\ip_utils::is_domain_matching_pattern($entry)) {
4131 continue;
4134 // Otherwise, the entry is invalid.
4135 $badentries[] = $entry;
4138 if ($badentries) {
4139 return get_string('validateerrorlist', 'admin', join(', ', $badentries));
4141 return true;
4145 * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
4147 * @param string $data the setting data, as sent from the web form.
4148 * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
4150 protected function ace_encode($data) {
4151 if (empty($data)) {
4152 return $data;
4154 $entries = explode("\n", $data);
4155 foreach ($entries as $key => $entry) {
4156 $entry = trim($entry);
4157 // This regex matches any string that has non-ascii character.
4158 if (preg_match('/[^\x00-\x7f]/', $entry)) {
4159 // If we can convert the unicode string to an idn, do so.
4160 // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
4161 $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4162 $entries[$key] = $val ? $val : $entry;
4165 return implode("\n", $entries);
4169 * Decode any ascii-encoded domain names back to their utf-8 representation for display.
4171 * @param string $data the setting data, as found in the database.
4172 * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
4174 protected function ace_decode($data) {
4175 $entries = explode("\n", $data);
4176 foreach ($entries as $key => $entry) {
4177 $entry = trim($entry);
4178 if (strpos($entry, 'xn--') !== false) {
4179 $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4182 return implode("\n", $entries);
4186 * Override, providing utf8-decoding for ascii-encoded IDN strings.
4188 * @return mixed returns punycode-converted setting string if successful, else null.
4190 public function get_setting() {
4191 // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
4192 $data = $this->config_read($this->name);
4193 if (function_exists('idn_to_utf8') && !is_null($data)) {
4194 $data = $this->ace_decode($data);
4196 return $data;
4200 * Override, providing ascii-encoding for utf8 (native) IDN strings.
4202 * @param string $data
4203 * @return string
4205 public function write_setting($data) {
4206 if ($this->paramtype === PARAM_INT and $data === '') {
4207 // Do not complain if '' used instead of 0.
4208 $data = 0;
4211 // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
4212 if (function_exists('idn_to_ascii')) {
4213 $data = $this->ace_encode($data);
4216 $validated = $this->validate($data);
4217 if ($validated !== true) {
4218 return $validated;
4220 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4225 * Used to validate a textarea used for port numbers.
4227 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4228 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4230 class admin_setting_configportlist extends admin_setting_configtextarea {
4233 * Validate the contents of the textarea as port numbers.
4234 * Used to validate a new line separated list of ports collected from a textarea control.
4236 * @param string $data A list of ports separated by new lines
4237 * @return mixed bool true for success or string:error on failure
4239 public function validate($data) {
4240 if (empty($data)) {
4241 return true;
4243 $ports = explode("\n", $data);
4244 $badentries = [];
4245 foreach ($ports as $port) {
4246 $port = trim($port);
4247 if (empty($port)) {
4248 return get_string('validateemptylineerror', 'admin');
4251 // Is the string a valid integer number?
4252 if (strval(intval($port)) !== $port || intval($port) <= 0) {
4253 $badentries[] = $port;
4256 if ($badentries) {
4257 return get_string('validateerrorlist', 'admin', $badentries);
4259 return true;
4265 * An admin setting for selecting one or more users who have a capability
4266 * in the system context
4268 * An admin setting for selecting one or more users, who have a particular capability
4269 * in the system context. Warning, make sure the list will never be too long. There is
4270 * no paging or searching of this list.
4272 * To correctly get a list of users from this config setting, you need to call the
4273 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
4275 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4277 class admin_setting_users_with_capability extends admin_setting_configmultiselect {
4278 /** @var string The capabilities name */
4279 protected $capability;
4280 /** @var int include admin users too */
4281 protected $includeadmins;
4284 * Constructor.
4286 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4287 * @param string $visiblename localised name
4288 * @param string $description localised long description
4289 * @param array $defaultsetting array of usernames
4290 * @param string $capability string capability name.
4291 * @param bool $includeadmins include administrators
4293 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
4294 $this->capability = $capability;
4295 $this->includeadmins = $includeadmins;
4296 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4300 * Load all of the uses who have the capability into choice array
4302 * @return bool Always returns true
4304 function load_choices() {
4305 if (is_array($this->choices)) {
4306 return true;
4308 list($sort, $sortparams) = users_order_by_sql('u');
4309 if (!empty($sortparams)) {
4310 throw new coding_exception('users_order_by_sql returned some query parameters. ' .
4311 'This is unexpected, and a problem because there is no way to pass these ' .
4312 'parameters to get_users_by_capability. See MDL-34657.');
4314 $userfieldsapi = \core_user\fields::for_name();
4315 $userfields = 'u.id, u.username, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects;
4316 $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
4317 $this->choices = array(
4318 '$@NONE@$' => get_string('nobody'),
4319 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4321 if ($this->includeadmins) {
4322 $admins = get_admins();
4323 foreach ($admins as $user) {
4324 $this->choices[$user->id] = fullname($user);
4327 if (is_array($users)) {
4328 foreach ($users as $user) {
4329 $this->choices[$user->id] = fullname($user);
4332 return true;
4336 * Returns the default setting for class
4338 * @return mixed Array, or string. Empty string if no default
4340 public function get_defaultsetting() {
4341 $this->load_choices();
4342 $defaultsetting = parent::get_defaultsetting();
4343 if (empty($defaultsetting)) {
4344 return array('$@NONE@$');
4345 } else if (array_key_exists($defaultsetting, $this->choices)) {
4346 return $defaultsetting;
4347 } else {
4348 return '';
4353 * Returns the current setting
4355 * @return mixed array or string
4357 public function get_setting() {
4358 $result = parent::get_setting();
4359 if ($result === null) {
4360 // this is necessary for settings upgrade
4361 return null;
4363 if (empty($result)) {
4364 $result = array('$@NONE@$');
4366 return $result;
4370 * Save the chosen setting provided as $data
4372 * @param array $data
4373 * @return mixed string or array
4375 public function write_setting($data) {
4376 // If all is selected, remove any explicit options.
4377 if (in_array('$@ALL@$', $data)) {
4378 $data = array('$@ALL@$');
4380 // None never needs to be written to the DB.
4381 if (in_array('$@NONE@$', $data)) {
4382 unset($data[array_search('$@NONE@$', $data)]);
4384 return parent::write_setting($data);
4390 * Special checkbox for calendar - resets SESSION vars.
4392 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4394 class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
4396 * Calls the parent::__construct with default values
4398 * name => calendar_adminseesall
4399 * visiblename => get_string('adminseesall', 'admin')
4400 * description => get_string('helpadminseesall', 'admin')
4401 * defaultsetting => 0
4403 public function __construct() {
4404 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
4405 get_string('helpadminseesall', 'admin'), '0');
4409 * Stores the setting passed in $data
4411 * @param mixed gets converted to string for comparison
4412 * @return string empty string or error message
4414 public function write_setting($data) {
4415 global $SESSION;
4416 return parent::write_setting($data);
4421 * Special select for settings that are altered in setup.php and can not be altered on the fly
4423 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4425 class admin_setting_special_selectsetup extends admin_setting_configselect {
4427 * Reads the setting directly from the database
4429 * @return mixed
4431 public function get_setting() {
4432 // read directly from db!
4433 return get_config(NULL, $this->name);
4437 * Save the setting passed in $data
4439 * @param string $data The setting to save
4440 * @return string empty or error message
4442 public function write_setting($data) {
4443 global $CFG;
4444 // do not change active CFG setting!
4445 $current = $CFG->{$this->name};
4446 $result = parent::write_setting($data);
4447 $CFG->{$this->name} = $current;
4448 return $result;
4454 * Special select for frontpage - stores data in course table
4456 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4458 class admin_setting_sitesetselect extends admin_setting_configselect {
4460 * Returns the site name for the selected site
4462 * @see get_site()
4463 * @return string The site name of the selected site
4465 public function get_setting() {
4466 $site = course_get_format(get_site())->get_course();
4467 return $site->{$this->name};
4471 * Updates the database and save the setting
4473 * @param string data
4474 * @return string empty or error message
4476 public function write_setting($data) {
4477 global $DB, $SITE, $COURSE;
4478 if (!in_array($data, array_keys($this->choices))) {
4479 return get_string('errorsetting', 'admin');
4481 $record = new stdClass();
4482 $record->id = SITEID;
4483 $temp = $this->name;
4484 $record->$temp = $data;
4485 $record->timemodified = time();
4487 course_get_format($SITE)->update_course_format_options($record);
4488 $DB->update_record('course', $record);
4490 // Reset caches.
4491 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4492 if ($SITE->id == $COURSE->id) {
4493 $COURSE = $SITE;
4495 format_base::reset_course_cache($SITE->id);
4497 return '';
4504 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
4505 * block to hidden.
4507 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4509 class admin_setting_bloglevel extends admin_setting_configselect {
4511 * Updates the database and save the setting
4513 * @param string data
4514 * @return string empty or error message
4516 public function write_setting($data) {
4517 global $DB, $CFG;
4518 if ($data == 0) {
4519 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
4520 foreach ($blogblocks as $block) {
4521 $DB->set_field('block', 'visible', 0, array('id' => $block->id));
4523 } else {
4524 // reenable all blocks only when switching from disabled blogs
4525 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
4526 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
4527 foreach ($blogblocks as $block) {
4528 $DB->set_field('block', 'visible', 1, array('id' => $block->id));
4532 return parent::write_setting($data);
4538 * Special select - lists on the frontpage - hacky
4540 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4542 class admin_setting_courselist_frontpage extends admin_setting {
4543 /** @var array Array of choices value=>label */
4544 public $choices;
4547 * Construct override, requires one param
4549 * @param bool $loggedin Is the user logged in
4551 public function __construct($loggedin) {
4552 global $CFG;
4553 require_once($CFG->dirroot.'/course/lib.php');
4554 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
4555 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
4556 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
4557 $defaults = array(FRONTPAGEALLCOURSELIST);
4558 parent::__construct($name, $visiblename, $description, $defaults);
4562 * Loads the choices available
4564 * @return bool always returns true
4566 public function load_choices() {
4567 if (is_array($this->choices)) {
4568 return true;
4570 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
4571 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
4572 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
4573 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
4574 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
4575 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
4576 'none' => get_string('none'));
4577 if ($this->name === 'frontpage') {
4578 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
4580 return true;
4584 * Returns the selected settings
4586 * @param mixed array or setting or null
4588 public function get_setting() {
4589 $result = $this->config_read($this->name);
4590 if (is_null($result)) {
4591 return NULL;
4593 if ($result === '') {
4594 return array();
4596 return explode(',', $result);
4600 * Save the selected options
4602 * @param array $data
4603 * @return mixed empty string (data is not an array) or bool true=success false=failure
4605 public function write_setting($data) {
4606 if (!is_array($data)) {
4607 return '';
4609 $this->load_choices();
4610 $save = array();
4611 foreach($data as $datum) {
4612 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
4613 continue;
4615 $save[$datum] = $datum; // no duplicates
4617 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
4621 * Return XHTML select field and wrapping div
4623 * @todo Add vartype handling to make sure $data is an array
4624 * @param array $data Array of elements to select by default
4625 * @return string XHTML select field and wrapping div
4627 public function output_html($data, $query='') {
4628 global $OUTPUT;
4630 $this->load_choices();
4631 $currentsetting = array();
4632 foreach ($data as $key) {
4633 if ($key != 'none' and array_key_exists($key, $this->choices)) {
4634 $currentsetting[] = $key; // already selected first
4638 $context = (object) [
4639 'id' => $this->get_id(),
4640 'name' => $this->get_full_name(),
4643 $options = $this->choices;
4644 $selects = [];
4645 for ($i = 0; $i < count($this->choices) - 1; $i++) {
4646 if (!array_key_exists($i, $currentsetting)) {
4647 $currentsetting[$i] = 'none';
4649 $selects[] = [
4650 'key' => $i,
4651 'options' => array_map(function($option) use ($options, $currentsetting, $i) {
4652 return [
4653 'name' => $options[$option],
4654 'value' => $option,
4655 'selected' => $currentsetting[$i] == $option
4657 }, array_keys($options))
4660 $context->selects = $selects;
4662 $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
4664 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
4670 * Special checkbox for frontpage - stores data in course table
4672 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4674 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
4676 * Returns the current sites name
4678 * @return string
4680 public function get_setting() {
4681 $site = course_get_format(get_site())->get_course();
4682 return $site->{$this->name};
4686 * Save the selected setting
4688 * @param string $data The selected site
4689 * @return string empty string or error message
4691 public function write_setting($data) {
4692 global $DB, $SITE, $COURSE;
4693 $record = new stdClass();
4694 $record->id = $SITE->id;
4695 $record->{$this->name} = ($data == '1' ? 1 : 0);
4696 $record->timemodified = time();
4698 course_get_format($SITE)->update_course_format_options($record);
4699 $DB->update_record('course', $record);
4701 // Reset caches.
4702 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4703 if ($SITE->id == $COURSE->id) {
4704 $COURSE = $SITE;
4706 format_base::reset_course_cache($SITE->id);
4708 return '';
4713 * Special text for frontpage - stores data in course table.
4714 * Empty string means not set here. Manual setting is required.
4716 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4718 class admin_setting_sitesettext extends admin_setting_configtext {
4721 * Constructor.
4723 public function __construct() {
4724 call_user_func_array(['parent', '__construct'], func_get_args());
4725 $this->set_force_ltr(false);
4729 * Return the current setting
4731 * @return mixed string or null
4733 public function get_setting() {
4734 $site = course_get_format(get_site())->get_course();
4735 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
4739 * Validate the selected data
4741 * @param string $data The selected value to validate
4742 * @return mixed true or message string
4744 public function validate($data) {
4745 global $DB, $SITE;
4746 $cleaned = clean_param($data, PARAM_TEXT);
4747 if ($cleaned === '') {
4748 return get_string('required');
4750 if ($this->name ==='shortname' &&
4751 $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
4752 return get_string('shortnametaken', 'error', $data);
4754 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
4755 return true;
4756 } else {
4757 return get_string('validateerror', 'admin');
4762 * Save the selected setting
4764 * @param string $data The selected value
4765 * @return string empty or error message
4767 public function write_setting($data) {
4768 global $DB, $SITE, $COURSE;
4769 $data = trim($data);
4770 $validated = $this->validate($data);
4771 if ($validated !== true) {
4772 return $validated;
4775 $record = new stdClass();
4776 $record->id = $SITE->id;
4777 $record->{$this->name} = $data;
4778 $record->timemodified = time();
4780 course_get_format($SITE)->update_course_format_options($record);
4781 $DB->update_record('course', $record);
4783 // Reset caches.
4784 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4785 if ($SITE->id == $COURSE->id) {
4786 $COURSE = $SITE;
4788 format_base::reset_course_cache($SITE->id);
4790 return '';
4796 * Special text editor for site description.
4798 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4800 class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
4803 * Calls parent::__construct with specific arguments
4805 public function __construct() {
4806 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
4807 PARAM_RAW, 60, 15);
4811 * Return the current setting
4812 * @return string The current setting
4814 public function get_setting() {
4815 $site = course_get_format(get_site())->get_course();
4816 return $site->{$this->name};
4820 * Save the new setting
4822 * @param string $data The new value to save
4823 * @return string empty or error message
4825 public function write_setting($data) {
4826 global $DB, $SITE, $COURSE;
4827 $record = new stdClass();
4828 $record->id = $SITE->id;
4829 $record->{$this->name} = $data;
4830 $record->timemodified = time();
4832 course_get_format($SITE)->update_course_format_options($record);
4833 $DB->update_record('course', $record);
4835 // Reset caches.
4836 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4837 if ($SITE->id == $COURSE->id) {
4838 $COURSE = $SITE;
4840 format_base::reset_course_cache($SITE->id);
4842 return '';
4848 * Administration interface for emoticon_manager settings.
4850 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4852 class admin_setting_emoticons extends admin_setting {
4855 * Calls parent::__construct with specific args
4857 public function __construct() {
4858 global $CFG;
4860 $manager = get_emoticon_manager();
4861 $defaults = $this->prepare_form_data($manager->default_emoticons());
4862 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
4866 * Return the current setting(s)
4868 * @return array Current settings array
4870 public function get_setting() {
4871 global $CFG;
4873 $manager = get_emoticon_manager();
4875 $config = $this->config_read($this->name);
4876 if (is_null($config)) {
4877 return null;
4880 $config = $manager->decode_stored_config($config);
4881 if (is_null($config)) {
4882 return null;
4885 return $this->prepare_form_data($config);
4889 * Save selected settings
4891 * @param array $data Array of settings to save
4892 * @return bool
4894 public function write_setting($data) {
4896 $manager = get_emoticon_manager();
4897 $emoticons = $this->process_form_data($data);
4899 if ($emoticons === false) {
4900 return false;
4903 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
4904 return ''; // success
4905 } else {
4906 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
4911 * Return XHTML field(s) for options
4913 * @param array $data Array of options to set in HTML
4914 * @return string XHTML string for the fields and wrapping div(s)
4916 public function output_html($data, $query='') {
4917 global $OUTPUT;
4919 $context = (object) [
4920 'name' => $this->get_full_name(),
4921 'emoticons' => [],
4922 'forceltr' => true,
4925 $i = 0;
4926 foreach ($data as $field => $value) {
4928 // When $i == 0: text.
4929 // When $i == 1: imagename.
4930 // When $i == 2: imagecomponent.
4931 // When $i == 3: altidentifier.
4932 // When $i == 4: altcomponent.
4933 $fields[$i] = (object) [
4934 'field' => $field,
4935 'value' => $value,
4936 'index' => $i
4938 $i++;
4940 if ($i > 4) {
4941 $icon = null;
4942 if (!empty($fields[1]->value)) {
4943 if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
4944 $alt = get_string($fields[3]->value, $fields[4]->value);
4945 } else {
4946 $alt = $fields[0]->value;
4948 $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
4950 $context->emoticons[] = [
4951 'fields' => $fields,
4952 'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
4954 $fields = [];
4955 $i = 0;
4959 $context->reseturl = new moodle_url('/admin/resetemoticons.php');
4960 $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
4961 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
4965 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
4967 * @see self::process_form_data()
4968 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
4969 * @return array of form fields and their values
4971 protected function prepare_form_data(array $emoticons) {
4973 $form = array();
4974 $i = 0;
4975 foreach ($emoticons as $emoticon) {
4976 $form['text'.$i] = $emoticon->text;
4977 $form['imagename'.$i] = $emoticon->imagename;
4978 $form['imagecomponent'.$i] = $emoticon->imagecomponent;
4979 $form['altidentifier'.$i] = $emoticon->altidentifier;
4980 $form['altcomponent'.$i] = $emoticon->altcomponent;
4981 $i++;
4983 // add one more blank field set for new object
4984 $form['text'.$i] = '';
4985 $form['imagename'.$i] = '';
4986 $form['imagecomponent'.$i] = '';
4987 $form['altidentifier'.$i] = '';
4988 $form['altcomponent'.$i] = '';
4990 return $form;
4994 * Converts the data from admin settings form into an array of emoticon objects
4996 * @see self::prepare_form_data()
4997 * @param array $data array of admin form fields and values
4998 * @return false|array of emoticon objects
5000 protected function process_form_data(array $form) {
5002 $count = count($form); // number of form field values
5004 if ($count % 5) {
5005 // we must get five fields per emoticon object
5006 return false;
5009 $emoticons = array();
5010 for ($i = 0; $i < $count / 5; $i++) {
5011 $emoticon = new stdClass();
5012 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
5013 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
5014 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
5015 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
5016 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
5018 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
5019 // prevent from breaking http://url.addresses by accident
5020 $emoticon->text = '';
5023 if (strlen($emoticon->text) < 2) {
5024 // do not allow single character emoticons
5025 $emoticon->text = '';
5028 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
5029 // emoticon text must contain some non-alphanumeric character to prevent
5030 // breaking HTML tags
5031 $emoticon->text = '';
5034 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
5035 $emoticons[] = $emoticon;
5038 return $emoticons;
5045 * Special setting for limiting of the list of available languages.
5047 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5049 class admin_setting_langlist extends admin_setting_configtext {
5051 * Calls parent::__construct with specific arguments
5053 public function __construct() {
5054 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
5058 * Validate that each language identifier exists on the site
5060 * @param string $data
5061 * @return bool|string True if validation successful, otherwise error string
5063 public function validate($data) {
5064 $parentcheck = parent::validate($data);
5065 if ($parentcheck !== true) {
5066 return $parentcheck;
5069 if ($data === '') {
5070 return true;
5073 // Normalize language identifiers.
5074 $langcodes = array_map('trim', explode(',', $data));
5075 foreach ($langcodes as $langcode) {
5076 // If the langcode contains optional alias, split it out.
5077 [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2);
5079 if (!get_string_manager()->translation_exists($langcode)) {
5080 return get_string('invalidlanguagecode', 'error', $langcode);
5084 return true;
5088 * Save the new setting
5090 * @param string $data The new setting
5091 * @return bool
5093 public function write_setting($data) {
5094 $return = parent::write_setting($data);
5095 get_string_manager()->reset_caches();
5096 return $return;
5102 * Allows to specify comma separated list of known country codes.
5104 * This is a simple subclass of the plain input text field with added validation so that all the codes are actually
5105 * known codes.
5107 * @package core
5108 * @category admin
5109 * @copyright 2020 David Mudrák <david@moodle.com>
5110 * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5112 class admin_setting_countrycodes extends admin_setting_configtext {
5115 * Construct the instance of the setting.
5117 * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'.
5118 * @param lang_string|string $visiblename Language string with the field label text.
5119 * @param lang_string|string $description Language string with the field description text.
5120 * @param string $defaultsetting Default value of the setting.
5121 * @param int $size Input text field size.
5123 public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) {
5124 parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size);
5128 * Validate the setting value before storing it.
5130 * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or
5131 * a comma separated list of such words.
5133 * @param string $data Value inserted into the setting field.
5134 * @return bool|string True if the value is OK, error string otherwise.
5136 public function validate($data) {
5138 $parentcheck = parent::validate($data);
5140 if ($parentcheck !== true) {
5141 return $parentcheck;
5144 if ($data === '') {
5145 return true;
5148 $allcountries = get_string_manager()->get_list_of_countries(true);
5150 foreach (explode(',', $data) as $code) {
5151 if (!isset($allcountries[$code])) {
5152 return get_string('invalidcountrycode', 'core_error', $code);
5156 return true;
5162 * Selection of one of the recognised countries using the list
5163 * returned by {@link get_list_of_countries()}.
5165 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5167 class admin_settings_country_select extends admin_setting_configselect {
5168 protected $includeall;
5169 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
5170 $this->includeall = $includeall;
5171 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
5175 * Lazy-load the available choices for the select box
5177 public function load_choices() {
5178 global $CFG;
5179 if (is_array($this->choices)) {
5180 return true;
5182 $this->choices = array_merge(
5183 array('0' => get_string('choosedots')),
5184 get_string_manager()->get_list_of_countries($this->includeall));
5185 return true;
5191 * admin_setting_configselect for the default number of sections in a course,
5192 * simply so we can lazy-load the choices.
5194 * @copyright 2011 The Open University
5195 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5197 class admin_settings_num_course_sections extends admin_setting_configselect {
5198 public function __construct($name, $visiblename, $description, $defaultsetting) {
5199 parent::__construct($name, $visiblename, $description, $defaultsetting, array());
5202 /** Lazy-load the available choices for the select box */
5203 public function load_choices() {
5204 $max = get_config('moodlecourse', 'maxsections');
5205 if (!isset($max) || !is_numeric($max)) {
5206 $max = 52;
5208 for ($i = 0; $i <= $max; $i++) {
5209 $this->choices[$i] = "$i";
5211 return true;
5217 * Course category selection
5219 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5221 class admin_settings_coursecat_select extends admin_setting_configselect_autocomplete {
5223 * Calls parent::__construct with specific arguments
5225 public function __construct($name, $visiblename, $description, $defaultsetting = 1) {
5226 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices = null);
5230 * Load the available choices for the select box
5232 * @return bool
5234 public function load_choices() {
5235 if (is_array($this->choices)) {
5236 return true;
5238 $this->choices = core_course_category::make_categories_list('', 0, ' / ');
5239 return true;
5245 * Special control for selecting days to backup
5247 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5249 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
5251 * Calls parent::__construct with specific arguments
5253 public function __construct() {
5254 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
5255 $this->plugin = 'backup';
5259 * Load the available choices for the select box
5261 * @return bool Always returns true
5263 public function load_choices() {
5264 if (is_array($this->choices)) {
5265 return true;
5267 $this->choices = array();
5268 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5269 foreach ($days as $day) {
5270 $this->choices[$day] = get_string($day, 'calendar');
5272 return true;
5277 * Special setting for backup auto destination.
5279 * @package core
5280 * @subpackage admin
5281 * @copyright 2014 Frédéric Massart - FMCorz.net
5282 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5284 class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
5287 * Calls parent::__construct with specific arguments.
5289 public function __construct() {
5290 parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
5294 * Check if the directory must be set, depending on backup/backup_auto_storage.
5296 * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
5297 * there will be conflicts if this validation happens before the other one.
5299 * @param string $data Form data.
5300 * @return string Empty when no errors.
5302 public function write_setting($data) {
5303 $storage = (int) get_config('backup', 'backup_auto_storage');
5304 if ($storage !== 0) {
5305 if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
5306 // The directory must exist and be writable.
5307 return get_string('backuperrorinvaliddestination');
5310 return parent::write_setting($data);
5316 * Special debug setting
5318 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5320 class admin_setting_special_debug extends admin_setting_configselect {
5322 * Calls parent::__construct with specific arguments
5324 public function __construct() {
5325 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
5329 * Load the available choices for the select box
5331 * @return bool
5333 public function load_choices() {
5334 if (is_array($this->choices)) {
5335 return true;
5337 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
5338 DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
5339 DEBUG_NORMAL => get_string('debugnormal', 'admin'),
5340 DEBUG_ALL => get_string('debugall', 'admin'),
5341 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
5342 return true;
5348 * Special admin control
5350 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5352 class admin_setting_special_calendar_weekend extends admin_setting {
5354 * Calls parent::__construct with specific arguments
5356 public function __construct() {
5357 $name = 'calendar_weekend';
5358 $visiblename = get_string('calendar_weekend', 'admin');
5359 $description = get_string('helpweekenddays', 'admin');
5360 $default = array ('0', '6'); // Saturdays and Sundays
5361 parent::__construct($name, $visiblename, $description, $default);
5365 * Gets the current settings as an array
5367 * @return mixed Null if none, else array of settings
5369 public function get_setting() {
5370 $result = $this->config_read($this->name);
5371 if (is_null($result)) {
5372 return NULL;
5374 if ($result === '') {
5375 return array();
5377 $settings = array();
5378 for ($i=0; $i<7; $i++) {
5379 if ($result & (1 << $i)) {
5380 $settings[] = $i;
5383 return $settings;
5387 * Save the new settings
5389 * @param array $data Array of new settings
5390 * @return bool
5392 public function write_setting($data) {
5393 if (!is_array($data)) {
5394 return '';
5396 unset($data['xxxxx']);
5397 $result = 0;
5398 foreach($data as $index) {
5399 $result |= 1 << $index;
5401 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
5405 * Return XHTML to display the control
5407 * @param array $data array of selected days
5408 * @param string $query
5409 * @return string XHTML for display (field + wrapping div(s)
5411 public function output_html($data, $query='') {
5412 global $OUTPUT;
5414 // The order matters very much because of the implied numeric keys.
5415 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5416 $context = (object) [
5417 'name' => $this->get_full_name(),
5418 'id' => $this->get_id(),
5419 'days' => array_map(function($index) use ($days, $data) {
5420 return [
5421 'index' => $index,
5422 'label' => get_string($days[$index], 'calendar'),
5423 'checked' => in_array($index, $data)
5425 }, array_keys($days))
5428 $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
5430 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5437 * Admin setting that allows a user to pick a behaviour.
5439 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5441 class admin_setting_question_behaviour extends admin_setting_configselect {
5443 * @param string $name name of config variable
5444 * @param string $visiblename display name
5445 * @param string $description description
5446 * @param string $default default.
5448 public function __construct($name, $visiblename, $description, $default) {
5449 parent::__construct($name, $visiblename, $description, $default, null);
5453 * Load list of behaviours as choices
5454 * @return bool true => success, false => error.
5456 public function load_choices() {
5457 global $CFG;
5458 require_once($CFG->dirroot . '/question/engine/lib.php');
5459 $this->choices = question_engine::get_behaviour_options('');
5460 return true;
5466 * Admin setting that allows a user to pick appropriate roles for something.
5468 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5470 class admin_setting_pickroles extends admin_setting_configmulticheckbox {
5471 /** @var array Array of capabilities which identify roles */
5472 private $types;
5475 * @param string $name Name of config variable
5476 * @param string $visiblename Display name
5477 * @param string $description Description
5478 * @param array $types Array of archetypes which identify
5479 * roles that will be enabled by default.
5481 public function __construct($name, $visiblename, $description, $types) {
5482 parent::__construct($name, $visiblename, $description, NULL, NULL);
5483 $this->types = $types;
5487 * Load roles as choices
5489 * @return bool true=>success, false=>error
5491 public function load_choices() {
5492 global $CFG, $DB;
5493 if (during_initial_install()) {
5494 return false;
5496 if (is_array($this->choices)) {
5497 return true;
5499 if ($roles = get_all_roles()) {
5500 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
5501 return true;
5502 } else {
5503 return false;
5508 * Return the default setting for this control
5510 * @return array Array of default settings
5512 public function get_defaultsetting() {
5513 global $CFG;
5515 if (during_initial_install()) {
5516 return null;
5518 $result = array();
5519 foreach($this->types as $archetype) {
5520 if ($caproles = get_archetype_roles($archetype)) {
5521 foreach ($caproles as $caprole) {
5522 $result[$caprole->id] = 1;
5526 return $result;
5532 * Admin setting that is a list of installed filter plugins.
5534 * @copyright 2015 The Open University
5535 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5537 class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
5540 * Constructor
5542 * @param string $name unique ascii name, either 'mysetting' for settings
5543 * that in config, or 'myplugin/mysetting' for ones in config_plugins.
5544 * @param string $visiblename localised name
5545 * @param string $description localised long description
5546 * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
5548 public function __construct($name, $visiblename, $description, $default) {
5549 if (empty($default)) {
5550 $default = array();
5552 $this->load_choices();
5553 foreach ($default as $plugin) {
5554 if (!isset($this->choices[$plugin])) {
5555 unset($default[$plugin]);
5558 parent::__construct($name, $visiblename, $description, $default, null);
5561 public function load_choices() {
5562 if (is_array($this->choices)) {
5563 return true;
5565 $this->choices = array();
5567 foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
5568 $this->choices[$plugin] = filter_get_name($plugin);
5570 return true;
5576 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
5578 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5580 class admin_setting_configtext_with_advanced extends admin_setting_configtext {
5582 * Constructor
5583 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5584 * @param string $visiblename localised
5585 * @param string $description long localised info
5586 * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
5587 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
5588 * @param int $size default field size
5590 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
5591 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
5592 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5598 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
5600 * @copyright 2009 Petr Skoda (http://skodak.org)
5601 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5603 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
5606 * Constructor
5607 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5608 * @param string $visiblename localised
5609 * @param string $description long localised info
5610 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
5611 * @param string $yes value used when checked
5612 * @param string $no value used when not checked
5614 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5615 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5616 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5623 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
5625 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
5627 * @copyright 2010 Sam Hemelryk
5628 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5630 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
5632 * Constructor
5633 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5634 * @param string $visiblename localised
5635 * @param string $description long localised info
5636 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5637 * @param string $yes value used when checked
5638 * @param string $no value used when not checked
5640 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5641 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5642 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5648 * Autocomplete as you type form element.
5650 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5652 class admin_setting_configselect_autocomplete extends admin_setting_configselect {
5653 /** @var boolean $tags Should we allow typing new entries to the field? */
5654 protected $tags = false;
5655 /** @var string $ajax Name of an AMD module to send/process ajax requests. */
5656 protected $ajax = '';
5657 /** @var string $placeholder Placeholder text for an empty list. */
5658 protected $placeholder = '';
5659 /** @var bool $casesensitive Whether the search has to be case-sensitive. */
5660 protected $casesensitive = false;
5661 /** @var bool $showsuggestions Show suggestions by default - but this can be turned off. */
5662 protected $showsuggestions = true;
5663 /** @var string $noselectionstring String that is shown when there are no selections. */
5664 protected $noselectionstring = '';
5667 * Returns XHTML select field and wrapping div(s)
5669 * @see output_select_html()
5671 * @param string $data the option to show as selected
5672 * @param string $query
5673 * @return string XHTML field and wrapping div
5675 public function output_html($data, $query='') {
5676 global $PAGE;
5678 $html = parent::output_html($data, $query);
5680 if ($html === '') {
5681 return $html;
5684 $this->placeholder = get_string('search');
5686 $params = array('#' . $this->get_id(), $this->tags, $this->ajax,
5687 $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring);
5689 // Load autocomplete wrapper for select2 library.
5690 $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params);
5692 return $html;
5697 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
5699 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5701 class admin_setting_configselect_with_advanced extends admin_setting_configselect {
5703 * Calls parent::__construct with specific arguments
5705 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5706 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5707 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5713 * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
5715 * @copyright 2017 Marina Glancy
5716 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5718 class admin_setting_configselect_with_lock extends admin_setting_configselect {
5720 * Constructor
5721 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
5722 * or 'myplugin/mysetting' for ones in config_plugins.
5723 * @param string $visiblename localised
5724 * @param string $description long localised info
5725 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5726 * @param array $choices array of $value=>$label for each selection
5728 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5729 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5730 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5736 * Graded roles in gradebook
5738 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5740 class admin_setting_special_gradebookroles extends admin_setting_pickroles {
5742 * Calls parent::__construct with specific arguments
5744 public function __construct() {
5745 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
5746 get_string('configgradebookroles', 'admin'),
5747 array('student'));
5754 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5756 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
5758 * Saves the new settings passed in $data
5760 * @param string $data
5761 * @return mixed string or Array
5763 public function write_setting($data) {
5764 global $CFG, $DB;
5766 $oldvalue = $this->config_read($this->name);
5767 $return = parent::write_setting($data);
5768 $newvalue = $this->config_read($this->name);
5770 if ($oldvalue !== $newvalue) {
5771 // force full regrading
5772 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
5775 return $return;
5781 * Which roles to show on course description page
5783 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5785 class admin_setting_special_coursecontact extends admin_setting_pickroles {
5787 * Calls parent::__construct with specific arguments
5789 public function __construct() {
5790 parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
5791 get_string('coursecontact_desc', 'admin'),
5792 array('editingteacher'));
5793 $this->set_updatedcallback(function (){
5794 cache::make('core', 'coursecontacts')->purge();
5802 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5804 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
5806 * Calls parent::__construct with specific arguments
5808 public function __construct() {
5809 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
5810 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
5814 * Old syntax of class constructor. Deprecated in PHP7.
5816 * @deprecated since Moodle 3.1
5818 public function admin_setting_special_gradelimiting() {
5819 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
5820 self::__construct();
5824 * Force site regrading
5826 function regrade_all() {
5827 global $CFG;
5828 require_once("$CFG->libdir/gradelib.php");
5829 grade_force_site_regrading();
5833 * Saves the new settings
5835 * @param mixed $data
5836 * @return string empty string or error message
5838 function write_setting($data) {
5839 $previous = $this->get_setting();
5841 if ($previous === null) {
5842 if ($data) {
5843 $this->regrade_all();
5845 } else {
5846 if ($data != $previous) {
5847 $this->regrade_all();
5850 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
5856 * Special setting for $CFG->grade_minmaxtouse.
5858 * @package core
5859 * @copyright 2015 Frédéric Massart - FMCorz.net
5860 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5862 class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
5865 * Constructor.
5867 public function __construct() {
5868 parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
5869 new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
5870 array(
5871 GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
5872 GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
5878 * Saves the new setting.
5880 * @param mixed $data
5881 * @return string empty string or error message
5883 function write_setting($data) {
5884 global $CFG;
5886 $previous = $this->get_setting();
5887 $result = parent::write_setting($data);
5889 // If saved and the value has changed.
5890 if (empty($result) && $previous != $data) {
5891 require_once($CFG->libdir . '/gradelib.php');
5892 grade_force_site_regrading();
5895 return $result;
5902 * Primary grade export plugin - has state tracking.
5904 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5906 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
5908 * Calls parent::__construct with specific arguments
5910 public function __construct() {
5911 parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
5912 get_string('configgradeexport', 'admin'), array(), NULL);
5916 * Load the available choices for the multicheckbox
5918 * @return bool always returns true
5920 public function load_choices() {
5921 if (is_array($this->choices)) {
5922 return true;
5924 $this->choices = array();
5926 if ($plugins = core_component::get_plugin_list('gradeexport')) {
5927 foreach($plugins as $plugin => $unused) {
5928 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
5931 return true;
5937 * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
5939 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5941 class admin_setting_special_gradepointdefault extends admin_setting_configtext {
5943 * Config gradepointmax constructor
5945 * @param string $name Overidden by "gradepointmax"
5946 * @param string $visiblename Overridden by "gradepointmax" language string.
5947 * @param string $description Overridden by "gradepointmax_help" language string.
5948 * @param string $defaultsetting Not used, overridden by 100.
5949 * @param mixed $paramtype Overridden by PARAM_INT.
5950 * @param int $size Overridden by 5.
5952 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
5953 $name = 'gradepointdefault';
5954 $visiblename = get_string('gradepointdefault', 'grades');
5955 $description = get_string('gradepointdefault_help', 'grades');
5956 $defaultsetting = 100;
5957 $paramtype = PARAM_INT;
5958 $size = 5;
5959 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
5963 * Validate data before storage
5964 * @param string $data The submitted data
5965 * @return bool|string true if ok, string if error found
5967 public function validate($data) {
5968 global $CFG;
5969 if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
5970 return true;
5971 } else {
5972 return get_string('gradepointdefault_validateerror', 'grades');
5979 * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
5981 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5983 class admin_setting_special_gradepointmax extends admin_setting_configtext {
5986 * Config gradepointmax constructor
5988 * @param string $name Overidden by "gradepointmax"
5989 * @param string $visiblename Overridden by "gradepointmax" language string.
5990 * @param string $description Overridden by "gradepointmax_help" language string.
5991 * @param string $defaultsetting Not used, overridden by 100.
5992 * @param mixed $paramtype Overridden by PARAM_INT.
5993 * @param int $size Overridden by 5.
5995 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
5996 $name = 'gradepointmax';
5997 $visiblename = get_string('gradepointmax', 'grades');
5998 $description = get_string('gradepointmax_help', 'grades');
5999 $defaultsetting = 100;
6000 $paramtype = PARAM_INT;
6001 $size = 5;
6002 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
6006 * Save the selected setting
6008 * @param string $data The selected site
6009 * @return string empty string or error message
6011 public function write_setting($data) {
6012 if ($data === '') {
6013 $data = (int)$this->defaultsetting;
6014 } else {
6015 $data = $data;
6017 return parent::write_setting($data);
6021 * Validate data before storage
6022 * @param string $data The submitted data
6023 * @return bool|string true if ok, string if error found
6025 public function validate($data) {
6026 if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
6027 return true;
6028 } else {
6029 return get_string('gradepointmax_validateerror', 'grades');
6034 * Return an XHTML string for the setting
6035 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6036 * @param string $query search query to be highlighted
6037 * @return string XHTML to display control
6039 public function output_html($data, $query = '') {
6040 global $OUTPUT;
6042 $default = $this->get_defaultsetting();
6043 $context = (object) [
6044 'size' => $this->size,
6045 'id' => $this->get_id(),
6046 'name' => $this->get_full_name(),
6047 'value' => $data,
6048 'attributes' => [
6049 'maxlength' => 5
6051 'forceltr' => $this->get_force_ltr()
6053 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
6055 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
6061 * Grade category settings
6063 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6065 class admin_setting_gradecat_combo extends admin_setting {
6066 /** @var array Array of choices */
6067 public $choices;
6070 * Sets choices and calls parent::__construct with passed arguments
6071 * @param string $name
6072 * @param string $visiblename
6073 * @param string $description
6074 * @param mixed $defaultsetting string or array depending on implementation
6075 * @param array $choices An array of choices for the control
6077 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
6078 $this->choices = $choices;
6079 parent::__construct($name, $visiblename, $description, $defaultsetting);
6083 * Return the current setting(s) array
6085 * @return array Array of value=>xx, forced=>xx, adv=>xx
6087 public function get_setting() {
6088 global $CFG;
6090 $value = $this->config_read($this->name);
6091 $flag = $this->config_read($this->name.'_flag');
6093 if (is_null($value) or is_null($flag)) {
6094 return NULL;
6097 $flag = (int)$flag;
6098 $forced = (boolean)(1 & $flag); // first bit
6099 $adv = (boolean)(2 & $flag); // second bit
6101 return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
6105 * Save the new settings passed in $data
6107 * @todo Add vartype handling to ensure $data is array
6108 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6109 * @return string empty or error message
6111 public function write_setting($data) {
6112 global $CFG;
6114 $value = $data['value'];
6115 $forced = empty($data['forced']) ? 0 : 1;
6116 $adv = empty($data['adv']) ? 0 : 2;
6117 $flag = ($forced | $adv); //bitwise or
6119 if (!in_array($value, array_keys($this->choices))) {
6120 return 'Error setting ';
6123 $oldvalue = $this->config_read($this->name);
6124 $oldflag = (int)$this->config_read($this->name.'_flag');
6125 $oldforced = (1 & $oldflag); // first bit
6127 $result1 = $this->config_write($this->name, $value);
6128 $result2 = $this->config_write($this->name.'_flag', $flag);
6130 // force regrade if needed
6131 if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
6132 require_once($CFG->libdir.'/gradelib.php');
6133 grade_category::updated_forced_settings();
6136 if ($result1 and $result2) {
6137 return '';
6138 } else {
6139 return get_string('errorsetting', 'admin');
6144 * Return XHTML to display the field and wrapping div
6146 * @todo Add vartype handling to ensure $data is array
6147 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6148 * @param string $query
6149 * @return string XHTML to display control
6151 public function output_html($data, $query='') {
6152 global $OUTPUT;
6154 $value = $data['value'];
6156 $default = $this->get_defaultsetting();
6157 if (!is_null($default)) {
6158 $defaultinfo = array();
6159 if (isset($this->choices[$default['value']])) {
6160 $defaultinfo[] = $this->choices[$default['value']];
6162 if (!empty($default['forced'])) {
6163 $defaultinfo[] = get_string('force');
6165 if (!empty($default['adv'])) {
6166 $defaultinfo[] = get_string('advanced');
6168 $defaultinfo = implode(', ', $defaultinfo);
6170 } else {
6171 $defaultinfo = NULL;
6174 $options = $this->choices;
6175 $context = (object) [
6176 'id' => $this->get_id(),
6177 'name' => $this->get_full_name(),
6178 'forced' => !empty($data['forced']),
6179 'advanced' => !empty($data['adv']),
6180 'options' => array_map(function($option) use ($options, $value) {
6181 return [
6182 'value' => $option,
6183 'name' => $options[$option],
6184 'selected' => $option == $value
6186 }, array_keys($options)),
6189 $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
6191 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
6197 * Selection of grade report in user profiles
6199 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6201 class admin_setting_grade_profilereport extends admin_setting_configselect {
6203 * Calls parent::__construct with specific arguments
6205 public function __construct() {
6206 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
6210 * Loads an array of choices for the configselect control
6212 * @return bool always return true
6214 public function load_choices() {
6215 if (is_array($this->choices)) {
6216 return true;
6218 $this->choices = array();
6220 global $CFG;
6221 require_once($CFG->libdir.'/gradelib.php');
6223 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6224 if (file_exists($plugindir.'/lib.php')) {
6225 require_once($plugindir.'/lib.php');
6226 $functionname = 'grade_report_'.$plugin.'_profilereport';
6227 if (function_exists($functionname)) {
6228 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
6232 return true;
6237 * Provides a selection of grade reports to be used for "grades".
6239 * @copyright 2015 Adrian Greeve <adrian@moodle.com>
6240 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6242 class admin_setting_my_grades_report extends admin_setting_configselect {
6245 * Calls parent::__construct with specific arguments.
6247 public function __construct() {
6248 parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
6249 new lang_string('mygrades_desc', 'grades'), 'overview', null);
6253 * Loads an array of choices for the configselect control.
6255 * @return bool always returns true.
6257 public function load_choices() {
6258 global $CFG; // Remove this line and behold the horror of behat test failures!
6259 $this->choices = array();
6260 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6261 if (file_exists($plugindir . '/lib.php')) {
6262 require_once($plugindir . '/lib.php');
6263 // Check to see if the class exists. Check the correct plugin convention first.
6264 if (class_exists('gradereport_' . $plugin)) {
6265 $classname = 'gradereport_' . $plugin;
6266 } else if (class_exists('grade_report_' . $plugin)) {
6267 // We are using the old plugin naming convention.
6268 $classname = 'grade_report_' . $plugin;
6269 } else {
6270 continue;
6272 if ($classname::supports_mygrades()) {
6273 $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
6277 // Add an option to specify an external url.
6278 $this->choices['external'] = get_string('externalurl', 'grades');
6279 return true;
6284 * Special class for register auth selection
6286 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6288 class admin_setting_special_registerauth extends admin_setting_configselect {
6290 * Calls parent::__construct with specific arguments
6292 public function __construct() {
6293 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
6297 * Returns the default option
6299 * @return string empty or default option
6301 public function get_defaultsetting() {
6302 $this->load_choices();
6303 $defaultsetting = parent::get_defaultsetting();
6304 if (array_key_exists($defaultsetting, $this->choices)) {
6305 return $defaultsetting;
6306 } else {
6307 return '';
6312 * Loads the possible choices for the array
6314 * @return bool always returns true
6316 public function load_choices() {
6317 global $CFG;
6319 if (is_array($this->choices)) {
6320 return true;
6322 $this->choices = array();
6323 $this->choices[''] = get_string('disable');
6325 $authsenabled = get_enabled_auth_plugins();
6327 foreach ($authsenabled as $auth) {
6328 $authplugin = get_auth_plugin($auth);
6329 if (!$authplugin->can_signup()) {
6330 continue;
6332 // Get the auth title (from core or own auth lang files)
6333 $authtitle = $authplugin->get_title();
6334 $this->choices[$auth] = $authtitle;
6336 return true;
6342 * General plugins manager
6344 class admin_page_pluginsoverview extends admin_externalpage {
6347 * Sets basic information about the external page
6349 public function __construct() {
6350 global $CFG;
6351 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
6352 "$CFG->wwwroot/$CFG->admin/plugins.php");
6357 * Module manage page
6359 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6361 class admin_page_managemods extends admin_externalpage {
6363 * Calls parent::__construct with specific arguments
6365 public function __construct() {
6366 global $CFG;
6367 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
6371 * Try to find the specified module
6373 * @param string $query The module to search for
6374 * @return array
6376 public function search($query) {
6377 global $CFG, $DB;
6378 if ($result = parent::search($query)) {
6379 return $result;
6382 $found = false;
6383 if ($modules = $DB->get_records('modules')) {
6384 foreach ($modules as $module) {
6385 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
6386 continue;
6388 if (strpos($module->name, $query) !== false) {
6389 $found = true;
6390 break;
6392 $strmodulename = get_string('modulename', $module->name);
6393 if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
6394 $found = true;
6395 break;
6399 if ($found) {
6400 $result = new stdClass();
6401 $result->page = $this;
6402 $result->settings = array();
6403 return array($this->name => $result);
6404 } else {
6405 return array();
6412 * Special class for enrol plugins management.
6414 * @copyright 2010 Petr Skoda {@link http://skodak.org}
6415 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6417 class admin_setting_manageenrols extends admin_setting {
6419 * Calls parent::__construct with specific arguments
6421 public function __construct() {
6422 $this->nosave = true;
6423 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
6427 * Always returns true, does nothing
6429 * @return true
6431 public function get_setting() {
6432 return true;
6436 * Always returns true, does nothing
6438 * @return true
6440 public function get_defaultsetting() {
6441 return true;
6445 * Always returns '', does not write anything
6447 * @return string Always returns ''
6449 public function write_setting($data) {
6450 // do not write any setting
6451 return '';
6455 * Checks if $query is one of the available enrol plugins
6457 * @param string $query The string to search for
6458 * @return bool Returns true if found, false if not
6460 public function is_related($query) {
6461 if (parent::is_related($query)) {
6462 return true;
6465 $query = core_text::strtolower($query);
6466 $enrols = enrol_get_plugins(false);
6467 foreach ($enrols as $name=>$enrol) {
6468 $localised = get_string('pluginname', 'enrol_'.$name);
6469 if (strpos(core_text::strtolower($name), $query) !== false) {
6470 return true;
6472 if (strpos(core_text::strtolower($localised), $query) !== false) {
6473 return true;
6476 return false;
6480 * Builds the XHTML to display the control
6482 * @param string $data Unused
6483 * @param string $query
6484 * @return string
6486 public function output_html($data, $query='') {
6487 global $CFG, $OUTPUT, $DB, $PAGE;
6489 // Display strings.
6490 $strup = get_string('up');
6491 $strdown = get_string('down');
6492 $strsettings = get_string('settings');
6493 $strenable = get_string('enable');
6494 $strdisable = get_string('disable');
6495 $struninstall = get_string('uninstallplugin', 'core_admin');
6496 $strusage = get_string('enrolusage', 'enrol');
6497 $strversion = get_string('version');
6498 $strtest = get_string('testsettings', 'core_enrol');
6500 $pluginmanager = core_plugin_manager::instance();
6502 $enrols_available = enrol_get_plugins(false);
6503 $active_enrols = enrol_get_plugins(true);
6505 $allenrols = array();
6506 foreach ($active_enrols as $key=>$enrol) {
6507 $allenrols[$key] = true;
6509 foreach ($enrols_available as $key=>$enrol) {
6510 $allenrols[$key] = true;
6512 // Now find all borked plugins and at least allow then to uninstall.
6513 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
6514 foreach ($condidates as $candidate) {
6515 if (empty($allenrols[$candidate])) {
6516 $allenrols[$candidate] = true;
6520 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
6521 $return .= $OUTPUT->box_start('generalbox enrolsui');
6523 $table = new html_table();
6524 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
6525 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6526 $table->id = 'courseenrolmentplugins';
6527 $table->attributes['class'] = 'admintable generaltable';
6528 $table->data = array();
6530 // Iterate through enrol plugins and add to the display table.
6531 $updowncount = 1;
6532 $enrolcount = count($active_enrols);
6533 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
6534 $printed = array();
6535 foreach($allenrols as $enrol => $unused) {
6536 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
6537 $version = get_config('enrol_'.$enrol, 'version');
6538 if ($version === false) {
6539 $version = '';
6542 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
6543 $name = get_string('pluginname', 'enrol_'.$enrol);
6544 } else {
6545 $name = $enrol;
6547 // Usage.
6548 $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
6549 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
6550 $usage = "$ci / $cp";
6552 // Hide/show links.
6553 $class = '';
6554 if (isset($active_enrols[$enrol])) {
6555 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
6556 $hideshow = "<a href=\"$aurl\">";
6557 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
6558 $enabled = true;
6559 $displayname = $name;
6560 } else if (isset($enrols_available[$enrol])) {
6561 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
6562 $hideshow = "<a href=\"$aurl\">";
6563 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
6564 $enabled = false;
6565 $displayname = $name;
6566 $class = 'dimmed_text';
6567 } else {
6568 $hideshow = '';
6569 $enabled = false;
6570 $displayname = '<span class="notifyproblem">'.$name.'</span>';
6572 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
6573 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
6574 } else {
6575 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
6578 // Up/down link (only if enrol is enabled).
6579 $updown = '';
6580 if ($enabled) {
6581 if ($updowncount > 1) {
6582 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
6583 $updown .= "<a href=\"$aurl\">";
6584 $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
6585 } else {
6586 $updown .= $OUTPUT->spacer() . '&nbsp;';
6588 if ($updowncount < $enrolcount) {
6589 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
6590 $updown .= "<a href=\"$aurl\">";
6591 $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
6592 } else {
6593 $updown .= $OUTPUT->spacer() . '&nbsp;';
6595 ++$updowncount;
6598 // Add settings link.
6599 if (!$version) {
6600 $settings = '';
6601 } else if ($surl = $plugininfo->get_settings_url()) {
6602 $settings = html_writer::link($surl, $strsettings);
6603 } else {
6604 $settings = '';
6607 // Add uninstall info.
6608 $uninstall = '';
6609 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
6610 $uninstall = html_writer::link($uninstallurl, $struninstall);
6613 $test = '';
6614 if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
6615 $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey()));
6616 $test = html_writer::link($testsettingsurl, $strtest);
6619 // Add a row to the table.
6620 $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
6621 if ($class) {
6622 $row->attributes['class'] = $class;
6624 $table->data[] = $row;
6626 $printed[$enrol] = true;
6629 $return .= html_writer::table($table);
6630 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
6631 $return .= $OUTPUT->box_end();
6632 return highlight($query, $return);
6638 * Blocks manage page
6640 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6642 class admin_page_manageblocks extends admin_externalpage {
6644 * Calls parent::__construct with specific arguments
6646 public function __construct() {
6647 global $CFG;
6648 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
6652 * Search for a specific block
6654 * @param string $query The string to search for
6655 * @return array
6657 public function search($query) {
6658 global $CFG, $DB;
6659 if ($result = parent::search($query)) {
6660 return $result;
6663 $found = false;
6664 if ($blocks = $DB->get_records('block')) {
6665 foreach ($blocks as $block) {
6666 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
6667 continue;
6669 if (strpos($block->name, $query) !== false) {
6670 $found = true;
6671 break;
6673 $strblockname = get_string('pluginname', 'block_'.$block->name);
6674 if (strpos(core_text::strtolower($strblockname), $query) !== false) {
6675 $found = true;
6676 break;
6680 if ($found) {
6681 $result = new stdClass();
6682 $result->page = $this;
6683 $result->settings = array();
6684 return array($this->name => $result);
6685 } else {
6686 return array();
6692 * Message outputs configuration
6694 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6696 class admin_page_managemessageoutputs extends admin_externalpage {
6698 * Calls parent::__construct with specific arguments
6700 public function __construct() {
6701 global $CFG;
6702 parent::__construct('managemessageoutputs',
6703 get_string('defaultmessageoutputs', 'message'),
6704 new moodle_url('/admin/message.php')
6709 * Search for a specific message processor
6711 * @param string $query The string to search for
6712 * @return array
6714 public function search($query) {
6715 global $CFG, $DB;
6716 if ($result = parent::search($query)) {
6717 return $result;
6720 $found = false;
6721 if ($processors = get_message_processors()) {
6722 foreach ($processors as $processor) {
6723 if (!$processor->available) {
6724 continue;
6726 if (strpos($processor->name, $query) !== false) {
6727 $found = true;
6728 break;
6730 $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
6731 if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
6732 $found = true;
6733 break;
6737 if ($found) {
6738 $result = new stdClass();
6739 $result->page = $this;
6740 $result->settings = array();
6741 return array($this->name => $result);
6742 } else {
6743 return array();
6749 * Manage question behaviours page
6751 * @copyright 2011 The Open University
6752 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6754 class admin_page_manageqbehaviours extends admin_externalpage {
6756 * Constructor
6758 public function __construct() {
6759 global $CFG;
6760 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
6761 new moodle_url('/admin/qbehaviours.php'));
6765 * Search question behaviours for the specified string
6767 * @param string $query The string to search for in question behaviours
6768 * @return array
6770 public function search($query) {
6771 global $CFG;
6772 if ($result = parent::search($query)) {
6773 return $result;
6776 $found = false;
6777 require_once($CFG->dirroot . '/question/engine/lib.php');
6778 foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
6779 if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
6780 $query) !== false) {
6781 $found = true;
6782 break;
6785 if ($found) {
6786 $result = new stdClass();
6787 $result->page = $this;
6788 $result->settings = array();
6789 return array($this->name => $result);
6790 } else {
6791 return array();
6798 * Question type manage page
6800 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6802 class admin_page_manageqtypes extends admin_externalpage {
6804 * Calls parent::__construct with specific arguments
6806 public function __construct() {
6807 global $CFG;
6808 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
6809 new moodle_url('/admin/qtypes.php'));
6813 * Search question types for the specified string
6815 * @param string $query The string to search for in question types
6816 * @return array
6818 public function search($query) {
6819 global $CFG;
6820 if ($result = parent::search($query)) {
6821 return $result;
6824 $found = false;
6825 require_once($CFG->dirroot . '/question/engine/bank.php');
6826 foreach (question_bank::get_all_qtypes() as $qtype) {
6827 if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
6828 $found = true;
6829 break;
6832 if ($found) {
6833 $result = new stdClass();
6834 $result->page = $this;
6835 $result->settings = array();
6836 return array($this->name => $result);
6837 } else {
6838 return array();
6844 class admin_page_manageportfolios extends admin_externalpage {
6846 * Calls parent::__construct with specific arguments
6848 public function __construct() {
6849 global $CFG;
6850 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
6851 "$CFG->wwwroot/$CFG->admin/portfolio.php");
6855 * Searches page for the specified string.
6856 * @param string $query The string to search for
6857 * @return bool True if it is found on this page
6859 public function search($query) {
6860 global $CFG;
6861 if ($result = parent::search($query)) {
6862 return $result;
6865 $found = false;
6866 $portfolios = core_component::get_plugin_list('portfolio');
6867 foreach ($portfolios as $p => $dir) {
6868 if (strpos($p, $query) !== false) {
6869 $found = true;
6870 break;
6873 if (!$found) {
6874 foreach (portfolio_instances(false, false) as $instance) {
6875 $title = $instance->get('name');
6876 if (strpos(core_text::strtolower($title), $query) !== false) {
6877 $found = true;
6878 break;
6883 if ($found) {
6884 $result = new stdClass();
6885 $result->page = $this;
6886 $result->settings = array();
6887 return array($this->name => $result);
6888 } else {
6889 return array();
6895 class admin_page_managerepositories extends admin_externalpage {
6897 * Calls parent::__construct with specific arguments
6899 public function __construct() {
6900 global $CFG;
6901 parent::__construct('managerepositories', get_string('manage',
6902 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
6906 * Searches page for the specified string.
6907 * @param string $query The string to search for
6908 * @return bool True if it is found on this page
6910 public function search($query) {
6911 global $CFG;
6912 if ($result = parent::search($query)) {
6913 return $result;
6916 $found = false;
6917 $repositories= core_component::get_plugin_list('repository');
6918 foreach ($repositories as $p => $dir) {
6919 if (strpos($p, $query) !== false) {
6920 $found = true;
6921 break;
6924 if (!$found) {
6925 foreach (repository::get_types() as $instance) {
6926 $title = $instance->get_typename();
6927 if (strpos(core_text::strtolower($title), $query) !== false) {
6928 $found = true;
6929 break;
6934 if ($found) {
6935 $result = new stdClass();
6936 $result->page = $this;
6937 $result->settings = array();
6938 return array($this->name => $result);
6939 } else {
6940 return array();
6947 * Special class for authentication administration.
6949 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6951 class admin_setting_manageauths extends admin_setting {
6953 * Calls parent::__construct with specific arguments
6955 public function __construct() {
6956 $this->nosave = true;
6957 parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
6961 * Always returns true
6963 * @return true
6965 public function get_setting() {
6966 return true;
6970 * Always returns true
6972 * @return true
6974 public function get_defaultsetting() {
6975 return true;
6979 * Always returns '' and doesn't write anything
6981 * @return string Always returns ''
6983 public function write_setting($data) {
6984 // do not write any setting
6985 return '';
6989 * Search to find if Query is related to auth plugin
6991 * @param string $query The string to search for
6992 * @return bool true for related false for not
6994 public function is_related($query) {
6995 if (parent::is_related($query)) {
6996 return true;
6999 $authsavailable = core_component::get_plugin_list('auth');
7000 foreach ($authsavailable as $auth => $dir) {
7001 if (strpos($auth, $query) !== false) {
7002 return true;
7004 $authplugin = get_auth_plugin($auth);
7005 $authtitle = $authplugin->get_title();
7006 if (strpos(core_text::strtolower($authtitle), $query) !== false) {
7007 return true;
7010 return false;
7014 * Return XHTML to display control
7016 * @param mixed $data Unused
7017 * @param string $query
7018 * @return string highlight
7020 public function output_html($data, $query='') {
7021 global $CFG, $OUTPUT, $DB;
7023 // display strings
7024 $txt = get_strings(array('authenticationplugins', 'users', 'administration',
7025 'settings', 'edit', 'name', 'enable', 'disable',
7026 'up', 'down', 'none', 'users'));
7027 $txt->updown = "$txt->up/$txt->down";
7028 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7029 $txt->testsettings = get_string('testsettings', 'core_auth');
7031 $authsavailable = core_component::get_plugin_list('auth');
7032 get_enabled_auth_plugins(true); // fix the list of enabled auths
7033 if (empty($CFG->auth)) {
7034 $authsenabled = array();
7035 } else {
7036 $authsenabled = explode(',', $CFG->auth);
7039 // construct the display array, with enabled auth plugins at the top, in order
7040 $displayauths = array();
7041 $registrationauths = array();
7042 $registrationauths[''] = $txt->disable;
7043 $authplugins = array();
7044 foreach ($authsenabled as $auth) {
7045 $authplugin = get_auth_plugin($auth);
7046 $authplugins[$auth] = $authplugin;
7047 /// Get the auth title (from core or own auth lang files)
7048 $authtitle = $authplugin->get_title();
7049 /// Apply titles
7050 $displayauths[$auth] = $authtitle;
7051 if ($authplugin->can_signup()) {
7052 $registrationauths[$auth] = $authtitle;
7056 foreach ($authsavailable as $auth => $dir) {
7057 if (array_key_exists($auth, $displayauths)) {
7058 continue; //already in the list
7060 $authplugin = get_auth_plugin($auth);
7061 $authplugins[$auth] = $authplugin;
7062 /// Get the auth title (from core or own auth lang files)
7063 $authtitle = $authplugin->get_title();
7064 /// Apply titles
7065 $displayauths[$auth] = $authtitle;
7066 if ($authplugin->can_signup()) {
7067 $registrationauths[$auth] = $authtitle;
7071 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
7072 $return .= $OUTPUT->box_start('generalbox authsui');
7074 $table = new html_table();
7075 $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
7076 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7077 $table->data = array();
7078 $table->attributes['class'] = 'admintable generaltable';
7079 $table->id = 'manageauthtable';
7081 //add always enabled plugins first
7082 $displayname = $displayauths['manual'];
7083 $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
7084 $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
7085 $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
7086 $displayname = $displayauths['nologin'];
7087 $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
7088 $table->data[] = array($displayname, $usercount, '', '', '', '', '');
7091 // iterate through auth plugins and add to the display table
7092 $updowncount = 1;
7093 $authcount = count($authsenabled);
7094 $url = "auth.php?sesskey=" . sesskey();
7095 foreach ($displayauths as $auth => $name) {
7096 if ($auth == 'manual' or $auth == 'nologin') {
7097 continue;
7099 $class = '';
7100 // hide/show link
7101 if (in_array($auth, $authsenabled)) {
7102 $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
7103 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
7104 $enabled = true;
7105 $displayname = $name;
7107 else {
7108 $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
7109 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
7110 $enabled = false;
7111 $displayname = $name;
7112 $class = 'dimmed_text';
7115 $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
7117 // up/down link (only if auth is enabled)
7118 $updown = '';
7119 if ($enabled) {
7120 if ($updowncount > 1) {
7121 $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
7122 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
7124 else {
7125 $updown .= $OUTPUT->spacer() . '&nbsp;';
7127 if ($updowncount < $authcount) {
7128 $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
7129 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
7131 else {
7132 $updown .= $OUTPUT->spacer() . '&nbsp;';
7134 ++ $updowncount;
7137 // settings link
7138 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
7139 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
7140 } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
7141 $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
7142 } else {
7143 $settings = '';
7146 // Uninstall link.
7147 $uninstall = '';
7148 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
7149 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7152 $test = '';
7153 if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
7154 $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey()));
7155 $test = html_writer::link($testurl, $txt->testsettings);
7158 // Add a row to the table.
7159 $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
7160 if ($class) {
7161 $row->attributes['class'] = $class;
7163 $table->data[] = $row;
7165 $return .= html_writer::table($table);
7166 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
7167 $return .= $OUTPUT->box_end();
7168 return highlight($query, $return);
7174 * Special class for authentication administration.
7176 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7178 class admin_setting_manageeditors extends admin_setting {
7180 * Calls parent::__construct with specific arguments
7182 public function __construct() {
7183 $this->nosave = true;
7184 parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
7188 * Always returns true, does nothing
7190 * @return true
7192 public function get_setting() {
7193 return true;
7197 * Always returns true, does nothing
7199 * @return true
7201 public function get_defaultsetting() {
7202 return true;
7206 * Always returns '', does not write anything
7208 * @return string Always returns ''
7210 public function write_setting($data) {
7211 // do not write any setting
7212 return '';
7216 * Checks if $query is one of the available editors
7218 * @param string $query The string to search for
7219 * @return bool Returns true if found, false if not
7221 public function is_related($query) {
7222 if (parent::is_related($query)) {
7223 return true;
7226 $editors_available = editors_get_available();
7227 foreach ($editors_available as $editor=>$editorstr) {
7228 if (strpos($editor, $query) !== false) {
7229 return true;
7231 if (strpos(core_text::strtolower($editorstr), $query) !== false) {
7232 return true;
7235 return false;
7239 * Builds the XHTML to display the control
7241 * @param string $data Unused
7242 * @param string $query
7243 * @return string
7245 public function output_html($data, $query='') {
7246 global $CFG, $OUTPUT;
7248 // display strings
7249 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7250 'up', 'down', 'none'));
7251 $struninstall = get_string('uninstallplugin', 'core_admin');
7253 $txt->updown = "$txt->up/$txt->down";
7255 $editors_available = editors_get_available();
7256 $active_editors = explode(',', $CFG->texteditors);
7258 $active_editors = array_reverse($active_editors);
7259 foreach ($active_editors as $key=>$editor) {
7260 if (empty($editors_available[$editor])) {
7261 unset($active_editors[$key]);
7262 } else {
7263 $name = $editors_available[$editor];
7264 unset($editors_available[$editor]);
7265 $editors_available[$editor] = $name;
7268 if (empty($active_editors)) {
7269 //$active_editors = array('textarea');
7271 $editors_available = array_reverse($editors_available, true);
7272 $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
7273 $return .= $OUTPUT->box_start('generalbox editorsui');
7275 $table = new html_table();
7276 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7277 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7278 $table->id = 'editormanagement';
7279 $table->attributes['class'] = 'admintable generaltable';
7280 $table->data = array();
7282 // iterate through auth plugins and add to the display table
7283 $updowncount = 1;
7284 $editorcount = count($active_editors);
7285 $url = "editors.php?sesskey=" . sesskey();
7286 foreach ($editors_available as $editor => $name) {
7287 // hide/show link
7288 $class = '';
7289 if (in_array($editor, $active_editors)) {
7290 $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
7291 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
7292 $enabled = true;
7293 $displayname = $name;
7295 else {
7296 $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
7297 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
7298 $enabled = false;
7299 $displayname = $name;
7300 $class = 'dimmed_text';
7303 // up/down link (only if auth is enabled)
7304 $updown = '';
7305 if ($enabled) {
7306 if ($updowncount > 1) {
7307 $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
7308 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
7310 else {
7311 $updown .= $OUTPUT->spacer() . '&nbsp;';
7313 if ($updowncount < $editorcount) {
7314 $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
7315 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
7317 else {
7318 $updown .= $OUTPUT->spacer() . '&nbsp;';
7320 ++ $updowncount;
7323 // settings link
7324 if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
7325 $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
7326 $settings = "<a href='$eurl'>{$txt->settings}</a>";
7327 } else {
7328 $settings = '';
7331 $uninstall = '';
7332 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('editor_'.$editor, 'manage')) {
7333 $uninstall = html_writer::link($uninstallurl, $struninstall);
7336 // Add a row to the table.
7337 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7338 if ($class) {
7339 $row->attributes['class'] = $class;
7341 $table->data[] = $row;
7343 $return .= html_writer::table($table);
7344 $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
7345 $return .= $OUTPUT->box_end();
7346 return highlight($query, $return);
7351 * Special class for antiviruses administration.
7353 * @copyright 2015 Ruslan Kabalin, Lancaster University.
7354 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7356 class admin_setting_manageantiviruses extends admin_setting {
7358 * Calls parent::__construct with specific arguments
7360 public function __construct() {
7361 $this->nosave = true;
7362 parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', '');
7366 * Always returns true, does nothing
7368 * @return true
7370 public function get_setting() {
7371 return true;
7375 * Always returns true, does nothing
7377 * @return true
7379 public function get_defaultsetting() {
7380 return true;
7384 * Always returns '', does not write anything
7386 * @param string $data Unused
7387 * @return string Always returns ''
7389 public function write_setting($data) {
7390 // Do not write any setting.
7391 return '';
7395 * Checks if $query is one of the available editors
7397 * @param string $query The string to search for
7398 * @return bool Returns true if found, false if not
7400 public function is_related($query) {
7401 if (parent::is_related($query)) {
7402 return true;
7405 $antivirusesavailable = \core\antivirus\manager::get_available();
7406 foreach ($antivirusesavailable as $antivirus => $antivirusstr) {
7407 if (strpos($antivirus, $query) !== false) {
7408 return true;
7410 if (strpos(core_text::strtolower($antivirusstr), $query) !== false) {
7411 return true;
7414 return false;
7418 * Builds the XHTML to display the control
7420 * @param string $data Unused
7421 * @param string $query
7422 * @return string
7424 public function output_html($data, $query='') {
7425 global $CFG, $OUTPUT;
7427 // Display strings.
7428 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7429 'up', 'down', 'none'));
7430 $struninstall = get_string('uninstallplugin', 'core_admin');
7432 $txt->updown = "$txt->up/$txt->down";
7434 $antivirusesavailable = \core\antivirus\manager::get_available();
7435 $activeantiviruses = explode(',', $CFG->antiviruses);
7437 $activeantiviruses = array_reverse($activeantiviruses);
7438 foreach ($activeantiviruses as $key => $antivirus) {
7439 if (empty($antivirusesavailable[$antivirus])) {
7440 unset($activeantiviruses[$key]);
7441 } else {
7442 $name = $antivirusesavailable[$antivirus];
7443 unset($antivirusesavailable[$antivirus]);
7444 $antivirusesavailable[$antivirus] = $name;
7447 $antivirusesavailable = array_reverse($antivirusesavailable, true);
7448 $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true);
7449 $return .= $OUTPUT->box_start('generalbox antivirusesui');
7451 $table = new html_table();
7452 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7453 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7454 $table->id = 'antivirusmanagement';
7455 $table->attributes['class'] = 'admintable generaltable';
7456 $table->data = array();
7458 // Iterate through auth plugins and add to the display table.
7459 $updowncount = 1;
7460 $antiviruscount = count($activeantiviruses);
7461 $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey()));
7462 foreach ($antivirusesavailable as $antivirus => $name) {
7463 // Hide/show link.
7464 $class = '';
7465 if (in_array($antivirus, $activeantiviruses)) {
7466 $hideshowurl = $baseurl;
7467 $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus));
7468 $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable'));
7469 $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7470 $enabled = true;
7471 $displayname = $name;
7472 } else {
7473 $hideshowurl = $baseurl;
7474 $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus));
7475 $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable'));
7476 $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7477 $enabled = false;
7478 $displayname = $name;
7479 $class = 'dimmed_text';
7482 // Up/down link.
7483 $updown = '';
7484 if ($enabled) {
7485 if ($updowncount > 1) {
7486 $updownurl = $baseurl;
7487 $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus));
7488 $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup'));
7489 $updown = html_writer::link($updownurl, $updownimg);
7490 } else {
7491 $updownimg = $OUTPUT->spacer();
7493 if ($updowncount < $antiviruscount) {
7494 $updownurl = $baseurl;
7495 $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus));
7496 $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown'));
7497 $updown = html_writer::link($updownurl, $updownimg);
7498 } else {
7499 $updownimg = $OUTPUT->spacer();
7501 ++ $updowncount;
7504 // Settings link.
7505 if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) {
7506 $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus));
7507 $settings = html_writer::link($eurl, $txt->settings);
7508 } else {
7509 $settings = '';
7512 $uninstall = '';
7513 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) {
7514 $uninstall = html_writer::link($uninstallurl, $struninstall);
7517 // Add a row to the table.
7518 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7519 if ($class) {
7520 $row->attributes['class'] = $class;
7522 $table->data[] = $row;
7524 $return .= html_writer::table($table);
7525 $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin');
7526 $return .= $OUTPUT->box_end();
7527 return highlight($query, $return);
7532 * Special class for license administration.
7534 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7535 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7536 * @todo MDL-45184 This class will be deleted in Moodle 4.1.
7538 class admin_setting_managelicenses extends admin_setting {
7540 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7541 * @todo MDL-45184 This class will be deleted in Moodle 4.1
7543 public function __construct() {
7544 global $ADMIN;
7546 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7547 DEBUG_DEVELOPER);
7549 // Replace admin setting load with new external page load for tool_licensemanager, if not loaded already.
7550 if (!is_null($ADMIN->locate('licensemanager'))) {
7551 $temp = new admin_externalpage('licensemanager',
7552 get_string('licensemanager', 'tool_licensemanager'),
7553 \tool_licensemanager\helper::get_licensemanager_url());
7555 $ADMIN->add('license', $temp);
7560 * Always returns true, does nothing
7562 * @deprecated since Moodle 3.9 MDL-45184.
7563 * @todo MDL-45184 This method will be deleted in Moodle 4.1
7565 * @return true
7567 public function get_setting() {
7568 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7569 DEBUG_DEVELOPER);
7571 return true;
7575 * Always returns true, does nothing
7577 * @deprecated since Moodle 3.9 MDL-45184.
7578 * @todo MDL-45184 This method will be deleted in Moodle 4.1
7580 * @return true
7582 public function get_defaultsetting() {
7583 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7584 DEBUG_DEVELOPER);
7586 return true;
7590 * Always returns '', does not write anything
7592 * @deprecated since Moodle 3.9 MDL-45184.
7593 * @todo MDL-45184 This method will be deleted in Moodle 4.1
7595 * @return string Always returns ''
7597 public function write_setting($data) {
7598 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7599 DEBUG_DEVELOPER);
7601 // do not write any setting
7602 return '';
7606 * Builds the XHTML to display the control
7608 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7609 * @todo MDL-45184 This method will be deleted in Moodle 4.1
7611 * @param string $data Unused
7612 * @param string $query
7613 * @return string
7615 public function output_html($data, $query='') {
7616 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7617 DEBUG_DEVELOPER);
7619 redirect(\tool_licensemanager\helper::get_licensemanager_url());
7624 * Course formats manager. Allows to enable/disable formats and jump to settings
7626 class admin_setting_manageformats extends admin_setting {
7629 * Calls parent::__construct with specific arguments
7631 public function __construct() {
7632 $this->nosave = true;
7633 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
7637 * Always returns true
7639 * @return true
7641 public function get_setting() {
7642 return true;
7646 * Always returns true
7648 * @return true
7650 public function get_defaultsetting() {
7651 return true;
7655 * Always returns '' and doesn't write anything
7657 * @param mixed $data string or array, must not be NULL
7658 * @return string Always returns ''
7660 public function write_setting($data) {
7661 // do not write any setting
7662 return '';
7666 * Search to find if Query is related to format plugin
7668 * @param string $query The string to search for
7669 * @return bool true for related false for not
7671 public function is_related($query) {
7672 if (parent::is_related($query)) {
7673 return true;
7675 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7676 foreach ($formats as $format) {
7677 if (strpos($format->component, $query) !== false ||
7678 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7679 return true;
7682 return false;
7686 * Return XHTML to display control
7688 * @param mixed $data Unused
7689 * @param string $query
7690 * @return string highlight
7692 public function output_html($data, $query='') {
7693 global $CFG, $OUTPUT;
7694 $return = '';
7695 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
7696 $return .= $OUTPUT->box_start('generalbox formatsui');
7698 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7700 // display strings
7701 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7702 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7703 $txt->updown = "$txt->up/$txt->down";
7705 $table = new html_table();
7706 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7707 $table->align = array('left', 'center', 'center', 'center', 'center');
7708 $table->attributes['class'] = 'manageformattable generaltable admintable';
7709 $table->data = array();
7711 $cnt = 0;
7712 $defaultformat = get_config('moodlecourse', 'format');
7713 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7714 foreach ($formats as $format) {
7715 $url = new moodle_url('/admin/courseformats.php',
7716 array('sesskey' => sesskey(), 'format' => $format->name));
7717 $isdefault = '';
7718 $class = '';
7719 if ($format->is_enabled()) {
7720 $strformatname = $format->displayname;
7721 if ($defaultformat === $format->name) {
7722 $hideshow = $txt->default;
7723 } else {
7724 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7725 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7727 } else {
7728 $strformatname = $format->displayname;
7729 $class = 'dimmed_text';
7730 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7731 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7733 $updown = '';
7734 if ($cnt) {
7735 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7736 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7737 } else {
7738 $updown .= $spacer;
7740 if ($cnt < count($formats) - 1) {
7741 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7742 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7743 } else {
7744 $updown .= $spacer;
7746 $cnt++;
7747 $settings = '';
7748 if ($format->get_settings_url()) {
7749 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7751 $uninstall = '';
7752 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
7753 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7755 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7756 if ($class) {
7757 $row->attributes['class'] = $class;
7759 $table->data[] = $row;
7761 $return .= html_writer::table($table);
7762 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
7763 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
7764 $return .= $OUTPUT->box_end();
7765 return highlight($query, $return);
7770 * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
7772 * @package core
7773 * @copyright 2018 Toni Barbera
7774 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7776 class admin_setting_managecustomfields extends admin_setting {
7779 * Calls parent::__construct with specific arguments
7781 public function __construct() {
7782 $this->nosave = true;
7783 parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
7787 * Always returns true
7789 * @return true
7791 public function get_setting() {
7792 return true;
7796 * Always returns true
7798 * @return true
7800 public function get_defaultsetting() {
7801 return true;
7805 * Always returns '' and doesn't write anything
7807 * @param mixed $data string or array, must not be NULL
7808 * @return string Always returns ''
7810 public function write_setting($data) {
7811 // Do not write any setting.
7812 return '';
7816 * Search to find if Query is related to format plugin
7818 * @param string $query The string to search for
7819 * @return bool true for related false for not
7821 public function is_related($query) {
7822 if (parent::is_related($query)) {
7823 return true;
7825 $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7826 foreach ($formats as $format) {
7827 if (strpos($format->component, $query) !== false ||
7828 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7829 return true;
7832 return false;
7836 * Return XHTML to display control
7838 * @param mixed $data Unused
7839 * @param string $query
7840 * @return string highlight
7842 public function output_html($data, $query='') {
7843 global $CFG, $OUTPUT;
7844 $return = '';
7845 $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
7846 $return .= $OUTPUT->box_start('generalbox customfieldsui');
7848 $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7850 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
7851 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7852 $txt->updown = "$txt->up/$txt->down";
7854 $table = new html_table();
7855 $table->head = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
7856 $table->align = array('left', 'center', 'center', 'center');
7857 $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
7858 $table->data = array();
7860 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7861 foreach ($fields as $field) {
7862 $url = new moodle_url('/admin/customfields.php',
7863 array('sesskey' => sesskey(), 'field' => $field->name));
7865 if ($field->is_enabled()) {
7866 $strfieldname = $field->displayname;
7867 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7868 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7869 } else {
7870 $strfieldname = $field->displayname;
7871 $class = 'dimmed_text';
7872 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7873 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7875 $settings = '';
7876 if ($field->get_settings_url()) {
7877 $settings = html_writer::link($field->get_settings_url(), $txt->settings);
7879 $uninstall = '';
7880 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
7881 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7883 $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
7884 $table->data[] = $row;
7886 $return .= html_writer::table($table);
7887 $return .= $OUTPUT->box_end();
7888 return highlight($query, $return);
7893 * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
7895 * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net)
7896 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7898 class admin_setting_managedataformats extends admin_setting {
7901 * Calls parent::__construct with specific arguments
7903 public function __construct() {
7904 $this->nosave = true;
7905 parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
7909 * Always returns true
7911 * @return true
7913 public function get_setting() {
7914 return true;
7918 * Always returns true
7920 * @return true
7922 public function get_defaultsetting() {
7923 return true;
7927 * Always returns '' and doesn't write anything
7929 * @param mixed $data string or array, must not be NULL
7930 * @return string Always returns ''
7932 public function write_setting($data) {
7933 // Do not write any setting.
7934 return '';
7938 * Search to find if Query is related to format plugin
7940 * @param string $query The string to search for
7941 * @return bool true for related false for not
7943 public function is_related($query) {
7944 if (parent::is_related($query)) {
7945 return true;
7947 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7948 foreach ($formats as $format) {
7949 if (strpos($format->component, $query) !== false ||
7950 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7951 return true;
7954 return false;
7958 * Return XHTML to display control
7960 * @param mixed $data Unused
7961 * @param string $query
7962 * @return string highlight
7964 public function output_html($data, $query='') {
7965 global $CFG, $OUTPUT;
7966 $return = '';
7968 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7970 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7971 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7972 $txt->updown = "$txt->up/$txt->down";
7974 $table = new html_table();
7975 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7976 $table->align = array('left', 'center', 'center', 'center', 'center');
7977 $table->attributes['class'] = 'manageformattable generaltable admintable';
7978 $table->data = array();
7980 $cnt = 0;
7981 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7982 $totalenabled = 0;
7983 foreach ($formats as $format) {
7984 if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
7985 $totalenabled++;
7988 foreach ($formats as $format) {
7989 $status = $format->get_status();
7990 $url = new moodle_url('/admin/dataformats.php',
7991 array('sesskey' => sesskey(), 'name' => $format->name));
7993 $class = '';
7994 if ($format->is_enabled()) {
7995 $strformatname = $format->displayname;
7996 if ($totalenabled == 1&& $format->is_enabled()) {
7997 $hideshow = '';
7998 } else {
7999 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8000 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8002 } else {
8003 $class = 'dimmed_text';
8004 $strformatname = $format->displayname;
8005 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8006 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8009 $updown = '';
8010 if ($cnt) {
8011 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8012 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8013 } else {
8014 $updown .= $spacer;
8016 if ($cnt < count($formats) - 1) {
8017 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8018 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8019 } else {
8020 $updown .= $spacer;
8023 $uninstall = '';
8024 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8025 $uninstall = get_string('status_missing', 'core_plugin');
8026 } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8027 $uninstall = get_string('status_new', 'core_plugin');
8028 } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
8029 if ($totalenabled != 1 || !$format->is_enabled()) {
8030 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8034 $settings = '';
8035 if ($format->get_settings_url()) {
8036 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
8039 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
8040 if ($class) {
8041 $row->attributes['class'] = $class;
8043 $table->data[] = $row;
8044 $cnt++;
8046 $return .= html_writer::table($table);
8047 return highlight($query, $return);
8052 * Special class for filter administration.
8054 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8056 class admin_page_managefilters extends admin_externalpage {
8058 * Calls parent::__construct with specific arguments
8060 public function __construct() {
8061 global $CFG;
8062 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
8066 * Searches all installed filters for specified filter
8068 * @param string $query The filter(string) to search for
8069 * @param string $query
8071 public function search($query) {
8072 global $CFG;
8073 if ($result = parent::search($query)) {
8074 return $result;
8077 $found = false;
8078 $filternames = filter_get_all_installed();
8079 foreach ($filternames as $path => $strfiltername) {
8080 if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
8081 $found = true;
8082 break;
8084 if (strpos($path, $query) !== false) {
8085 $found = true;
8086 break;
8090 if ($found) {
8091 $result = new stdClass;
8092 $result->page = $this;
8093 $result->settings = array();
8094 return array($this->name => $result);
8095 } else {
8096 return array();
8102 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8103 * Requires a get_rank method on the plugininfo class for sorting.
8105 * @copyright 2017 Damyon Wiese
8106 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8108 abstract class admin_setting_manage_plugins extends admin_setting {
8111 * Get the admin settings section name (just a unique string)
8113 * @return string
8115 public function get_section_name() {
8116 return 'manage' . $this->get_plugin_type() . 'plugins';
8120 * Get the admin settings section title (use get_string).
8122 * @return string
8124 abstract public function get_section_title();
8127 * Get the type of plugin to manage.
8129 * @return string
8131 abstract public function get_plugin_type();
8134 * Get the name of the second column.
8136 * @return string
8138 public function get_info_column_name() {
8139 return '';
8143 * Get the type of plugin to manage.
8145 * @param plugininfo The plugin info class.
8146 * @return string
8148 abstract public function get_info_column($plugininfo);
8151 * Calls parent::__construct with specific arguments
8153 public function __construct() {
8154 $this->nosave = true;
8155 parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
8159 * Always returns true, does nothing
8161 * @return true
8163 public function get_setting() {
8164 return true;
8168 * Always returns true, does nothing
8170 * @return true
8172 public function get_defaultsetting() {
8173 return true;
8177 * Always returns '', does not write anything
8179 * @param mixed $data
8180 * @return string Always returns ''
8182 public function write_setting($data) {
8183 // Do not write any setting.
8184 return '';
8188 * Checks if $query is one of the available plugins of this type
8190 * @param string $query The string to search for
8191 * @return bool Returns true if found, false if not
8193 public function is_related($query) {
8194 if (parent::is_related($query)) {
8195 return true;
8198 $query = core_text::strtolower($query);
8199 $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
8200 foreach ($plugins as $name => $plugin) {
8201 $localised = $plugin->displayname;
8202 if (strpos(core_text::strtolower($name), $query) !== false) {
8203 return true;
8205 if (strpos(core_text::strtolower($localised), $query) !== false) {
8206 return true;
8209 return false;
8213 * The URL for the management page for this plugintype.
8215 * @return moodle_url
8217 protected function get_manage_url() {
8218 return new moodle_url('/admin/updatesetting.php');
8222 * Builds the HTML to display the control.
8224 * @param string $data Unused
8225 * @param string $query
8226 * @return string
8228 public function output_html($data, $query = '') {
8229 global $CFG, $OUTPUT, $DB, $PAGE;
8231 $context = (object) [
8232 'manageurl' => new moodle_url($this->get_manage_url(), [
8233 'type' => $this->get_plugin_type(),
8234 'sesskey' => sesskey(),
8236 'infocolumnname' => $this->get_info_column_name(),
8237 'plugins' => [],
8240 $pluginmanager = core_plugin_manager::instance();
8241 $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
8242 $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
8243 $plugins = array_merge($enabled, $allplugins);
8244 foreach ($plugins as $key => $plugin) {
8245 $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
8247 $pluginkey = (object) [
8248 'plugin' => $plugin->displayname,
8249 'enabled' => $plugin->is_enabled(),
8250 'togglelink' => '',
8251 'moveuplink' => '',
8252 'movedownlink' => '',
8253 'settingslink' => $plugin->get_settings_url(),
8254 'uninstalllink' => '',
8255 'info' => '',
8258 // Enable/Disable link.
8259 $togglelink = new moodle_url($pluginlink);
8260 if ($plugin->is_enabled()) {
8261 $toggletarget = false;
8262 $togglelink->param('action', 'disable');
8264 if (count($context->plugins)) {
8265 // This is not the first plugin.
8266 $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
8269 if (count($enabled) > count($context->plugins) + 1) {
8270 // This is not the last plugin.
8271 $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
8274 $pluginkey->info = $this->get_info_column($plugin);
8275 } else {
8276 $toggletarget = true;
8277 $togglelink->param('action', 'enable');
8280 $pluginkey->toggletarget = $toggletarget;
8281 $pluginkey->togglelink = $togglelink;
8283 $frankenstyle = $plugin->type . '_' . $plugin->name;
8284 if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
8285 // This plugin supports uninstallation.
8286 $pluginkey->uninstalllink = $uninstalllink;
8289 if (!empty($this->get_info_column_name())) {
8290 // This plugintype has an info column.
8291 $pluginkey->info = $this->get_info_column($plugin);
8294 $context->plugins[] = $pluginkey;
8297 $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
8298 return highlight($query, $str);
8303 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8304 * Requires a get_rank method on the plugininfo class for sorting.
8306 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
8307 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8309 class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
8310 public function get_section_title() {
8311 return get_string('type_fileconverter_plural', 'plugin');
8314 public function get_plugin_type() {
8315 return 'fileconverter';
8318 public function get_info_column_name() {
8319 return get_string('supportedconversions', 'plugin');
8322 public function get_info_column($plugininfo) {
8323 return $plugininfo->get_supported_conversions();
8328 * Special class for media player plugins management.
8330 * @copyright 2016 Marina Glancy
8331 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8333 class admin_setting_managemediaplayers extends admin_setting {
8335 * Calls parent::__construct with specific arguments
8337 public function __construct() {
8338 $this->nosave = true;
8339 parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
8343 * Always returns true, does nothing
8345 * @return true
8347 public function get_setting() {
8348 return true;
8352 * Always returns true, does nothing
8354 * @return true
8356 public function get_defaultsetting() {
8357 return true;
8361 * Always returns '', does not write anything
8363 * @param mixed $data
8364 * @return string Always returns ''
8366 public function write_setting($data) {
8367 // Do not write any setting.
8368 return '';
8372 * Checks if $query is one of the available enrol plugins
8374 * @param string $query The string to search for
8375 * @return bool Returns true if found, false if not
8377 public function is_related($query) {
8378 if (parent::is_related($query)) {
8379 return true;
8382 $query = core_text::strtolower($query);
8383 $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
8384 foreach ($plugins as $name => $plugin) {
8385 $localised = $plugin->displayname;
8386 if (strpos(core_text::strtolower($name), $query) !== false) {
8387 return true;
8389 if (strpos(core_text::strtolower($localised), $query) !== false) {
8390 return true;
8393 return false;
8397 * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8398 * @return \core\plugininfo\media[]
8400 protected function get_sorted_plugins() {
8401 $pluginmanager = core_plugin_manager::instance();
8403 $plugins = $pluginmanager->get_plugins_of_type('media');
8404 $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8406 // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8407 \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
8409 $order = array_values($enabledplugins);
8410 $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
8412 $sortedplugins = array();
8413 foreach ($order as $name) {
8414 $sortedplugins[$name] = $plugins[$name];
8417 return $sortedplugins;
8421 * Builds the XHTML to display the control
8423 * @param string $data Unused
8424 * @param string $query
8425 * @return string
8427 public function output_html($data, $query='') {
8428 global $CFG, $OUTPUT, $DB, $PAGE;
8430 // Display strings.
8431 $strup = get_string('up');
8432 $strdown = get_string('down');
8433 $strsettings = get_string('settings');
8434 $strenable = get_string('enable');
8435 $strdisable = get_string('disable');
8436 $struninstall = get_string('uninstallplugin', 'core_admin');
8437 $strversion = get_string('version');
8438 $strname = get_string('name');
8439 $strsupports = get_string('supports', 'core_media');
8441 $pluginmanager = core_plugin_manager::instance();
8443 $plugins = $this->get_sorted_plugins();
8444 $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8446 $return = $OUTPUT->box_start('generalbox mediaplayersui');
8448 $table = new html_table();
8449 $table->head = array($strname, $strsupports, $strversion,
8450 $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
8451 $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
8452 'centeralign', 'centeralign', 'centeralign', 'centeralign');
8453 $table->id = 'mediaplayerplugins';
8454 $table->attributes['class'] = 'admintable generaltable';
8455 $table->data = array();
8457 // Iterate through media plugins and add to the display table.
8458 $updowncount = 1;
8459 $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
8460 $printed = array();
8461 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8463 $usedextensions = [];
8464 foreach ($plugins as $name => $plugin) {
8465 $url->param('media', $name);
8466 $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
8467 $version = $plugininfo->versiondb;
8468 $supports = $plugininfo->supports($usedextensions);
8470 // Hide/show links.
8471 $class = '';
8472 if (!$plugininfo->is_installed_and_upgraded()) {
8473 $hideshow = '';
8474 $enabled = false;
8475 $displayname = '<span class="notifyproblem">'.$name.'</span>';
8476 } else {
8477 $enabled = $plugininfo->is_enabled();
8478 if ($enabled) {
8479 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
8480 $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
8481 } else {
8482 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
8483 $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
8484 $class = 'dimmed_text';
8486 $displayname = $plugin->displayname;
8487 if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
8488 $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
8491 if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
8492 $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
8493 } else {
8494 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
8497 // Up/down link (only if enrol is enabled).
8498 $updown = '';
8499 if ($enabled) {
8500 if ($updowncount > 1) {
8501 $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
8502 $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
8503 } else {
8504 $updown = $spacer;
8506 if ($updowncount < count($enabledplugins)) {
8507 $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
8508 $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
8509 } else {
8510 $updown .= $spacer;
8512 ++$updowncount;
8515 $uninstall = '';
8516 $status = $plugininfo->get_status();
8517 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8518 $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
8520 if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8521 $uninstall = get_string('status_new', 'core_plugin');
8522 } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
8523 $uninstall .= html_writer::link($uninstallurl, $struninstall);
8526 $settings = '';
8527 if ($plugininfo->get_settings_url()) {
8528 $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
8531 // Add a row to the table.
8532 $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
8533 if ($class) {
8534 $row->attributes['class'] = $class;
8536 $table->data[] = $row;
8538 $printed[$name] = true;
8541 $return .= html_writer::table($table);
8542 $return .= $OUTPUT->box_end();
8543 return highlight($query, $return);
8549 * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
8551 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
8552 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8554 class admin_setting_managecontentbankcontenttypes extends admin_setting {
8557 * Calls parent::__construct with specific arguments
8559 public function __construct() {
8560 $this->nosave = true;
8561 parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
8565 * Always returns true
8567 * @return true
8569 public function get_setting() {
8570 return true;
8574 * Always returns true
8576 * @return true
8578 public function get_defaultsetting() {
8579 return true;
8583 * Always returns '' and doesn't write anything
8585 * @param mixed $data string or array, must not be NULL
8586 * @return string Always returns ''
8588 public function write_setting($data) {
8589 // Do not write any setting.
8590 return '';
8594 * Search to find if Query is related to content bank plugin
8596 * @param string $query The string to search for
8597 * @return bool true for related false for not
8599 public function is_related($query) {
8600 if (parent::is_related($query)) {
8601 return true;
8603 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8604 foreach ($types as $type) {
8605 if (strpos($type->component, $query) !== false ||
8606 strpos(core_text::strtolower($type->displayname), $query) !== false) {
8607 return true;
8610 return false;
8614 * Return XHTML to display control
8616 * @param mixed $data Unused
8617 * @param string $query
8618 * @return string highlight
8620 public function output_html($data, $query='') {
8621 global $CFG, $OUTPUT;
8622 $return = '';
8624 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8625 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
8626 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
8628 $table = new html_table();
8629 $table->head = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
8630 $table->align = array('left', 'center', 'center', 'center', 'center');
8631 $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
8632 $table->data = array();
8633 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8635 $totalenabled = 0;
8636 $count = 0;
8637 foreach ($types as $type) {
8638 if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
8639 $totalenabled++;
8643 foreach ($types as $type) {
8644 $url = new moodle_url('/admin/contentbank.php',
8645 array('sesskey' => sesskey(), 'name' => $type->name));
8647 $class = '';
8648 $strtypename = $type->displayname;
8649 if ($type->is_enabled()) {
8650 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8651 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8652 } else {
8653 $class = 'dimmed_text';
8654 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8655 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8658 $updown = '';
8659 if ($count) {
8660 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8661 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8662 } else {
8663 $updown .= $spacer;
8665 if ($count < count($types) - 1) {
8666 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8667 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8668 } else {
8669 $updown .= $spacer;
8672 $settings = '';
8673 if ($type->get_settings_url()) {
8674 $settings = html_writer::link($type->get_settings_url(), $txt->settings);
8677 $uninstall = '';
8678 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
8679 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8682 $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
8683 if ($class) {
8684 $row->attributes['class'] = $class;
8686 $table->data[] = $row;
8687 $count++;
8689 $return .= html_writer::table($table);
8690 return highlight($query, $return);
8695 * Initialise admin page - this function does require login and permission
8696 * checks specified in page definition.
8698 * This function must be called on each admin page before other code.
8700 * @global moodle_page $PAGE
8702 * @param string $section name of page
8703 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
8704 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
8705 * added to the turn blocks editing on/off form, so this page reloads correctly.
8706 * @param string $actualurl if the actual page being viewed is not the normal one for this
8707 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
8708 * @param array $options Additional options that can be specified for page setup.
8709 * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
8711 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
8712 global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
8714 $PAGE->set_context(null); // hack - set context to something, by default to system context
8716 $site = get_site();
8717 require_login(null, false);
8719 if (!empty($options['pagelayout'])) {
8720 // A specific page layout has been requested.
8721 $PAGE->set_pagelayout($options['pagelayout']);
8722 } else if ($section === 'upgradesettings') {
8723 $PAGE->set_pagelayout('maintenance');
8724 } else {
8725 $PAGE->set_pagelayout('admin');
8728 $adminroot = admin_get_root(false, false); // settings not required for external pages
8729 $extpage = $adminroot->locate($section, true);
8731 if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
8732 // The requested section isn't in the admin tree
8733 // It could be because the user has inadequate capapbilities or because the section doesn't exist
8734 if (!has_capability('moodle/site:config', context_system::instance())) {
8735 // The requested section could depend on a different capability
8736 // but most likely the user has inadequate capabilities
8737 print_error('accessdenied', 'admin');
8738 } else {
8739 print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
8743 // this eliminates our need to authenticate on the actual pages
8744 if (!$extpage->check_access()) {
8745 print_error('accessdenied', 'admin');
8746 die;
8749 navigation_node::require_admin_tree();
8751 // $PAGE->set_extra_button($extrabutton); TODO
8753 if (!$actualurl) {
8754 $actualurl = $extpage->url;
8757 $PAGE->set_url($actualurl, $extraurlparams);
8758 if (strpos($PAGE->pagetype, 'admin-') !== 0) {
8759 $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
8762 if (empty($SITE->fullname) || empty($SITE->shortname)) {
8763 // During initial install.
8764 $strinstallation = get_string('installation', 'install');
8765 $strsettings = get_string('settings');
8766 $PAGE->navbar->add($strsettings);
8767 $PAGE->set_title($strinstallation);
8768 $PAGE->set_heading($strinstallation);
8769 $PAGE->set_cacheable(false);
8770 return;
8773 // Locate the current item on the navigation and make it active when found.
8774 $path = $extpage->path;
8775 $node = $PAGE->settingsnav;
8776 while ($node && count($path) > 0) {
8777 $node = $node->get(array_pop($path));
8779 if ($node) {
8780 $node->make_active();
8783 // Normal case.
8784 $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
8785 if ($PAGE->user_allowed_editing() && $adminediting != -1) {
8786 $USER->editing = $adminediting;
8789 $visiblepathtosection = array_reverse($extpage->visiblepath);
8791 if ($PAGE->user_allowed_editing()) {
8792 if ($PAGE->user_is_editing()) {
8793 $caption = get_string('blockseditoff');
8794 $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
8795 } else {
8796 $caption = get_string('blocksediton');
8797 $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
8799 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
8802 $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
8803 $PAGE->set_heading($SITE->fullname);
8805 // prevent caching in nav block
8806 $PAGE->navigation->clear_cache();
8810 * Returns the reference to admin tree root
8812 * @return object admin_root object
8814 function admin_get_root($reload=false, $requirefulltree=true) {
8815 global $CFG, $DB, $OUTPUT, $ADMIN;
8817 if (is_null($ADMIN)) {
8818 // create the admin tree!
8819 $ADMIN = new admin_root($requirefulltree);
8822 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
8823 $ADMIN->purge_children($requirefulltree);
8826 if (!$ADMIN->loaded) {
8827 // we process this file first to create categories first and in correct order
8828 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
8830 // now we process all other files in admin/settings to build the admin tree
8831 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
8832 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
8833 continue;
8835 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
8836 // plugins are loaded last - they may insert pages anywhere
8837 continue;
8839 require($file);
8841 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
8843 $ADMIN->loaded = true;
8846 return $ADMIN;
8849 /// settings utility functions
8852 * This function applies default settings.
8853 * Because setting the defaults of some settings can enable other settings,
8854 * this function is called recursively until no more new settings are found.
8856 * @param object $node, NULL means complete tree, null by default
8857 * @param bool $unconditional if true overrides all values with defaults, true by default
8858 * @param array $admindefaultsettings default admin settings to apply. Used recursively
8859 * @param array $settingsoutput The names and values of the changed settings. Used recursively
8860 * @return array $settingsoutput The names and values of the changed settings
8862 function admin_apply_default_settings($node=null, $unconditional=true, $admindefaultsettings=array(), $settingsoutput=array()) {
8863 $counter = 0;
8865 if (is_null($node)) {
8866 core_plugin_manager::reset_caches();
8867 $node = admin_get_root(true, true);
8868 $counter = count($settingsoutput);
8871 if ($node instanceof admin_category) {
8872 $entries = array_keys($node->children);
8873 foreach ($entries as $entry) {
8874 $settingsoutput = admin_apply_default_settings(
8875 $node->children[$entry], $unconditional, $admindefaultsettings, $settingsoutput
8879 } else if ($node instanceof admin_settingpage) {
8880 foreach ($node->settings as $setting) {
8881 if (!$unconditional && !is_null($setting->get_setting())) {
8882 // Do not override existing defaults.
8883 continue;
8885 $defaultsetting = $setting->get_defaultsetting();
8886 if (is_null($defaultsetting)) {
8887 // No value yet - default maybe applied after admin user creation or in upgradesettings.
8888 continue;
8891 $settingname = $node->name . '_' . $setting->name; // Get a unique name for the setting.
8893 if (!array_key_exists($settingname, $admindefaultsettings)) { // Only update a setting if not already processed.
8894 $admindefaultsettings[$settingname] = $settingname;
8895 $settingsoutput[$settingname] = $defaultsetting;
8897 // Set the default for this setting.
8898 $setting->write_setting($defaultsetting);
8899 $setting->write_setting_flags(null);
8900 } else {
8901 unset($admindefaultsettings[$settingname]); // Remove processed settings.
8906 // Call this function recursively until all settings are processed.
8907 if (($node instanceof admin_root) && ($counter != count($settingsoutput))) {
8908 $settingsoutput = admin_apply_default_settings(null, $unconditional, $admindefaultsettings, $settingsoutput);
8910 // Just in case somebody modifies the list of active plugins directly.
8911 core_plugin_manager::reset_caches();
8913 return $settingsoutput;
8917 * Store changed settings, this function updates the errors variable in $ADMIN
8919 * @param object $formdata from form
8920 * @return int number of changed settings
8922 function admin_write_settings($formdata) {
8923 global $CFG, $SITE, $DB;
8925 $olddbsessions = !empty($CFG->dbsessions);
8926 $formdata = (array)$formdata;
8928 $data = array();
8929 foreach ($formdata as $fullname=>$value) {
8930 if (strpos($fullname, 's_') !== 0) {
8931 continue; // not a config value
8933 $data[$fullname] = $value;
8936 $adminroot = admin_get_root();
8937 $settings = admin_find_write_settings($adminroot, $data);
8939 $count = 0;
8940 foreach ($settings as $fullname=>$setting) {
8941 /** @var $setting admin_setting */
8942 $original = $setting->get_setting();
8943 $error = $setting->write_setting($data[$fullname]);
8944 if ($error !== '') {
8945 $adminroot->errors[$fullname] = new stdClass();
8946 $adminroot->errors[$fullname]->data = $data[$fullname];
8947 $adminroot->errors[$fullname]->id = $setting->get_id();
8948 $adminroot->errors[$fullname]->error = $error;
8949 } else {
8950 $setting->write_setting_flags($data);
8952 if ($setting->post_write_settings($original)) {
8953 $count++;
8957 if ($olddbsessions != !empty($CFG->dbsessions)) {
8958 require_logout();
8961 // Now update $SITE - just update the fields, in case other people have a
8962 // a reference to it (e.g. $PAGE, $COURSE).
8963 $newsite = $DB->get_record('course', array('id'=>$SITE->id));
8964 foreach (get_object_vars($newsite) as $field => $value) {
8965 $SITE->$field = $value;
8968 // now reload all settings - some of them might depend on the changed
8969 admin_get_root(true);
8970 return $count;
8974 * Internal recursive function - finds all settings from submitted form
8976 * @param object $node Instance of admin_category, or admin_settingpage
8977 * @param array $data
8978 * @return array
8980 function admin_find_write_settings($node, $data) {
8981 $return = array();
8983 if (empty($data)) {
8984 return $return;
8987 if ($node instanceof admin_category) {
8988 if ($node->check_access()) {
8989 $entries = array_keys($node->children);
8990 foreach ($entries as $entry) {
8991 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
8995 } else if ($node instanceof admin_settingpage) {
8996 if ($node->check_access()) {
8997 foreach ($node->settings as $setting) {
8998 $fullname = $setting->get_full_name();
8999 if (array_key_exists($fullname, $data)) {
9000 $return[$fullname] = $setting;
9007 return $return;
9011 * Internal function - prints the search results
9013 * @param string $query String to search for
9014 * @return string empty or XHTML
9016 function admin_search_settings_html($query) {
9017 global $CFG, $OUTPUT, $PAGE;
9019 if (core_text::strlen($query) < 2) {
9020 return '';
9022 $query = core_text::strtolower($query);
9024 $adminroot = admin_get_root();
9025 $findings = $adminroot->search($query);
9026 $savebutton = false;
9028 $tpldata = (object) [
9029 'actionurl' => $PAGE->url->out(false),
9030 'results' => [],
9031 'sesskey' => sesskey(),
9034 foreach ($findings as $found) {
9035 $page = $found->page;
9036 $settings = $found->settings;
9037 if ($page->is_hidden()) {
9038 // hidden pages are not displayed in search results
9039 continue;
9042 $heading = highlight($query, $page->visiblename);
9043 $headingurl = null;
9044 if ($page instanceof admin_externalpage) {
9045 $headingurl = new moodle_url($page->url);
9046 } else if ($page instanceof admin_settingpage) {
9047 $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
9048 } else {
9049 continue;
9052 // Locate the page in the admin root and populate its visiblepath attribute.
9053 $path = array();
9054 $located = $adminroot->locate($page->name, true);
9055 if ($located) {
9056 foreach ($located->visiblepath as $pathitem) {
9057 array_unshift($path, (string) $pathitem);
9061 $sectionsettings = [];
9062 if (!empty($settings)) {
9063 foreach ($settings as $setting) {
9064 if (empty($setting->nosave)) {
9065 $savebutton = true;
9067 $fullname = $setting->get_full_name();
9068 if (array_key_exists($fullname, $adminroot->errors)) {
9069 $data = $adminroot->errors[$fullname]->data;
9070 } else {
9071 $data = $setting->get_setting();
9072 // do not use defaults if settings not available - upgradesettings handles the defaults!
9074 $sectionsettings[] = $setting->output_html($data, $query);
9078 $tpldata->results[] = (object) [
9079 'title' => $heading,
9080 'path' => $path,
9081 'url' => $headingurl->out(false),
9082 'settings' => $sectionsettings
9086 $tpldata->showsave = $savebutton;
9087 $tpldata->hasresults = !empty($tpldata->results);
9089 return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
9093 * Internal function - returns arrays of html pages with uninitialised settings
9095 * @param object $node Instance of admin_category or admin_settingpage
9096 * @return array
9098 function admin_output_new_settings_by_page($node) {
9099 global $OUTPUT;
9100 $return = array();
9102 if ($node instanceof admin_category) {
9103 $entries = array_keys($node->children);
9104 foreach ($entries as $entry) {
9105 $return += admin_output_new_settings_by_page($node->children[$entry]);
9108 } else if ($node instanceof admin_settingpage) {
9109 $newsettings = array();
9110 foreach ($node->settings as $setting) {
9111 if (is_null($setting->get_setting())) {
9112 $newsettings[] = $setting;
9115 if (count($newsettings) > 0) {
9116 $adminroot = admin_get_root();
9117 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
9118 $page .= '<fieldset class="adminsettings">'."\n";
9119 foreach ($newsettings as $setting) {
9120 $fullname = $setting->get_full_name();
9121 if (array_key_exists($fullname, $adminroot->errors)) {
9122 $data = $adminroot->errors[$fullname]->data;
9123 } else {
9124 $data = $setting->get_setting();
9125 if (is_null($data)) {
9126 $data = $setting->get_defaultsetting();
9129 $page .= '<div class="clearer"><!-- --></div>'."\n";
9130 $page .= $setting->output_html($data);
9132 $page .= '</fieldset>';
9133 $return[$node->name] = $page;
9137 return $return;
9141 * Format admin settings
9143 * @param object $setting
9144 * @param string $title label element
9145 * @param string $form form fragment, html code - not highlighted automatically
9146 * @param string $description
9147 * @param mixed $label link label to id, true by default or string being the label to connect it to
9148 * @param string $warning warning text
9149 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
9150 * @param string $query search query to be highlighted
9151 * @return string XHTML
9153 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
9154 global $CFG, $OUTPUT;
9156 $context = (object) [
9157 'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
9158 'fullname' => $setting->get_full_name(),
9161 // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
9162 if ($label === true) {
9163 $context->labelfor = $setting->get_id();
9164 } else if ($label === false) {
9165 $context->labelfor = '';
9166 } else {
9167 $context->labelfor = $label;
9170 $form .= $setting->output_setting_flags();
9172 $context->warning = $warning;
9173 $context->override = '';
9174 if (empty($setting->plugin)) {
9175 if (array_key_exists($setting->name, $CFG->config_php_settings)) {
9176 $context->override = get_string('configoverride', 'admin');
9178 } else {
9179 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
9180 $context->override = get_string('configoverride', 'admin');
9184 $defaults = array();
9185 if (!is_null($defaultinfo)) {
9186 if ($defaultinfo === '') {
9187 $defaultinfo = get_string('emptysettingvalue', 'admin');
9189 $defaults[] = $defaultinfo;
9192 $context->default = null;
9193 $setting->get_setting_flag_defaults($defaults);
9194 if (!empty($defaults)) {
9195 $defaultinfo = implode(', ', $defaults);
9196 $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
9197 $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
9201 $context->error = '';
9202 $adminroot = admin_get_root();
9203 if (array_key_exists($context->fullname, $adminroot->errors)) {
9204 $context->error = $adminroot->errors[$context->fullname]->error;
9207 if ($dependenton = $setting->get_dependent_on()) {
9208 $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
9211 $context->id = 'admin-' . $setting->name;
9212 $context->title = highlightfast($query, $title);
9213 $context->name = highlightfast($query, $context->name);
9214 $context->description = highlight($query, markdown_to_html($description));
9215 $context->element = $form;
9216 $context->forceltr = $setting->get_force_ltr();
9217 $context->customcontrol = $setting->has_custom_form_control();
9219 return $OUTPUT->render_from_template('core_admin/setting', $context);
9223 * Based on find_new_settings{@link ()} in upgradesettings.php
9224 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
9226 * @param object $node Instance of admin_category, or admin_settingpage
9227 * @return boolean true if any settings haven't been initialised, false if they all have
9229 function any_new_admin_settings($node) {
9231 if ($node instanceof admin_category) {
9232 $entries = array_keys($node->children);
9233 foreach ($entries as $entry) {
9234 if (any_new_admin_settings($node->children[$entry])) {
9235 return true;
9239 } else if ($node instanceof admin_settingpage) {
9240 foreach ($node->settings as $setting) {
9241 if ($setting->get_setting() === NULL) {
9242 return true;
9247 return false;
9251 * Given a table and optionally a column name should replaces be done?
9253 * @param string $table name
9254 * @param string $column name
9255 * @return bool success or fail
9257 function db_should_replace($table, $column = '', $additionalskiptables = ''): bool {
9259 // TODO: this is horrible hack, we should have a hook and each plugin should be responsible for proper replacing...
9260 $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions',
9261 'events_queue', 'repository_instance_config', 'block_instances', 'files'];
9263 // Additional skip tables.
9264 if (!empty($additionalskiptables)) {
9265 $skiptables = array_merge($skiptables, explode(',', str_replace(' ', '', $additionalskiptables)));
9268 // Don't process these.
9269 if (in_array($table, $skiptables)) {
9270 return false;
9273 // To be safe never replace inside a table that looks related to logging.
9274 if (preg_match('/(^|_)logs?($|_)/', $table)) {
9275 return false;
9278 // Do column based exclusions.
9279 if (!empty($column)) {
9280 // Don't touch anything that looks like a hash.
9281 if (preg_match('/hash$/', $column)) {
9282 return false;
9286 return true;
9290 * Moved from admin/replace.php so that we can use this in cron
9292 * @param string $search string to look for
9293 * @param string $replace string to replace
9294 * @return bool success or fail
9296 function db_replace($search, $replace, $additionalskiptables = '') {
9297 global $DB, $CFG, $OUTPUT;
9299 // Turn off time limits, sometimes upgrades can be slow.
9300 core_php_time_limit::raise();
9302 if (!$tables = $DB->get_tables() ) { // No tables yet at all.
9303 return false;
9305 foreach ($tables as $table) {
9307 if (!db_should_replace($table, '', $additionalskiptables)) {
9308 continue;
9311 if ($columns = $DB->get_columns($table)) {
9312 $DB->set_debug(true);
9313 foreach ($columns as $column) {
9314 if (!db_should_replace($table, $column->name)) {
9315 continue;
9317 $DB->replace_all_text($table, $column, $search, $replace);
9319 $DB->set_debug(false);
9323 // delete modinfo caches
9324 rebuild_course_cache(0, true);
9326 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
9327 $blocks = core_component::get_plugin_list('block');
9328 foreach ($blocks as $blockname=>$fullblock) {
9329 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
9330 continue;
9333 if (!is_readable($fullblock.'/lib.php')) {
9334 continue;
9337 $function = 'block_'.$blockname.'_global_db_replace';
9338 include_once($fullblock.'/lib.php');
9339 if (!function_exists($function)) {
9340 continue;
9343 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
9344 $function($search, $replace);
9345 echo $OUTPUT->notification("...finished", 'notifysuccess');
9348 // Trigger an event.
9349 $eventargs = [
9350 'context' => context_system::instance(),
9351 'other' => [
9352 'search' => $search,
9353 'replace' => $replace
9356 $event = \core\event\database_text_field_content_replaced::create($eventargs);
9357 $event->trigger();
9359 purge_all_caches();
9361 return true;
9365 * Manage repository settings
9367 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9369 class admin_setting_managerepository extends admin_setting {
9370 /** @var string */
9371 private $baseurl;
9374 * calls parent::__construct with specific arguments
9376 public function __construct() {
9377 global $CFG;
9378 parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
9379 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
9383 * Always returns true, does nothing
9385 * @return true
9387 public function get_setting() {
9388 return true;
9392 * Always returns true does nothing
9394 * @return true
9396 public function get_defaultsetting() {
9397 return true;
9401 * Always returns s_managerepository
9403 * @return string Always return 's_managerepository'
9405 public function get_full_name() {
9406 return 's_managerepository';
9410 * Always returns '' doesn't do anything
9412 public function write_setting($data) {
9413 $url = $this->baseurl . '&amp;new=' . $data;
9414 return '';
9415 // TODO
9416 // Should not use redirect and exit here
9417 // Find a better way to do this.
9418 // redirect($url);
9419 // exit;
9423 * Searches repository plugins for one that matches $query
9425 * @param string $query The string to search for
9426 * @return bool true if found, false if not
9428 public function is_related($query) {
9429 if (parent::is_related($query)) {
9430 return true;
9433 $repositories= core_component::get_plugin_list('repository');
9434 foreach ($repositories as $p => $dir) {
9435 if (strpos($p, $query) !== false) {
9436 return true;
9439 foreach (repository::get_types() as $instance) {
9440 $title = $instance->get_typename();
9441 if (strpos(core_text::strtolower($title), $query) !== false) {
9442 return true;
9445 return false;
9449 * Helper function that generates a moodle_url object
9450 * relevant to the repository
9453 function repository_action_url($repository) {
9454 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
9458 * Builds XHTML to display the control
9460 * @param string $data Unused
9461 * @param string $query
9462 * @return string XHTML
9464 public function output_html($data, $query='') {
9465 global $CFG, $USER, $OUTPUT;
9467 // Get strings that are used
9468 $strshow = get_string('on', 'repository');
9469 $strhide = get_string('off', 'repository');
9470 $strdelete = get_string('disabled', 'repository');
9472 $actionchoicesforexisting = array(
9473 'show' => $strshow,
9474 'hide' => $strhide,
9475 'delete' => $strdelete
9478 $actionchoicesfornew = array(
9479 'newon' => $strshow,
9480 'newoff' => $strhide,
9481 'delete' => $strdelete
9484 $return = '';
9485 $return .= $OUTPUT->box_start('generalbox');
9487 // Set strings that are used multiple times
9488 $settingsstr = get_string('settings');
9489 $disablestr = get_string('disable');
9491 // Table to list plug-ins
9492 $table = new html_table();
9493 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
9494 $table->align = array('left', 'center', 'center', 'center', 'center');
9495 $table->data = array();
9497 // Get list of used plug-ins
9498 $repositorytypes = repository::get_types();
9499 if (!empty($repositorytypes)) {
9500 // Array to store plugins being used
9501 $alreadyplugins = array();
9502 $totalrepositorytypes = count($repositorytypes);
9503 $updowncount = 1;
9504 foreach ($repositorytypes as $i) {
9505 $settings = '';
9506 $typename = $i->get_typename();
9507 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
9508 $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
9509 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
9511 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
9512 // Calculate number of instances in order to display them for the Moodle administrator
9513 if (!empty($instanceoptionnames)) {
9514 $params = array();
9515 $params['context'] = array(context_system::instance());
9516 $params['onlyvisible'] = false;
9517 $params['type'] = $typename;
9518 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
9519 // site instances
9520 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
9521 $params['context'] = array();
9522 $instances = repository::static_function($typename, 'get_instances', $params);
9523 $courseinstances = array();
9524 $userinstances = array();
9526 foreach ($instances as $instance) {
9527 $repocontext = context::instance_by_id($instance->instance->contextid);
9528 if ($repocontext->contextlevel == CONTEXT_COURSE) {
9529 $courseinstances[] = $instance;
9530 } else if ($repocontext->contextlevel == CONTEXT_USER) {
9531 $userinstances[] = $instance;
9534 // course instances
9535 $instancenumber = count($courseinstances);
9536 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
9538 // user private instances
9539 $instancenumber = count($userinstances);
9540 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
9541 } else {
9542 $admininstancenumbertext = "";
9543 $courseinstancenumbertext = "";
9544 $userinstancenumbertext = "";
9547 $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
9549 $settings .= $OUTPUT->container_start('mdl-left');
9550 $settings .= '<br/>';
9551 $settings .= $admininstancenumbertext;
9552 $settings .= '<br/>';
9553 $settings .= $courseinstancenumbertext;
9554 $settings .= '<br/>';
9555 $settings .= $userinstancenumbertext;
9556 $settings .= $OUTPUT->container_end();
9558 // Get the current visibility
9559 if ($i->get_visible()) {
9560 $currentaction = 'show';
9561 } else {
9562 $currentaction = 'hide';
9565 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
9567 // Display up/down link
9568 $updown = '';
9569 // Should be done with CSS instead.
9570 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
9572 if ($updowncount > 1) {
9573 $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
9574 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
9576 else {
9577 $updown .= $spacer;
9579 if ($updowncount < $totalrepositorytypes) {
9580 $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
9581 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
9583 else {
9584 $updown .= $spacer;
9587 $updowncount++;
9589 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
9591 if (!in_array($typename, $alreadyplugins)) {
9592 $alreadyplugins[] = $typename;
9597 // Get all the plugins that exist on disk
9598 $plugins = core_component::get_plugin_list('repository');
9599 if (!empty($plugins)) {
9600 foreach ($plugins as $plugin => $dir) {
9601 // Check that it has not already been listed
9602 if (!in_array($plugin, $alreadyplugins)) {
9603 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
9604 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
9609 $return .= html_writer::table($table);
9610 $return .= $OUTPUT->box_end();
9611 return highlight($query, $return);
9616 * Special checkbox for enable mobile web service
9617 * If enable then we store the service id of the mobile service into config table
9618 * If disable then we unstore the service id from the config table
9620 class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
9622 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
9623 private $restuse;
9626 * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
9628 * @return boolean
9630 private function is_protocol_cap_allowed() {
9631 global $DB, $CFG;
9633 // If the $this->restuse variable is not set, it needs to be set.
9634 if (empty($this->restuse) and $this->restuse!==false) {
9635 $params = array();
9636 $params['permission'] = CAP_ALLOW;
9637 $params['roleid'] = $CFG->defaultuserroleid;
9638 $params['capability'] = 'webservice/rest:use';
9639 $this->restuse = $DB->record_exists('role_capabilities', $params);
9642 return $this->restuse;
9646 * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
9647 * @param type $status true to allow, false to not set
9649 private function set_protocol_cap($status) {
9650 global $CFG;
9651 if ($status and !$this->is_protocol_cap_allowed()) {
9652 //need to allow the cap
9653 $permission = CAP_ALLOW;
9654 $assign = true;
9655 } else if (!$status and $this->is_protocol_cap_allowed()){
9656 //need to disallow the cap
9657 $permission = CAP_INHERIT;
9658 $assign = true;
9660 if (!empty($assign)) {
9661 $systemcontext = context_system::instance();
9662 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
9667 * Builds XHTML to display the control.
9668 * The main purpose of this overloading is to display a warning when https
9669 * is not supported by the server
9670 * @param string $data Unused
9671 * @param string $query
9672 * @return string XHTML
9674 public function output_html($data, $query='') {
9675 global $OUTPUT;
9676 $html = parent::output_html($data, $query);
9678 if ((string)$data === $this->yes) {
9679 $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
9680 foreach ($notifications as $notification) {
9681 $message = get_string($notification[0], $notification[1]);
9682 $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
9686 return $html;
9690 * Retrieves the current setting using the objects name
9692 * @return string
9694 public function get_setting() {
9695 global $CFG;
9697 // First check if is not set.
9698 $result = $this->config_read($this->name);
9699 if (is_null($result)) {
9700 return null;
9703 // For install cli script, $CFG->defaultuserroleid is not set so return 0
9704 // Or if web services aren't enabled this can't be,
9705 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
9706 return 0;
9709 require_once($CFG->dirroot . '/webservice/lib.php');
9710 $webservicemanager = new webservice();
9711 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9712 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
9713 return $result;
9714 } else {
9715 return 0;
9720 * Save the selected setting
9722 * @param string $data The selected site
9723 * @return string empty string or error message
9725 public function write_setting($data) {
9726 global $DB, $CFG;
9728 //for install cli script, $CFG->defaultuserroleid is not set so do nothing
9729 if (empty($CFG->defaultuserroleid)) {
9730 return '';
9733 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
9735 require_once($CFG->dirroot . '/webservice/lib.php');
9736 $webservicemanager = new webservice();
9738 $updateprotocol = false;
9739 if ((string)$data === $this->yes) {
9740 //code run when enable mobile web service
9741 //enable web service systeme if necessary
9742 set_config('enablewebservices', true);
9744 //enable mobile service
9745 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9746 $mobileservice->enabled = 1;
9747 $webservicemanager->update_external_service($mobileservice);
9749 // Enable REST server.
9750 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9752 if (!in_array('rest', $activeprotocols)) {
9753 $activeprotocols[] = 'rest';
9754 $updateprotocol = true;
9757 if ($updateprotocol) {
9758 set_config('webserviceprotocols', implode(',', $activeprotocols));
9761 // Allow rest:use capability for authenticated user.
9762 $this->set_protocol_cap(true);
9764 } else {
9765 //disable web service system if no other services are enabled
9766 $otherenabledservices = $DB->get_records_select('external_services',
9767 'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
9768 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
9769 if (empty($otherenabledservices)) {
9770 set_config('enablewebservices', false);
9772 // Also disable REST server.
9773 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9775 $protocolkey = array_search('rest', $activeprotocols);
9776 if ($protocolkey !== false) {
9777 unset($activeprotocols[$protocolkey]);
9778 $updateprotocol = true;
9781 if ($updateprotocol) {
9782 set_config('webserviceprotocols', implode(',', $activeprotocols));
9785 // Disallow rest:use capability for authenticated user.
9786 $this->set_protocol_cap(false);
9789 //disable the mobile service
9790 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9791 $mobileservice->enabled = 0;
9792 $webservicemanager->update_external_service($mobileservice);
9795 return (parent::write_setting($data));
9800 * Special class for management of external services
9802 * @author Petr Skoda (skodak)
9804 class admin_setting_manageexternalservices extends admin_setting {
9806 * Calls parent::__construct with specific arguments
9808 public function __construct() {
9809 $this->nosave = true;
9810 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
9814 * Always returns true, does nothing
9816 * @return true
9818 public function get_setting() {
9819 return true;
9823 * Always returns true, does nothing
9825 * @return true
9827 public function get_defaultsetting() {
9828 return true;
9832 * Always returns '', does not write anything
9834 * @return string Always returns ''
9836 public function write_setting($data) {
9837 // do not write any setting
9838 return '';
9842 * Checks if $query is one of the available external services
9844 * @param string $query The string to search for
9845 * @return bool Returns true if found, false if not
9847 public function is_related($query) {
9848 global $DB;
9850 if (parent::is_related($query)) {
9851 return true;
9854 $services = $DB->get_records('external_services', array(), 'id, name');
9855 foreach ($services as $service) {
9856 if (strpos(core_text::strtolower($service->name), $query) !== false) {
9857 return true;
9860 return false;
9864 * Builds the XHTML to display the control
9866 * @param string $data Unused
9867 * @param string $query
9868 * @return string
9870 public function output_html($data, $query='') {
9871 global $CFG, $OUTPUT, $DB;
9873 // display strings
9874 $stradministration = get_string('administration');
9875 $stredit = get_string('edit');
9876 $strservice = get_string('externalservice', 'webservice');
9877 $strdelete = get_string('delete');
9878 $strplugin = get_string('plugin', 'admin');
9879 $stradd = get_string('add');
9880 $strfunctions = get_string('functions', 'webservice');
9881 $strusers = get_string('users');
9882 $strserviceusers = get_string('serviceusers', 'webservice');
9884 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
9885 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
9886 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
9888 // built in services
9889 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
9890 $return = "";
9891 if (!empty($services)) {
9892 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
9896 $table = new html_table();
9897 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
9898 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9899 $table->id = 'builtinservices';
9900 $table->attributes['class'] = 'admintable externalservices generaltable';
9901 $table->data = array();
9903 // iterate through auth plugins and add to the display table
9904 foreach ($services as $service) {
9905 $name = $service->name;
9907 // hide/show link
9908 if ($service->enabled) {
9909 $displayname = "<span>$name</span>";
9910 } else {
9911 $displayname = "<span class=\"dimmed_text\">$name</span>";
9914 $plugin = $service->component;
9916 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9918 if ($service->restrictedusers) {
9919 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9920 } else {
9921 $users = get_string('allusers', 'webservice');
9924 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9926 // add a row to the table
9927 $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
9929 $return .= html_writer::table($table);
9932 // Custom services
9933 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
9934 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
9936 $table = new html_table();
9937 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
9938 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9939 $table->id = 'customservices';
9940 $table->attributes['class'] = 'admintable externalservices generaltable';
9941 $table->data = array();
9943 // iterate through auth plugins and add to the display table
9944 foreach ($services as $service) {
9945 $name = $service->name;
9947 // hide/show link
9948 if ($service->enabled) {
9949 $displayname = "<span>$name</span>";
9950 } else {
9951 $displayname = "<span class=\"dimmed_text\">$name</span>";
9954 // delete link
9955 $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
9957 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9959 if ($service->restrictedusers) {
9960 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9961 } else {
9962 $users = get_string('allusers', 'webservice');
9965 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9967 // add a row to the table
9968 $table->data[] = array($displayname, $delete, $functions, $users, $edit);
9970 // add new custom service option
9971 $return .= html_writer::table($table);
9973 $return .= '<br />';
9974 // add a token to the table
9975 $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
9977 return highlight($query, $return);
9982 * Special class for overview of external services
9984 * @author Jerome Mouneyrac
9986 class admin_setting_webservicesoverview extends admin_setting {
9989 * Calls parent::__construct with specific arguments
9991 public function __construct() {
9992 $this->nosave = true;
9993 parent::__construct('webservicesoverviewui',
9994 get_string('webservicesoverview', 'webservice'), '', '');
9998 * Always returns true, does nothing
10000 * @return true
10002 public function get_setting() {
10003 return true;
10007 * Always returns true, does nothing
10009 * @return true
10011 public function get_defaultsetting() {
10012 return true;
10016 * Always returns '', does not write anything
10018 * @return string Always returns ''
10020 public function write_setting($data) {
10021 // do not write any setting
10022 return '';
10026 * Builds the XHTML to display the control
10028 * @param string $data Unused
10029 * @param string $query
10030 * @return string
10032 public function output_html($data, $query='') {
10033 global $CFG, $OUTPUT;
10035 $return = "";
10036 $brtag = html_writer::empty_tag('br');
10038 /// One system controlling Moodle with Token
10039 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
10040 $table = new html_table();
10041 $table->head = array(get_string('step', 'webservice'), get_string('status'),
10042 get_string('description'));
10043 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
10044 $table->id = 'onesystemcontrol';
10045 $table->attributes['class'] = 'admintable wsoverview generaltable';
10046 $table->data = array();
10048 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
10049 . $brtag . $brtag;
10051 /// 1. Enable Web Services
10052 $row = array();
10053 $url = new moodle_url("/admin/search.php?query=enablewebservices");
10054 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10055 array('href' => $url));
10056 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
10057 if ($CFG->enablewebservices) {
10058 $status = get_string('yes');
10060 $row[1] = $status;
10061 $row[2] = get_string('enablewsdescription', 'webservice');
10062 $table->data[] = $row;
10064 /// 2. Enable protocols
10065 $row = array();
10066 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10067 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10068 array('href' => $url));
10069 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
10070 //retrieve activated protocol
10071 $active_protocols = empty($CFG->webserviceprotocols) ?
10072 array() : explode(',', $CFG->webserviceprotocols);
10073 if (!empty($active_protocols)) {
10074 $status = "";
10075 foreach ($active_protocols as $protocol) {
10076 $status .= $protocol . $brtag;
10079 $row[1] = $status;
10080 $row[2] = get_string('enableprotocolsdescription', 'webservice');
10081 $table->data[] = $row;
10083 /// 3. Create user account
10084 $row = array();
10085 $url = new moodle_url("/user/editadvanced.php?id=-1");
10086 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
10087 array('href' => $url));
10088 $row[1] = "";
10089 $row[2] = get_string('createuserdescription', 'webservice');
10090 $table->data[] = $row;
10092 /// 4. Add capability to users
10093 $row = array();
10094 $url = new moodle_url("/admin/roles/check.php?contextid=1");
10095 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
10096 array('href' => $url));
10097 $row[1] = "";
10098 $row[2] = get_string('checkusercapabilitydescription', 'webservice');
10099 $table->data[] = $row;
10101 /// 5. Select a web service
10102 $row = array();
10103 $url = new moodle_url("/admin/settings.php?section=externalservices");
10104 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10105 array('href' => $url));
10106 $row[1] = "";
10107 $row[2] = get_string('createservicedescription', 'webservice');
10108 $table->data[] = $row;
10110 /// 6. Add functions
10111 $row = array();
10112 $url = new moodle_url("/admin/settings.php?section=externalservices");
10113 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10114 array('href' => $url));
10115 $row[1] = "";
10116 $row[2] = get_string('addfunctionsdescription', 'webservice');
10117 $table->data[] = $row;
10119 /// 7. Add the specific user
10120 $row = array();
10121 $url = new moodle_url("/admin/settings.php?section=externalservices");
10122 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
10123 array('href' => $url));
10124 $row[1] = "";
10125 $row[2] = get_string('selectspecificuserdescription', 'webservice');
10126 $table->data[] = $row;
10128 /// 8. Create token for the specific user
10129 $row = array();
10130 $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
10131 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
10132 array('href' => $url));
10133 $row[1] = "";
10134 $row[2] = get_string('createtokenforuserdescription', 'webservice');
10135 $table->data[] = $row;
10137 /// 9. Enable the documentation
10138 $row = array();
10139 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
10140 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
10141 array('href' => $url));
10142 $status = '<span class="warning">' . get_string('no') . '</span>';
10143 if ($CFG->enablewsdocumentation) {
10144 $status = get_string('yes');
10146 $row[1] = $status;
10147 $row[2] = get_string('enabledocumentationdescription', 'webservice');
10148 $table->data[] = $row;
10150 /// 10. Test the service
10151 $row = array();
10152 $url = new moodle_url("/admin/webservice/testclient.php");
10153 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10154 array('href' => $url));
10155 $row[1] = "";
10156 $row[2] = get_string('testwithtestclientdescription', 'webservice');
10157 $table->data[] = $row;
10159 $return .= html_writer::table($table);
10161 /// Users as clients with token
10162 $return .= $brtag . $brtag . $brtag;
10163 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
10164 $table = new html_table();
10165 $table->head = array(get_string('step', 'webservice'), get_string('status'),
10166 get_string('description'));
10167 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
10168 $table->id = 'userasclients';
10169 $table->attributes['class'] = 'admintable wsoverview generaltable';
10170 $table->data = array();
10172 $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
10173 $brtag . $brtag;
10175 /// 1. Enable Web Services
10176 $row = array();
10177 $url = new moodle_url("/admin/search.php?query=enablewebservices");
10178 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10179 array('href' => $url));
10180 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
10181 if ($CFG->enablewebservices) {
10182 $status = get_string('yes');
10184 $row[1] = $status;
10185 $row[2] = get_string('enablewsdescription', 'webservice');
10186 $table->data[] = $row;
10188 /// 2. Enable protocols
10189 $row = array();
10190 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10191 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10192 array('href' => $url));
10193 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
10194 //retrieve activated protocol
10195 $active_protocols = empty($CFG->webserviceprotocols) ?
10196 array() : explode(',', $CFG->webserviceprotocols);
10197 if (!empty($active_protocols)) {
10198 $status = "";
10199 foreach ($active_protocols as $protocol) {
10200 $status .= $protocol . $brtag;
10203 $row[1] = $status;
10204 $row[2] = get_string('enableprotocolsdescription', 'webservice');
10205 $table->data[] = $row;
10208 /// 3. Select a web service
10209 $row = array();
10210 $url = new moodle_url("/admin/settings.php?section=externalservices");
10211 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10212 array('href' => $url));
10213 $row[1] = "";
10214 $row[2] = get_string('createserviceforusersdescription', 'webservice');
10215 $table->data[] = $row;
10217 /// 4. Add functions
10218 $row = array();
10219 $url = new moodle_url("/admin/settings.php?section=externalservices");
10220 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10221 array('href' => $url));
10222 $row[1] = "";
10223 $row[2] = get_string('addfunctionsdescription', 'webservice');
10224 $table->data[] = $row;
10226 /// 5. Add capability to users
10227 $row = array();
10228 $url = new moodle_url("/admin/roles/check.php?contextid=1");
10229 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
10230 array('href' => $url));
10231 $row[1] = "";
10232 $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
10233 $table->data[] = $row;
10235 /// 6. Test the service
10236 $row = array();
10237 $url = new moodle_url("/admin/webservice/testclient.php");
10238 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10239 array('href' => $url));
10240 $row[1] = "";
10241 $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
10242 $table->data[] = $row;
10244 $return .= html_writer::table($table);
10246 return highlight($query, $return);
10253 * Special class for web service protocol administration.
10255 * @author Petr Skoda (skodak)
10257 class admin_setting_managewebserviceprotocols extends admin_setting {
10260 * Calls parent::__construct with specific arguments
10262 public function __construct() {
10263 $this->nosave = true;
10264 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
10268 * Always returns true, does nothing
10270 * @return true
10272 public function get_setting() {
10273 return true;
10277 * Always returns true, does nothing
10279 * @return true
10281 public function get_defaultsetting() {
10282 return true;
10286 * Always returns '', does not write anything
10288 * @return string Always returns ''
10290 public function write_setting($data) {
10291 // do not write any setting
10292 return '';
10296 * Checks if $query is one of the available webservices
10298 * @param string $query The string to search for
10299 * @return bool Returns true if found, false if not
10301 public function is_related($query) {
10302 if (parent::is_related($query)) {
10303 return true;
10306 $protocols = core_component::get_plugin_list('webservice');
10307 foreach ($protocols as $protocol=>$location) {
10308 if (strpos($protocol, $query) !== false) {
10309 return true;
10311 $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
10312 if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
10313 return true;
10316 return false;
10320 * Builds the XHTML to display the control
10322 * @param string $data Unused
10323 * @param string $query
10324 * @return string
10326 public function output_html($data, $query='') {
10327 global $CFG, $OUTPUT;
10329 // display strings
10330 $stradministration = get_string('administration');
10331 $strsettings = get_string('settings');
10332 $stredit = get_string('edit');
10333 $strprotocol = get_string('protocol', 'webservice');
10334 $strenable = get_string('enable');
10335 $strdisable = get_string('disable');
10336 $strversion = get_string('version');
10338 $protocols_available = core_component::get_plugin_list('webservice');
10339 $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
10340 ksort($protocols_available);
10342 foreach ($active_protocols as $key=>$protocol) {
10343 if (empty($protocols_available[$protocol])) {
10344 unset($active_protocols[$key]);
10348 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
10349 $return .= $OUTPUT->box_start('generalbox webservicesui');
10351 $table = new html_table();
10352 $table->head = array($strprotocol, $strversion, $strenable, $strsettings);
10353 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
10354 $table->id = 'webserviceprotocols';
10355 $table->attributes['class'] = 'admintable generaltable';
10356 $table->data = array();
10358 // iterate through auth plugins and add to the display table
10359 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
10360 foreach ($protocols_available as $protocol => $location) {
10361 $name = get_string('pluginname', 'webservice_'.$protocol);
10363 $plugin = new stdClass();
10364 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
10365 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
10367 $version = isset($plugin->version) ? $plugin->version : '';
10369 // hide/show link
10370 if (in_array($protocol, $active_protocols)) {
10371 $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
10372 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
10373 $displayname = "<span>$name</span>";
10374 } else {
10375 $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
10376 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
10377 $displayname = "<span class=\"dimmed_text\">$name</span>";
10380 // settings link
10381 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
10382 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
10383 } else {
10384 $settings = '';
10387 // add a row to the table
10388 $table->data[] = array($displayname, $version, $hideshow, $settings);
10390 $return .= html_writer::table($table);
10391 $return .= get_string('configwebserviceplugins', 'webservice');
10392 $return .= $OUTPUT->box_end();
10394 return highlight($query, $return);
10399 * Colour picker
10401 * @copyright 2010 Sam Hemelryk
10402 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10404 class admin_setting_configcolourpicker extends admin_setting {
10407 * Information for previewing the colour
10409 * @var array|null
10411 protected $previewconfig = null;
10414 * Use default when empty.
10416 protected $usedefaultwhenempty = true;
10420 * @param string $name
10421 * @param string $visiblename
10422 * @param string $description
10423 * @param string $defaultsetting
10424 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
10426 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null,
10427 $usedefaultwhenempty = true) {
10428 $this->previewconfig = $previewconfig;
10429 $this->usedefaultwhenempty = $usedefaultwhenempty;
10430 parent::__construct($name, $visiblename, $description, $defaultsetting);
10431 $this->set_force_ltr(true);
10435 * Return the setting
10437 * @return mixed returns config if successful else null
10439 public function get_setting() {
10440 return $this->config_read($this->name);
10444 * Saves the setting
10446 * @param string $data
10447 * @return bool
10449 public function write_setting($data) {
10450 $data = $this->validate($data);
10451 if ($data === false) {
10452 return get_string('validateerror', 'admin');
10454 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
10458 * Validates the colour that was entered by the user
10460 * @param string $data
10461 * @return string|false
10463 protected function validate($data) {
10465 * List of valid HTML colour names
10467 * @var array
10469 $colornames = array(
10470 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
10471 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
10472 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
10473 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
10474 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
10475 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
10476 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
10477 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
10478 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
10479 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
10480 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
10481 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
10482 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
10483 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
10484 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
10485 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
10486 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
10487 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
10488 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
10489 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
10490 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
10491 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
10492 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
10493 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
10494 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
10495 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
10496 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
10497 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
10498 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
10499 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
10500 'whitesmoke', 'yellow', 'yellowgreen'
10503 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
10504 if (strpos($data, '#')!==0) {
10505 $data = '#'.$data;
10507 return $data;
10508 } else if (in_array(strtolower($data), $colornames)) {
10509 return $data;
10510 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
10511 return $data;
10512 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
10513 return $data;
10514 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
10515 return $data;
10516 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
10517 return $data;
10518 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
10519 return $data;
10520 } else if (empty($data)) {
10521 if ($this->usedefaultwhenempty){
10522 return $this->defaultsetting;
10523 } else {
10524 return '';
10526 } else {
10527 return false;
10532 * Generates the HTML for the setting
10534 * @global moodle_page $PAGE
10535 * @global core_renderer $OUTPUT
10536 * @param string $data
10537 * @param string $query
10539 public function output_html($data, $query = '') {
10540 global $PAGE, $OUTPUT;
10542 $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
10543 $context = (object) [
10544 'id' => $this->get_id(),
10545 'name' => $this->get_full_name(),
10546 'value' => $data,
10547 'icon' => $icon->export_for_template($OUTPUT),
10548 'haspreviewconfig' => !empty($this->previewconfig),
10549 'forceltr' => $this->get_force_ltr(),
10550 'readonly' => $this->is_readonly(),
10553 $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
10554 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
10556 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
10557 $this->get_defaultsetting(), $query);
10564 * Class used for uploading of one file into file storage,
10565 * the file name is stored in config table.
10567 * Please note you need to implement your own '_pluginfile' callback function,
10568 * this setting only stores the file, it does not deal with file serving.
10570 * @copyright 2013 Petr Skoda {@link http://skodak.org}
10571 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10573 class admin_setting_configstoredfile extends admin_setting {
10574 /** @var array file area options - should be one file only */
10575 protected $options;
10576 /** @var string name of the file area */
10577 protected $filearea;
10578 /** @var int intemid */
10579 protected $itemid;
10580 /** @var string used for detection of changes */
10581 protected $oldhashes;
10584 * Create new stored file setting.
10586 * @param string $name low level setting name
10587 * @param string $visiblename human readable setting name
10588 * @param string $description description of setting
10589 * @param mixed $filearea file area for file storage
10590 * @param int $itemid itemid for file storage
10591 * @param array $options file area options
10593 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
10594 parent::__construct($name, $visiblename, $description, '');
10595 $this->filearea = $filearea;
10596 $this->itemid = $itemid;
10597 $this->options = (array)$options;
10598 $this->customcontrol = true;
10602 * Applies defaults and returns all options.
10603 * @return array
10605 protected function get_options() {
10606 global $CFG;
10608 require_once("$CFG->libdir/filelib.php");
10609 require_once("$CFG->dirroot/repository/lib.php");
10610 $defaults = array(
10611 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
10612 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
10613 'context' => context_system::instance());
10614 foreach($this->options as $k => $v) {
10615 $defaults[$k] = $v;
10618 return $defaults;
10621 public function get_setting() {
10622 return $this->config_read($this->name);
10625 public function write_setting($data) {
10626 global $USER;
10628 // Let's not deal with validation here, this is for admins only.
10629 $current = $this->get_setting();
10630 if (empty($data) && $current === null) {
10631 // This will be the case when applying default settings (installation).
10632 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
10633 } else if (!is_number($data)) {
10634 // Draft item id is expected here!
10635 return get_string('errorsetting', 'admin');
10638 $options = $this->get_options();
10639 $fs = get_file_storage();
10640 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10642 $this->oldhashes = null;
10643 if ($current) {
10644 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10645 if ($file = $fs->get_file_by_hash($hash)) {
10646 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
10648 unset($file);
10651 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
10652 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
10653 // But we can safely ignore that if the destination area is empty, so that the user is not prompt
10654 // with an error because the draft area does not exist, as he did not use it.
10655 $usercontext = context_user::instance($USER->id);
10656 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
10657 return get_string('errorsetting', 'admin');
10661 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10662 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
10664 $filepath = '';
10665 if ($files) {
10666 /** @var stored_file $file */
10667 $file = reset($files);
10668 $filepath = $file->get_filepath().$file->get_filename();
10671 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
10674 public function post_write_settings($original) {
10675 $options = $this->get_options();
10676 $fs = get_file_storage();
10677 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10679 $current = $this->get_setting();
10680 $newhashes = null;
10681 if ($current) {
10682 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10683 if ($file = $fs->get_file_by_hash($hash)) {
10684 $newhashes = $file->get_contenthash().$file->get_pathnamehash();
10686 unset($file);
10689 if ($this->oldhashes === $newhashes) {
10690 $this->oldhashes = null;
10691 return false;
10693 $this->oldhashes = null;
10695 $callbackfunction = $this->updatedcallback;
10696 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
10697 $callbackfunction($this->get_full_name());
10699 return true;
10702 public function output_html($data, $query = '') {
10703 global $PAGE, $CFG;
10705 $options = $this->get_options();
10706 $id = $this->get_id();
10707 $elname = $this->get_full_name();
10708 $draftitemid = file_get_submitted_draft_itemid($elname);
10709 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10710 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10712 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
10713 require_once("$CFG->dirroot/lib/form/filemanager.php");
10715 $fmoptions = new stdClass();
10716 $fmoptions->mainfile = $options['mainfile'];
10717 $fmoptions->maxbytes = $options['maxbytes'];
10718 $fmoptions->maxfiles = $options['maxfiles'];
10719 $fmoptions->client_id = uniqid();
10720 $fmoptions->itemid = $draftitemid;
10721 $fmoptions->subdirs = $options['subdirs'];
10722 $fmoptions->target = $id;
10723 $fmoptions->accepted_types = $options['accepted_types'];
10724 $fmoptions->return_types = $options['return_types'];
10725 $fmoptions->context = $options['context'];
10726 $fmoptions->areamaxbytes = $options['areamaxbytes'];
10728 $fm = new form_filemanager($fmoptions);
10729 $output = $PAGE->get_renderer('core', 'files');
10730 $html = $output->render($fm);
10732 $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
10733 $html .= '<input value="" id="'.$id.'" type="hidden" />';
10735 return format_admin_setting($this, $this->visiblename,
10736 '<div class="form-filemanager" data-fieldtype="filemanager">'.$html.'</div>',
10737 $this->description, true, '', '', $query);
10743 * Administration interface for user specified regular expressions for device detection.
10745 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10747 class admin_setting_devicedetectregex extends admin_setting {
10750 * Calls parent::__construct with specific args
10752 * @param string $name
10753 * @param string $visiblename
10754 * @param string $description
10755 * @param mixed $defaultsetting
10757 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
10758 global $CFG;
10759 parent::__construct($name, $visiblename, $description, $defaultsetting);
10763 * Return the current setting(s)
10765 * @return array Current settings array
10767 public function get_setting() {
10768 global $CFG;
10770 $config = $this->config_read($this->name);
10771 if (is_null($config)) {
10772 return null;
10775 return $this->prepare_form_data($config);
10779 * Save selected settings
10781 * @param array $data Array of settings to save
10782 * @return bool
10784 public function write_setting($data) {
10785 if (empty($data)) {
10786 $data = array();
10789 if ($this->config_write($this->name, $this->process_form_data($data))) {
10790 return ''; // success
10791 } else {
10792 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
10797 * Return XHTML field(s) for regexes
10799 * @param array $data Array of options to set in HTML
10800 * @return string XHTML string for the fields and wrapping div(s)
10802 public function output_html($data, $query='') {
10803 global $OUTPUT;
10805 $context = (object) [
10806 'expressions' => [],
10807 'name' => $this->get_full_name()
10810 if (empty($data)) {
10811 $looplimit = 1;
10812 } else {
10813 $looplimit = (count($data)/2)+1;
10816 for ($i=0; $i<$looplimit; $i++) {
10818 $expressionname = 'expression'.$i;
10820 if (!empty($data[$expressionname])){
10821 $expression = $data[$expressionname];
10822 } else {
10823 $expression = '';
10826 $valuename = 'value'.$i;
10828 if (!empty($data[$valuename])){
10829 $value = $data[$valuename];
10830 } else {
10831 $value= '';
10834 $context->expressions[] = [
10835 'index' => $i,
10836 'expression' => $expression,
10837 'value' => $value
10841 $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
10843 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
10847 * Converts the string of regexes
10849 * @see self::process_form_data()
10850 * @param $regexes string of regexes
10851 * @return array of form fields and their values
10853 protected function prepare_form_data($regexes) {
10855 $regexes = json_decode($regexes);
10857 $form = array();
10859 $i = 0;
10861 foreach ($regexes as $value => $regex) {
10862 $expressionname = 'expression'.$i;
10863 $valuename = 'value'.$i;
10865 $form[$expressionname] = $regex;
10866 $form[$valuename] = $value;
10867 $i++;
10870 return $form;
10874 * Converts the data from admin settings form into a string of regexes
10876 * @see self::prepare_form_data()
10877 * @param array $data array of admin form fields and values
10878 * @return false|string of regexes
10880 protected function process_form_data(array $form) {
10882 $count = count($form); // number of form field values
10884 if ($count % 2) {
10885 // we must get five fields per expression
10886 return false;
10889 $regexes = array();
10890 for ($i = 0; $i < $count / 2; $i++) {
10891 $expressionname = "expression".$i;
10892 $valuename = "value".$i;
10894 $expression = trim($form['expression'.$i]);
10895 $value = trim($form['value'.$i]);
10897 if (empty($expression)){
10898 continue;
10901 $regexes[$value] = $expression;
10904 $regexes = json_encode($regexes);
10906 return $regexes;
10912 * Multiselect for current modules
10914 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10916 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
10917 private $excludesystem;
10920 * Calls parent::__construct - note array $choices is not required
10922 * @param string $name setting name
10923 * @param string $visiblename localised setting name
10924 * @param string $description setting description
10925 * @param array $defaultsetting a plain array of default module ids
10926 * @param bool $excludesystem If true, excludes modules with 'system' archetype
10928 public function __construct($name, $visiblename, $description, $defaultsetting = array(),
10929 $excludesystem = true) {
10930 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
10931 $this->excludesystem = $excludesystem;
10935 * Loads an array of current module choices
10937 * @return bool always return true
10939 public function load_choices() {
10940 if (is_array($this->choices)) {
10941 return true;
10943 $this->choices = array();
10945 global $CFG, $DB;
10946 $records = $DB->get_records('modules', array('visible'=>1), 'name');
10947 foreach ($records as $record) {
10948 // Exclude modules if the code doesn't exist
10949 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
10950 // Also exclude system modules (if specified)
10951 if (!($this->excludesystem &&
10952 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
10953 MOD_ARCHETYPE_SYSTEM)) {
10954 $this->choices[$record->id] = $record->name;
10958 return true;
10963 * Admin setting to show if a php extension is enabled or not.
10965 * @copyright 2013 Damyon Wiese
10966 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10968 class admin_setting_php_extension_enabled extends admin_setting {
10970 /** @var string The name of the extension to check for */
10971 private $extension;
10974 * Calls parent::__construct with specific arguments
10976 public function __construct($name, $visiblename, $description, $extension) {
10977 $this->extension = $extension;
10978 $this->nosave = true;
10979 parent::__construct($name, $visiblename, $description, '');
10983 * Always returns true, does nothing
10985 * @return true
10987 public function get_setting() {
10988 return true;
10992 * Always returns true, does nothing
10994 * @return true
10996 public function get_defaultsetting() {
10997 return true;
11001 * Always returns '', does not write anything
11003 * @return string Always returns ''
11005 public function write_setting($data) {
11006 // Do not write any setting.
11007 return '';
11011 * Outputs the html for this setting.
11012 * @return string Returns an XHTML string
11014 public function output_html($data, $query='') {
11015 global $OUTPUT;
11017 $o = '';
11018 if (!extension_loaded($this->extension)) {
11019 $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description;
11021 $o .= format_admin_setting($this, $this->visiblename, $warning);
11023 return $o;
11028 * Server timezone setting.
11030 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
11031 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11032 * @author Petr Skoda <petr.skoda@totaralms.com>
11034 class admin_setting_servertimezone extends admin_setting_configselect {
11036 * Constructor.
11038 public function __construct() {
11039 $default = core_date::get_default_php_timezone();
11040 if ($default === 'UTC') {
11041 // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
11042 $default = 'Europe/London';
11045 parent::__construct('timezone',
11046 new lang_string('timezone', 'core_admin'),
11047 new lang_string('configtimezone', 'core_admin'), $default, null);
11051 * Lazy load timezone options.
11052 * @return bool true if loaded, false if error
11054 public function load_choices() {
11055 global $CFG;
11056 if (is_array($this->choices)) {
11057 return true;
11060 $current = isset($CFG->timezone) ? $CFG->timezone : null;
11061 $this->choices = core_date::get_list_of_timezones($current, false);
11062 if ($current == 99) {
11063 // Do not show 99 unless it is current value, we want to get rid of it over time.
11064 $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
11065 core_date::get_default_php_timezone());
11068 return true;
11073 * Forced user timezone setting.
11075 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
11076 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11077 * @author Petr Skoda <petr.skoda@totaralms.com>
11079 class admin_setting_forcetimezone extends admin_setting_configselect {
11081 * Constructor.
11083 public function __construct() {
11084 parent::__construct('forcetimezone',
11085 new lang_string('forcetimezone', 'core_admin'),
11086 new lang_string('helpforcetimezone', 'core_admin'), '99', null);
11090 * Lazy load timezone options.
11091 * @return bool true if loaded, false if error
11093 public function load_choices() {
11094 global $CFG;
11095 if (is_array($this->choices)) {
11096 return true;
11099 $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
11100 $this->choices = core_date::get_list_of_timezones($current, true);
11101 $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
11103 return true;
11109 * Search setup steps info.
11111 * @package core
11112 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
11113 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11115 class admin_setting_searchsetupinfo extends admin_setting {
11118 * Calls parent::__construct with specific arguments
11120 public function __construct() {
11121 $this->nosave = true;
11122 parent::__construct('searchsetupinfo', '', '', '');
11126 * Always returns true, does nothing
11128 * @return true
11130 public function get_setting() {
11131 return true;
11135 * Always returns true, does nothing
11137 * @return true
11139 public function get_defaultsetting() {
11140 return true;
11144 * Always returns '', does not write anything
11146 * @param array $data
11147 * @return string Always returns ''
11149 public function write_setting($data) {
11150 // Do not write any setting.
11151 return '';
11155 * Builds the HTML to display the control
11157 * @param string $data Unused
11158 * @param string $query
11159 * @return string
11161 public function output_html($data, $query='') {
11162 global $CFG, $OUTPUT, $ADMIN;
11164 $return = '';
11165 $brtag = html_writer::empty_tag('br');
11167 $searchareas = \core_search\manager::get_search_areas_list();
11168 $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
11169 $anyindexed = false;
11170 foreach ($searchareas as $areaid => $searcharea) {
11171 list($componentname, $varname) = $searcharea->get_config_var_name();
11172 if (get_config($componentname, $varname . '_indexingstart')) {
11173 $anyindexed = true;
11174 break;
11178 $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
11180 $table = new html_table();
11181 $table->head = array(get_string('step', 'search'), get_string('status'));
11182 $table->colclasses = array('leftalign step', 'leftalign status');
11183 $table->id = 'searchsetup';
11184 $table->attributes['class'] = 'admintable generaltable';
11185 $table->data = array();
11187 $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
11189 // Select a search engine.
11190 $row = array();
11191 $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
11192 $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
11193 array('href' => $url));
11195 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11196 if (!empty($CFG->searchengine)) {
11197 $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
11198 array('class' => 'badge badge-success'));
11201 $row[1] = $status;
11202 $table->data[] = $row;
11204 // Available areas.
11205 $row = array();
11206 $url = new moodle_url('/admin/searchareas.php');
11207 $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
11208 array('href' => $url));
11210 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11211 if ($anyenabled) {
11212 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11215 $row[1] = $status;
11216 $table->data[] = $row;
11218 // Setup search engine.
11219 $row = array();
11220 if (empty($CFG->searchengine)) {
11221 $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
11222 $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11223 } else {
11224 if ($ADMIN->locate('search' . $CFG->searchengine)) {
11225 $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
11226 $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin'));
11227 } else {
11228 $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin');
11231 // Check the engine status.
11232 $searchengine = \core_search\manager::search_engine_instance();
11233 try {
11234 $serverstatus = $searchengine->is_server_ready();
11235 } catch (\moodle_exception $e) {
11236 $serverstatus = $e->getMessage();
11238 if ($serverstatus === true) {
11239 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11240 } else {
11241 $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger'));
11243 $row[1] = $status;
11245 $table->data[] = $row;
11247 // Indexed data.
11248 $row = array();
11249 $url = new moodle_url('/admin/searchareas.php');
11250 $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
11251 if ($anyindexed) {
11252 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11253 } else {
11254 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11256 $row[1] = $status;
11257 $table->data[] = $row;
11259 // Enable global search.
11260 $row = array();
11261 $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
11262 $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
11263 array('href' => $url));
11264 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11265 if (\core_search\manager::is_global_search_enabled()) {
11266 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11268 $row[1] = $status;
11269 $table->data[] = $row;
11271 $return .= html_writer::table($table);
11273 return highlight($query, $return);
11279 * Used to validate the contents of SCSS code and ensuring they are parsable.
11281 * It does not attempt to detect undefined SCSS variables because it is designed
11282 * to be used without knowledge of other config/scss included.
11284 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11285 * @copyright 2016 Dan Poltawski <dan@moodle.com>
11287 class admin_setting_scsscode extends admin_setting_configtextarea {
11290 * Validate the contents of the SCSS to ensure its parsable. Does not
11291 * attempt to detect undefined scss variables.
11293 * @param string $data The scss code from text field.
11294 * @return mixed bool true for success or string:error on failure.
11296 public function validate($data) {
11297 if (empty($data)) {
11298 return true;
11301 $scss = new core_scss();
11302 try {
11303 $scss->compile($data);
11304 } catch (ScssPhp\ScssPhp\Exception\ParserException $e) {
11305 return get_string('scssinvalid', 'admin', $e->getMessage());
11306 } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) {
11307 // Silently ignore this - it could be a scss variable defined from somewhere
11308 // else which we are not examining here.
11309 return true;
11312 return true;
11318 * Administration setting to define a list of file types.
11320 * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
11321 * @copyright 2017 David Mudrák <david@moodle.com>
11322 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11324 class admin_setting_filetypes extends admin_setting_configtext {
11326 /** @var array Allow selection from these file types only. */
11327 protected $onlytypes = [];
11329 /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
11330 protected $allowall = true;
11332 /** @var core_form\filetypes_util instance to use as a helper. */
11333 protected $util = null;
11336 * Constructor.
11338 * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
11339 * @param string $visiblename Localised label of the setting
11340 * @param string $description Localised description of the setting
11341 * @param string $defaultsetting Default setting value.
11342 * @param array $options Setting widget options, an array with optional keys:
11343 * 'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
11344 * 'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
11346 public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
11348 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
11350 if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
11351 $this->onlytypes = $options['onlytypes'];
11354 if (!$this->onlytypes && array_key_exists('allowall', $options)) {
11355 $this->allowall = (bool)$options['allowall'];
11358 $this->util = new \core_form\filetypes_util();
11362 * Normalize the user's input and write it to the database as comma separated list.
11364 * Comma separated list as a text representation of the array was chosen to
11365 * make this compatible with how the $CFG->courseoverviewfilesext values are stored.
11367 * @param string $data Value submitted by the admin.
11368 * @return string Epty string if all good, error message otherwise.
11370 public function write_setting($data) {
11371 return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
11375 * Validate data before storage
11377 * @param string $data The setting values provided by the admin
11378 * @return bool|string True if ok, the string if error found
11380 public function validate($data) {
11382 // No need to call parent's validation here as we are PARAM_RAW.
11384 if ($this->util->is_listed($data, $this->onlytypes)) {
11385 return true;
11387 } else {
11388 $troublemakers = $this->util->get_not_listed($data, $this->onlytypes);
11389 return get_string('filetypesnotallowed', 'core_form', implode(' ', $troublemakers));
11394 * Return an HTML string for the setting element.
11396 * @param string $data The current setting value
11397 * @param string $query Admin search query to be highlighted
11398 * @return string HTML to be displayed
11400 public function output_html($data, $query='') {
11401 global $OUTPUT, $PAGE;
11403 $default = $this->get_defaultsetting();
11404 $context = (object) [
11405 'id' => $this->get_id(),
11406 'name' => $this->get_full_name(),
11407 'value' => $data,
11408 'descriptions' => $this->util->describe_file_types($data),
11410 $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
11412 $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
11413 $this->get_id(),
11414 $this->visiblename->out(),
11415 $this->onlytypes,
11416 $this->allowall,
11419 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
11423 * Should the values be always displayed in LTR mode?
11425 * We always return true here because these values are not RTL compatible.
11427 * @return bool True because these values are not RTL compatible.
11429 public function get_force_ltr() {
11430 return true;
11435 * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
11437 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11438 * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
11440 class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
11443 * Constructor.
11445 * @param string $name
11446 * @param string $visiblename
11447 * @param string $description
11448 * @param mixed $defaultsetting string or array
11449 * @param mixed $paramtype
11450 * @param string $cols
11451 * @param string $rows
11453 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
11454 $cols = '60', $rows = '8') {
11455 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
11456 // Pre-set force LTR to false.
11457 $this->set_force_ltr(false);
11461 * Validate the content and format of the age of digital consent map to ensure it is parsable.
11463 * @param string $data The age of digital consent map from text field.
11464 * @return mixed bool true for success or string:error on failure.
11466 public function validate($data) {
11467 if (empty($data)) {
11468 return true;
11471 try {
11472 \core_auth\digital_consent::parse_age_digital_consent_map($data);
11473 } catch (\moodle_exception $e) {
11474 return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
11477 return true;
11482 * Selection of plugins that can work as site policy handlers
11484 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11485 * @copyright 2018 Marina Glancy
11487 class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
11490 * Constructor
11491 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11492 * for ones in config_plugins.
11493 * @param string $visiblename localised
11494 * @param string $description long localised info
11495 * @param string $defaultsetting
11497 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11498 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11502 * Lazy-load the available choices for the select box
11504 public function load_choices() {
11505 if (during_initial_install()) {
11506 return false;
11508 if (is_array($this->choices)) {
11509 return true;
11512 $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
11513 $manager = new \core_privacy\local\sitepolicy\manager();
11514 $plugins = $manager->get_all_handlers();
11515 foreach ($plugins as $pname => $unused) {
11516 $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11517 ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
11520 return true;
11525 * Used to validate theme presets code and ensuring they compile well.
11527 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11528 * @copyright 2019 Bas Brands <bas@moodle.com>
11530 class admin_setting_configthemepreset extends admin_setting_configselect {
11532 /** @var string The name of the theme to check for */
11533 private $themename;
11536 * Constructor
11537 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
11538 * or 'myplugin/mysetting' for ones in config_plugins.
11539 * @param string $visiblename localised
11540 * @param string $description long localised info
11541 * @param string|int $defaultsetting
11542 * @param array $choices array of $value=>$label for each selection
11543 * @param string $themename name of theme to check presets for.
11545 public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
11546 $this->themename = $themename;
11547 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
11551 * Write settings if validated
11553 * @param string $data
11554 * @return string
11556 public function write_setting($data) {
11557 $validated = $this->validate($data);
11558 if ($validated !== true) {
11559 return $validated;
11561 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
11565 * Validate the preset file to ensure its parsable.
11567 * @param string $data The preset file chosen.
11568 * @return mixed bool true for success or string:error on failure.
11570 public function validate($data) {
11572 if (in_array($data, ['default.scss', 'plain.scss'])) {
11573 return true;
11576 $fs = get_file_storage();
11577 $theme = theme_config::load($this->themename);
11578 $context = context_system::instance();
11580 // If the preset has not changed there is no need to validate it.
11581 if ($theme->settings->preset == $data) {
11582 return true;
11585 if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
11586 // This operation uses a lot of resources.
11587 raise_memory_limit(MEMORY_EXTRA);
11588 core_php_time_limit::raise(300);
11590 // TODO: MDL-62757 When changing anything in this method please do not forget to check
11591 // if the get_css_content_from_scss() method in class theme_config needs updating too.
11593 $compiler = new core_scss();
11594 $compiler->prepend_raw_scss($theme->get_pre_scss_code());
11595 $compiler->append_raw_scss($presetfile->get_content());
11596 if ($scssproperties = $theme->get_scss_property()) {
11597 $compiler->setImportPaths($scssproperties[0]);
11599 $compiler->append_raw_scss($theme->get_extra_scss_code());
11601 try {
11602 $compiler->to_css();
11603 } catch (Exception $e) {
11604 return get_string('invalidthemepreset', 'admin', $e->getMessage());
11607 // Try to save memory.
11608 $compiler = null;
11609 unset($compiler);
11612 return true;
11617 * Selection of plugins that can work as H5P libraries handlers
11619 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11620 * @copyright 2020 Sara Arjona <sara@moodle.com>
11622 class admin_settings_h5plib_handler_select extends admin_setting_configselect {
11625 * Constructor
11626 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11627 * for ones in config_plugins.
11628 * @param string $visiblename localised
11629 * @param string $description long localised info
11630 * @param string $defaultsetting
11632 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11633 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11637 * Lazy-load the available choices for the select box
11639 public function load_choices() {
11640 if (during_initial_install()) {
11641 return false;
11643 if (is_array($this->choices)) {
11644 return true;
11647 $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
11648 foreach ($this->choices as $name => $class) {
11649 $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11650 ['name' => new lang_string('pluginname', $name), 'component' => $name]);
11653 return true;