Merge branch 'MDL-79498-402' of https://github.com/junpataleta/moodle into MOODLE_402...
[moodle.git] / lib / adminlib.php
blob195888c8e7f719339f85eb543740f3d17d4e1683
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 use core_admin\local\settings\linkable_settings_page;
107 defined('MOODLE_INTERNAL') || die();
109 /// Add libraries
110 require_once($CFG->libdir.'/ddllib.php');
111 require_once($CFG->libdir.'/xmlize.php');
112 require_once($CFG->libdir.'/messagelib.php');
114 // Add classes, traits, and interfaces which should be autoloaded.
115 // The autoloader is configured late in setup.php, after ABORT_AFTER_CONFIG.
116 // This is also required where the setup system is not included at all.
117 require_once($CFG->dirroot.'/'.$CFG->admin.'/classes/local/settings/linkable_settings_page.php');
119 define('INSECURE_DATAROOT_WARNING', 1);
120 define('INSECURE_DATAROOT_ERROR', 2);
123 * Automatically clean-up all plugin data and remove the plugin DB tables
125 * NOTE: do not call directly, use new /admin/plugins.php?uninstall=component instead!
127 * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
128 * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
129 * @uses global $OUTPUT to produce notices and other messages
130 * @return void
132 function uninstall_plugin($type, $name) {
133 global $CFG, $DB, $OUTPUT;
135 // This may take a long time.
136 core_php_time_limit::raise();
138 // Recursively uninstall all subplugins first.
139 $subplugintypes = core_component::get_plugin_types_with_subplugins();
140 if (isset($subplugintypes[$type])) {
141 $base = core_component::get_plugin_directory($type, $name);
143 $subpluginsfile = "{$base}/db/subplugins.json";
144 if (file_exists($subpluginsfile)) {
145 $subplugins = (array) json_decode(file_get_contents($subpluginsfile))->plugintypes;
146 } else if (file_exists("{$base}/db/subplugins.php")) {
147 debugging('Use of subplugins.php has been deprecated. ' .
148 'Please update your plugin to provide a subplugins.json file instead.',
149 DEBUG_DEVELOPER);
150 $subplugins = [];
151 include("{$base}/db/subplugins.php");
154 if (!empty($subplugins)) {
155 foreach (array_keys($subplugins) as $subplugintype) {
156 $instances = core_component::get_plugin_list($subplugintype);
157 foreach ($instances as $subpluginname => $notusedpluginpath) {
158 uninstall_plugin($subplugintype, $subpluginname);
164 $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
166 if ($type === 'mod') {
167 $pluginname = $name; // eg. 'forum'
168 if (get_string_manager()->string_exists('modulename', $component)) {
169 $strpluginname = get_string('modulename', $component);
170 } else {
171 $strpluginname = $component;
174 } else {
175 $pluginname = $component;
176 if (get_string_manager()->string_exists('pluginname', $component)) {
177 $strpluginname = get_string('pluginname', $component);
178 } else {
179 $strpluginname = $component;
183 echo $OUTPUT->heading($pluginname);
185 // Delete all tag areas, collections and instances associated with this plugin.
186 core_tag_area::uninstall($component);
188 // Custom plugin uninstall.
189 $plugindirectory = core_component::get_plugin_directory($type, $name);
190 $uninstalllib = $plugindirectory . '/db/uninstall.php';
191 if (file_exists($uninstalllib)) {
192 require_once($uninstalllib);
193 $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
194 if (function_exists($uninstallfunction)) {
195 // Do not verify result, let plugin complain if necessary.
196 $uninstallfunction();
200 // Specific plugin type cleanup.
201 $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
202 if ($plugininfo) {
203 $plugininfo->uninstall_cleanup();
204 core_plugin_manager::reset_caches();
206 $plugininfo = null;
208 // Perform clean-up task common for all the plugin/subplugin types.
210 // Delete the web service functions and pre-built services.
211 \core_external\util::delete_service_descriptions($component);
213 // delete calendar events
214 $DB->delete_records('event', array('modulename' => $pluginname));
215 $DB->delete_records('event', ['component' => $component]);
217 // Delete scheduled tasks.
218 $DB->delete_records('task_adhoc', ['component' => $component]);
219 $DB->delete_records('task_scheduled', array('component' => $component));
221 // Delete Inbound Message datakeys.
222 $DB->delete_records_select('messageinbound_datakeys',
223 'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
225 // Delete Inbound Message handlers.
226 $DB->delete_records('messageinbound_handlers', array('component' => $component));
228 // delete all the logs
229 $DB->delete_records('log', array('module' => $pluginname));
231 // delete log_display information
232 $DB->delete_records('log_display', array('component' => $component));
234 // delete the module configuration records
235 unset_all_config_for_plugin($component);
236 if ($type === 'mod') {
237 unset_all_config_for_plugin($pluginname);
240 // Wipe any xAPI state information.
241 if (core_xapi\handler::supports_xapi($component)) {
242 core_xapi\api::remove_states_from_component($component);
245 // delete message provider
246 message_provider_uninstall($component);
248 // delete the plugin tables
249 $xmldbfilepath = $plugindirectory . '/db/install.xml';
250 drop_plugin_tables($component, $xmldbfilepath, false);
251 if ($type === 'mod' or $type === 'block') {
252 // non-frankenstyle table prefixes
253 drop_plugin_tables($name, $xmldbfilepath, false);
256 // delete the capabilities that were defined by this module
257 capabilities_cleanup($component);
259 // Delete all remaining files in the filepool owned by the component.
260 $fs = get_file_storage();
261 $fs->delete_component_files($component);
263 // Finally purge all caches.
264 purge_all_caches();
266 // Invalidate the hash used for upgrade detections.
267 set_config('allversionshash', '');
269 echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
273 * Returns the version of installed component
275 * @param string $component component name
276 * @param string $source either 'disk' or 'installed' - where to get the version information from
277 * @return string|bool version number or false if the component is not found
279 function get_component_version($component, $source='installed') {
280 global $CFG, $DB;
282 list($type, $name) = core_component::normalize_component($component);
284 // moodle core or a core subsystem
285 if ($type === 'core') {
286 if ($source === 'installed') {
287 if (empty($CFG->version)) {
288 return false;
289 } else {
290 return $CFG->version;
292 } else {
293 if (!is_readable($CFG->dirroot.'/version.php')) {
294 return false;
295 } else {
296 $version = null; //initialize variable for IDEs
297 include($CFG->dirroot.'/version.php');
298 return $version;
303 // activity module
304 if ($type === 'mod') {
305 if ($source === 'installed') {
306 if ($CFG->version < 2013092001.02) {
307 return $DB->get_field('modules', 'version', array('name'=>$name));
308 } else {
309 return get_config('mod_'.$name, 'version');
312 } else {
313 $mods = core_component::get_plugin_list('mod');
314 if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
315 return false;
316 } else {
317 $plugin = new stdClass();
318 $plugin->version = null;
319 $module = $plugin;
320 include($mods[$name].'/version.php');
321 return $plugin->version;
326 // block
327 if ($type === 'block') {
328 if ($source === 'installed') {
329 if ($CFG->version < 2013092001.02) {
330 return $DB->get_field('block', 'version', array('name'=>$name));
331 } else {
332 return get_config('block_'.$name, 'version');
334 } else {
335 $blocks = core_component::get_plugin_list('block');
336 if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
337 return false;
338 } else {
339 $plugin = new stdclass();
340 include($blocks[$name].'/version.php');
341 return $plugin->version;
346 // all other plugin types
347 if ($source === 'installed') {
348 return get_config($type.'_'.$name, 'version');
349 } else {
350 $plugins = core_component::get_plugin_list($type);
351 if (empty($plugins[$name])) {
352 return false;
353 } else {
354 $plugin = new stdclass();
355 include($plugins[$name].'/version.php');
356 return $plugin->version;
362 * Delete all plugin tables
364 * @param string $name Name of plugin, used as table prefix
365 * @param string $file Path to install.xml file
366 * @param bool $feedback defaults to true
367 * @return bool Always returns true
369 function drop_plugin_tables($name, $file, $feedback=true) {
370 global $CFG, $DB;
372 // first try normal delete
373 if (file_exists($file)) {
374 $DB->get_manager()->delete_tables_from_xmldb_file($file);
377 // then try to find all tables that start with name and are not in any xml file
378 $used_tables = get_used_table_names();
380 $tables = $DB->get_tables();
382 /// Iterate over, fixing id fields as necessary
383 foreach ($tables as $table) {
384 if (in_array($table, $used_tables)) {
385 continue;
388 if (strpos($table, $name) !== 0) {
389 continue;
392 // found orphan table --> delete it
393 if ($DB->get_manager()->table_exists($table)) {
394 $xmldb_table = new xmldb_table($table);
395 $DB->get_manager()->drop_table($xmldb_table);
399 return true;
403 * Returns names of all known tables == tables that moodle knows about.
405 * @return array Array of lowercase table names
407 function get_used_table_names() {
408 $table_names = array();
409 $dbdirs = get_db_directories();
411 foreach ($dbdirs as $dbdir) {
412 $file = $dbdir.'/install.xml';
414 $xmldb_file = new xmldb_file($file);
416 if (!$xmldb_file->fileExists()) {
417 continue;
420 $loaded = $xmldb_file->loadXMLStructure();
421 $structure = $xmldb_file->getStructure();
423 if ($loaded and $tables = $structure->getTables()) {
424 foreach($tables as $table) {
425 $table_names[] = strtolower($table->getName());
430 return $table_names;
434 * Returns list of all directories where we expect install.xml files
435 * @return array Array of paths
437 function get_db_directories() {
438 global $CFG;
440 $dbdirs = array();
442 /// First, the main one (lib/db)
443 $dbdirs[] = $CFG->libdir.'/db';
445 /// Then, all the ones defined by core_component::get_plugin_types()
446 $plugintypes = core_component::get_plugin_types();
447 foreach ($plugintypes as $plugintype => $pluginbasedir) {
448 if ($plugins = core_component::get_plugin_list($plugintype)) {
449 foreach ($plugins as $plugin => $plugindir) {
450 $dbdirs[] = $plugindir.'/db';
455 return $dbdirs;
459 * Try to obtain or release the cron lock.
460 * @param string $name name of lock
461 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
462 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
463 * @return bool true if lock obtained
465 function set_cron_lock($name, $until, $ignorecurrent=false) {
466 global $DB;
467 if (empty($name)) {
468 debugging("Tried to get a cron lock for a null fieldname");
469 return false;
472 // remove lock by force == remove from config table
473 if (is_null($until)) {
474 set_config($name, null);
475 return true;
478 if (!$ignorecurrent) {
479 // read value from db - other processes might have changed it
480 $value = $DB->get_field('config', 'value', array('name'=>$name));
482 if ($value and $value > time()) {
483 //lock active
484 return false;
488 set_config($name, $until);
489 return true;
493 * Test if and critical warnings are present
494 * @return bool
496 function admin_critical_warnings_present() {
497 global $SESSION;
499 if (!has_capability('moodle/site:config', context_system::instance())) {
500 return 0;
503 if (!isset($SESSION->admin_critical_warning)) {
504 $SESSION->admin_critical_warning = 0;
505 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
506 $SESSION->admin_critical_warning = 1;
510 return $SESSION->admin_critical_warning;
514 * Detects if float supports at least 10 decimal digits
516 * Detects if float supports at least 10 decimal digits
517 * and also if float-->string conversion works as expected.
519 * @return bool true if problem found
521 function is_float_problem() {
522 $num1 = 2009010200.01;
523 $num2 = 2009010200.02;
525 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
529 * Try to verify that dataroot is not accessible from web.
531 * Try to verify that dataroot is not accessible from web.
532 * It is not 100% correct but might help to reduce number of vulnerable sites.
533 * Protection from httpd.conf and .htaccess is not detected properly.
535 * @uses INSECURE_DATAROOT_WARNING
536 * @uses INSECURE_DATAROOT_ERROR
537 * @param bool $fetchtest try to test public access by fetching file, default false
538 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
540 function is_dataroot_insecure($fetchtest=false) {
541 global $CFG;
543 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
545 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
546 $rp = strrev(trim($rp, '/'));
547 $rp = explode('/', $rp);
548 foreach($rp as $r) {
549 if (strpos($siteroot, '/'.$r.'/') === 0) {
550 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
551 } else {
552 break; // probably alias root
556 $siteroot = strrev($siteroot);
557 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
559 if (strpos($dataroot, $siteroot) !== 0) {
560 return false;
563 if (!$fetchtest) {
564 return INSECURE_DATAROOT_WARNING;
567 // now try all methods to fetch a test file using http protocol
569 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
570 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
571 $httpdocroot = $matches[1];
572 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
573 make_upload_directory('diag');
574 $testfile = $CFG->dataroot.'/diag/public.txt';
575 if (!file_exists($testfile)) {
576 file_put_contents($testfile, 'test file, do not delete');
577 @chmod($testfile, $CFG->filepermissions);
579 $teststr = trim(file_get_contents($testfile));
580 if (empty($teststr)) {
581 // hmm, strange
582 return INSECURE_DATAROOT_WARNING;
585 $testurl = $datarooturl.'/diag/public.txt';
586 if (extension_loaded('curl') and
587 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
588 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
589 ($ch = @curl_init($testurl)) !== false) {
590 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
591 curl_setopt($ch, CURLOPT_HEADER, false);
592 $data = curl_exec($ch);
593 if (!curl_errno($ch)) {
594 $data = trim($data);
595 if ($data === $teststr) {
596 curl_close($ch);
597 return INSECURE_DATAROOT_ERROR;
600 curl_close($ch);
603 if ($data = @file_get_contents($testurl)) {
604 $data = trim($data);
605 if ($data === $teststr) {
606 return INSECURE_DATAROOT_ERROR;
610 preg_match('|https?://([^/]+)|i', $testurl, $matches);
611 $sitename = $matches[1];
612 $error = 0;
613 if ($fp = @fsockopen($sitename, 80, $error)) {
614 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
615 $localurl = $matches[1];
616 $out = "GET $localurl HTTP/1.1\r\n";
617 $out .= "Host: $sitename\r\n";
618 $out .= "Connection: Close\r\n\r\n";
619 fwrite($fp, $out);
620 $data = '';
621 $incoming = false;
622 while (!feof($fp)) {
623 if ($incoming) {
624 $data .= fgets($fp, 1024);
625 } else if (@fgets($fp, 1024) === "\r\n") {
626 $incoming = true;
629 fclose($fp);
630 $data = trim($data);
631 if ($data === $teststr) {
632 return INSECURE_DATAROOT_ERROR;
636 return INSECURE_DATAROOT_WARNING;
640 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
642 function enable_cli_maintenance_mode() {
643 global $CFG, $SITE;
645 if (file_exists("$CFG->dataroot/climaintenance.html")) {
646 unlink("$CFG->dataroot/climaintenance.html");
649 if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
650 $data = $CFG->maintenance_message;
651 $data = bootstrap_renderer::early_error_content($data, null, null, null);
652 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
654 } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
655 $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
657 } else {
658 $data = get_string('sitemaintenance', 'admin');
659 $data = bootstrap_renderer::early_error_content($data, null, null, null);
660 $data = bootstrap_renderer::plain_page(get_string('sitemaintenancetitle', 'admin',
661 format_string($SITE->fullname, true, ['context' => context_system::instance()])), $data);
664 file_put_contents("$CFG->dataroot/climaintenance.html", $data);
665 chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
668 /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
672 * Interface for anything appearing in the admin tree
674 * The interface that is implemented by anything that appears in the admin tree
675 * block. It forces inheriting classes to define a method for checking user permissions
676 * and methods for finding something in the admin tree.
678 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
680 interface part_of_admin_tree {
683 * Finds a named part_of_admin_tree.
685 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
686 * and not parentable_part_of_admin_tree, then this function should only check if
687 * $this->name matches $name. If it does, it should return a reference to $this,
688 * otherwise, it should return a reference to NULL.
690 * If a class inherits parentable_part_of_admin_tree, this method should be called
691 * recursively on all child objects (assuming, of course, the parent object's name
692 * doesn't match the search criterion).
694 * @param string $name The internal name of the part_of_admin_tree we're searching for.
695 * @return mixed An object reference or a NULL reference.
697 public function locate($name);
700 * Removes named part_of_admin_tree.
702 * @param string $name The internal name of the part_of_admin_tree we want to remove.
703 * @return bool success.
705 public function prune($name);
708 * Search using query
709 * @param string $query
710 * @return mixed array-object structure of found settings and pages
712 public function search($query);
715 * Verifies current user's access to this part_of_admin_tree.
717 * Used to check if the current user has access to this part of the admin tree or
718 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
719 * then this method is usually just a call to has_capability() in the site context.
721 * If a class inherits parentable_part_of_admin_tree, this method should return the
722 * logical OR of the return of check_access() on all child objects.
724 * @return bool True if the user has access, false if she doesn't.
726 public function check_access();
729 * Mostly useful for removing of some parts of the tree in admin tree block.
731 * @return True is hidden from normal list view
733 public function is_hidden();
736 * Show we display Save button at the page bottom?
737 * @return bool
739 public function show_save();
744 * Interface implemented by any part_of_admin_tree that has children.
746 * The interface implemented by any part_of_admin_tree that can be a parent
747 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
748 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
749 * include an add method for adding other part_of_admin_tree objects as children.
751 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
753 interface parentable_part_of_admin_tree extends part_of_admin_tree {
756 * Adds a part_of_admin_tree object to the admin tree.
758 * Used to add a part_of_admin_tree object to this object or a child of this
759 * object. $something should only be added if $destinationname matches
760 * $this->name. If it doesn't, add should be called on child objects that are
761 * also parentable_part_of_admin_tree's.
763 * $something should be appended as the last child in the $destinationname. If the
764 * $beforesibling is specified, $something should be prepended to it. If the given
765 * sibling is not found, $something should be appended to the end of $destinationname
766 * and a developer debugging message should be displayed.
768 * @param string $destinationname The internal name of the new parent for $something.
769 * @param part_of_admin_tree $something The object to be added.
770 * @return bool True on success, false on failure.
772 public function add($destinationname, $something, $beforesibling = null);
778 * The object used to represent folders (a.k.a. categories) in the admin tree block.
780 * Each admin_category object contains a number of part_of_admin_tree objects.
782 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
784 class admin_category implements parentable_part_of_admin_tree, linkable_settings_page {
786 /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
787 protected $children;
788 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
789 public $name;
790 /** @var string The displayed name for this category. Usually obtained through get_string() */
791 public $visiblename;
792 /** @var bool Should this category be hidden in admin tree block? */
793 public $hidden;
794 /** @var mixed Either a string or an array or strings */
795 public $path;
796 /** @var mixed Either a string or an array or strings */
797 public $visiblepath;
799 /** @var array fast lookup category cache, all categories of one tree point to one cache */
800 protected $category_cache;
802 /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
803 protected $sort = false;
804 /** @var bool If set to true children will be sorted in ascending order. */
805 protected $sortasc = true;
806 /** @var bool If set to true sub categories and pages will be split and then sorted.. */
807 protected $sortsplit = true;
808 /** @var bool $sorted True if the children have been sorted and don't need resorting */
809 protected $sorted = false;
812 * Constructor for an empty admin category
814 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
815 * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
816 * @param bool $hidden hide category in admin tree block, defaults to false
818 public function __construct($name, $visiblename, $hidden=false) {
819 $this->children = array();
820 $this->name = $name;
821 $this->visiblename = $visiblename;
822 $this->hidden = $hidden;
826 * Get the URL to view this settings page.
828 * @return moodle_url
830 public function get_settings_page_url(): moodle_url {
831 return new moodle_url(
832 '/admin/category.php',
834 'category' => $this->name,
840 * Returns a reference to the part_of_admin_tree object with internal name $name.
842 * @param string $name The internal name of the object we want.
843 * @param bool $findpath initialize path and visiblepath arrays
844 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
845 * defaults to false
847 public function locate($name, $findpath=false) {
848 if (!isset($this->category_cache[$this->name])) {
849 // somebody much have purged the cache
850 $this->category_cache[$this->name] = $this;
853 if ($this->name == $name) {
854 if ($findpath) {
855 $this->visiblepath[] = $this->visiblename;
856 $this->path[] = $this->name;
858 return $this;
861 // quick category lookup
862 if (!$findpath and isset($this->category_cache[$name])) {
863 return $this->category_cache[$name];
866 $return = NULL;
867 foreach($this->children as $childid=>$unused) {
868 if ($return = $this->children[$childid]->locate($name, $findpath)) {
869 break;
873 if (!is_null($return) and $findpath) {
874 $return->visiblepath[] = $this->visiblename;
875 $return->path[] = $this->name;
878 return $return;
882 * Search using query
884 * @param string query
885 * @return mixed array-object structure of found settings and pages
887 public function search($query) {
888 $result = array();
889 foreach ($this->get_children() as $child) {
890 $subsearch = $child->search($query);
891 if (!is_array($subsearch)) {
892 debugging('Incorrect search result from '.$child->name);
893 continue;
895 $result = array_merge($result, $subsearch);
897 return $result;
901 * Removes part_of_admin_tree object with internal name $name.
903 * @param string $name The internal name of the object we want to remove.
904 * @return bool success
906 public function prune($name) {
908 if ($this->name == $name) {
909 return false; //can not remove itself
912 foreach($this->children as $precedence => $child) {
913 if ($child->name == $name) {
914 // clear cache and delete self
915 while($this->category_cache) {
916 // delete the cache, but keep the original array address
917 array_pop($this->category_cache);
919 unset($this->children[$precedence]);
920 return true;
921 } else if ($this->children[$precedence]->prune($name)) {
922 return true;
925 return false;
929 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
931 * By default the new part of the tree is appended as the last child of the parent. You
932 * can specify a sibling node that the new part should be prepended to. If the given
933 * sibling is not found, the part is appended to the end (as it would be by default) and
934 * a developer debugging message is displayed.
936 * @throws coding_exception if the $beforesibling is empty string or is not string at all.
937 * @param string $destinationame The internal name of the immediate parent that we want for $something.
938 * @param mixed $something A part_of_admin_tree or setting instance to be added.
939 * @param string $beforesibling The name of the parent's child the $something should be prepended to.
940 * @return bool True if successfully added, false if $something can not be added.
942 public function add($parentname, $something, $beforesibling = null) {
943 global $CFG;
945 $parent = $this->locate($parentname);
946 if (is_null($parent)) {
947 debugging('parent does not exist!');
948 return false;
951 if ($something instanceof part_of_admin_tree) {
952 if (!($parent instanceof parentable_part_of_admin_tree)) {
953 debugging('error - parts of tree can be inserted only into parentable parts');
954 return false;
956 if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
957 // The name of the node is already used, simply warn the developer that this should not happen.
958 // It is intentional to check for the debug level before performing the check.
959 debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
961 if (is_null($beforesibling)) {
962 // Append $something as the parent's last child.
963 $parent->children[] = $something;
964 } else {
965 if (!is_string($beforesibling) or trim($beforesibling) === '') {
966 throw new coding_exception('Unexpected value of the beforesibling parameter');
968 // Try to find the position of the sibling.
969 $siblingposition = null;
970 foreach ($parent->children as $childposition => $child) {
971 if ($child->name === $beforesibling) {
972 $siblingposition = $childposition;
973 break;
976 if (is_null($siblingposition)) {
977 debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
978 $parent->children[] = $something;
979 } else {
980 $parent->children = array_merge(
981 array_slice($parent->children, 0, $siblingposition),
982 array($something),
983 array_slice($parent->children, $siblingposition)
987 if ($something instanceof admin_category) {
988 if (isset($this->category_cache[$something->name])) {
989 debugging('Duplicate admin category name: '.$something->name);
990 } else {
991 $this->category_cache[$something->name] = $something;
992 $something->category_cache =& $this->category_cache;
993 foreach ($something->children as $child) {
994 // just in case somebody already added subcategories
995 if ($child instanceof admin_category) {
996 if (isset($this->category_cache[$child->name])) {
997 debugging('Duplicate admin category name: '.$child->name);
998 } else {
999 $this->category_cache[$child->name] = $child;
1000 $child->category_cache =& $this->category_cache;
1006 return true;
1008 } else {
1009 debugging('error - can not add this element');
1010 return false;
1016 * Checks if the user has access to anything in this category.
1018 * @return bool True if the user has access to at least one child in this category, false otherwise.
1020 public function check_access() {
1021 foreach ($this->children as $child) {
1022 if ($child->check_access()) {
1023 return true;
1026 return false;
1030 * Is this category hidden in admin tree block?
1032 * @return bool True if hidden
1034 public function is_hidden() {
1035 return $this->hidden;
1039 * Show we display Save button at the page bottom?
1040 * @return bool
1042 public function show_save() {
1043 foreach ($this->children as $child) {
1044 if ($child->show_save()) {
1045 return true;
1048 return false;
1052 * Sets sorting on this category.
1054 * Please note this function doesn't actually do the sorting.
1055 * It can be called anytime.
1056 * Sorting occurs when the user calls get_children.
1057 * Code using the children array directly won't see the sorted results.
1059 * @param bool $sort If set to true children will be sorted, if false they won't be.
1060 * @param bool $asc If true sorting will be ascending, otherwise descending.
1061 * @param bool $split If true we sort pages and sub categories separately.
1063 public function set_sorting($sort, $asc = true, $split = true) {
1064 $this->sort = (bool)$sort;
1065 $this->sortasc = (bool)$asc;
1066 $this->sortsplit = (bool)$split;
1070 * Returns the children associated with this category.
1072 * @return part_of_admin_tree[]
1074 public function get_children() {
1075 // If we should sort and it hasn't already been sorted.
1076 if ($this->sort && !$this->sorted) {
1077 if ($this->sortsplit) {
1078 $categories = array();
1079 $pages = array();
1080 foreach ($this->children as $child) {
1081 if ($child instanceof admin_category) {
1082 $categories[] = $child;
1083 } else {
1084 $pages[] = $child;
1087 core_collator::asort_objects_by_property($categories, 'visiblename');
1088 core_collator::asort_objects_by_property($pages, 'visiblename');
1089 if (!$this->sortasc) {
1090 $categories = array_reverse($categories);
1091 $pages = array_reverse($pages);
1093 $this->children = array_merge($pages, $categories);
1094 } else {
1095 core_collator::asort_objects_by_property($this->children, 'visiblename');
1096 if (!$this->sortasc) {
1097 $this->children = array_reverse($this->children);
1100 $this->sorted = true;
1102 return $this->children;
1106 * Magically gets a property from this object.
1108 * @param $property
1109 * @return part_of_admin_tree[]
1110 * @throws coding_exception
1112 public function __get($property) {
1113 if ($property === 'children') {
1114 return $this->get_children();
1116 throw new coding_exception('Invalid property requested.');
1120 * Magically sets a property against this object.
1122 * @param string $property
1123 * @param mixed $value
1124 * @throws coding_exception
1126 public function __set($property, $value) {
1127 if ($property === 'children') {
1128 $this->sorted = false;
1129 $this->children = $value;
1130 } else {
1131 throw new coding_exception('Invalid property requested.');
1136 * Checks if an inaccessible property is set.
1138 * @param string $property
1139 * @return bool
1140 * @throws coding_exception
1142 public function __isset($property) {
1143 if ($property === 'children') {
1144 return isset($this->children);
1146 throw new coding_exception('Invalid property requested.');
1152 * Root of admin settings tree, does not have any parent.
1154 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1156 class admin_root extends admin_category {
1157 /** @var array List of errors */
1158 public $errors;
1159 /** @var string search query */
1160 public $search;
1161 /** @var bool full tree flag - true means all settings required, false only pages required */
1162 public $fulltree;
1163 /** @var bool flag indicating loaded tree */
1164 public $loaded;
1165 /** @var mixed site custom defaults overriding defaults in settings files*/
1166 public $custom_defaults;
1169 * @param bool $fulltree true means all settings required,
1170 * false only pages required
1172 public function __construct($fulltree) {
1173 global $CFG;
1175 parent::__construct('root', get_string('administration'), false);
1176 $this->errors = array();
1177 $this->search = '';
1178 $this->fulltree = $fulltree;
1179 $this->loaded = false;
1181 $this->category_cache = array();
1183 // load custom defaults if found
1184 $this->custom_defaults = null;
1185 $defaultsfile = "$CFG->dirroot/local/defaults.php";
1186 if (is_readable($defaultsfile)) {
1187 $defaults = array();
1188 include($defaultsfile);
1189 if (is_array($defaults) and count($defaults)) {
1190 $this->custom_defaults = $defaults;
1196 * Empties children array, and sets loaded to false
1198 * @param bool $requirefulltree
1200 public function purge_children($requirefulltree) {
1201 $this->children = array();
1202 $this->fulltree = ($requirefulltree || $this->fulltree);
1203 $this->loaded = false;
1204 //break circular dependencies - this helps PHP 5.2
1205 while($this->category_cache) {
1206 array_pop($this->category_cache);
1208 $this->category_cache = array();
1214 * Links external PHP pages into the admin tree.
1216 * See detailed usage example at the top of this document (adminlib.php)
1218 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1220 class admin_externalpage implements part_of_admin_tree, linkable_settings_page {
1222 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1223 public $name;
1225 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1226 public $visiblename;
1228 /** @var string The external URL that we should link to when someone requests this external page. */
1229 public $url;
1231 /** @var array The role capability/permission a user must have to access this external page. */
1232 public $req_capability;
1234 /** @var object The context in which capability/permission should be checked, default is site context. */
1235 public $context;
1237 /** @var bool hidden in admin tree block. */
1238 public $hidden;
1240 /** @var mixed either string or array of string */
1241 public $path;
1243 /** @var array list of visible names of page parents */
1244 public $visiblepath;
1247 * Constructor for adding an external page into the admin tree.
1249 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1250 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1251 * @param string $url The external URL that we should link to when someone requests this external page.
1252 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1253 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1254 * @param stdClass $context The context the page relates to. Not sure what happens
1255 * if you specify something other than system or front page. Defaults to system.
1257 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1258 $this->name = $name;
1259 $this->visiblename = $visiblename;
1260 $this->url = $url;
1261 if (is_array($req_capability)) {
1262 $this->req_capability = $req_capability;
1263 } else {
1264 $this->req_capability = array($req_capability);
1266 $this->hidden = $hidden;
1267 $this->context = $context;
1271 * Get the URL to view this settings page.
1273 * @return moodle_url
1275 public function get_settings_page_url(): moodle_url {
1276 return new moodle_url($this->url);
1280 * Returns a reference to the part_of_admin_tree object with internal name $name.
1282 * @param string $name The internal name of the object we want.
1283 * @param bool $findpath defaults to false
1284 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1286 public function locate($name, $findpath=false) {
1287 if ($this->name == $name) {
1288 if ($findpath) {
1289 $this->visiblepath = array($this->visiblename);
1290 $this->path = array($this->name);
1292 return $this;
1293 } else {
1294 $return = NULL;
1295 return $return;
1300 * This function always returns false, required function by interface
1302 * @param string $name
1303 * @return false
1305 public function prune($name) {
1306 return false;
1310 * Search using query
1312 * @param string $query
1313 * @return mixed array-object structure of found settings and pages
1315 public function search($query) {
1316 $found = false;
1317 if (strpos(strtolower($this->name), $query) !== false) {
1318 $found = true;
1319 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1320 $found = true;
1322 if ($found) {
1323 $result = new stdClass();
1324 $result->page = $this;
1325 $result->settings = array();
1326 return array($this->name => $result);
1327 } else {
1328 return array();
1333 * Determines if the current user has access to this external page based on $this->req_capability.
1335 * @return bool True if user has access, false otherwise.
1337 public function check_access() {
1338 global $CFG;
1339 $context = empty($this->context) ? context_system::instance() : $this->context;
1340 foreach($this->req_capability as $cap) {
1341 if (has_capability($cap, $context)) {
1342 return true;
1345 return false;
1349 * Is this external page hidden in admin tree block?
1351 * @return bool True if hidden
1353 public function is_hidden() {
1354 return $this->hidden;
1358 * Show we display Save button at the page bottom?
1359 * @return bool
1361 public function show_save() {
1362 return false;
1367 * Used to store details of the dependency between two settings elements.
1369 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1370 * @copyright 2017 Davo Smith, Synergy Learning
1372 class admin_settingdependency {
1373 /** @var string the name of the setting to be shown/hidden */
1374 public $settingname;
1375 /** @var string the setting this is dependent on */
1376 public $dependenton;
1377 /** @var string the condition to show/hide the element */
1378 public $condition;
1379 /** @var string the value to compare against */
1380 public $value;
1382 /** @var string[] list of valid conditions */
1383 private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
1386 * admin_settingdependency constructor.
1387 * @param string $settingname
1388 * @param string $dependenton
1389 * @param string $condition
1390 * @param string $value
1391 * @throws \coding_exception
1393 public function __construct($settingname, $dependenton, $condition, $value) {
1394 $this->settingname = $this->parse_name($settingname);
1395 $this->dependenton = $this->parse_name($dependenton);
1396 $this->condition = $condition;
1397 $this->value = $value;
1399 if (!in_array($this->condition, self::$validconditions)) {
1400 throw new coding_exception("Invalid condition '$condition'");
1405 * Convert the setting name into the form field name.
1406 * @param string $name
1407 * @return string
1409 private function parse_name($name) {
1410 $bits = explode('/', $name);
1411 $name = array_pop($bits);
1412 $plugin = '';
1413 if ($bits) {
1414 $plugin = array_pop($bits);
1415 if ($plugin === 'moodle') {
1416 $plugin = '';
1419 return 's_'.$plugin.'_'.$name;
1423 * Gather together all the dependencies in a format suitable for initialising javascript
1424 * @param admin_settingdependency[] $dependencies
1425 * @return array
1427 public static function prepare_for_javascript($dependencies) {
1428 $result = [];
1429 foreach ($dependencies as $d) {
1430 if (!isset($result[$d->dependenton])) {
1431 $result[$d->dependenton] = [];
1433 if (!isset($result[$d->dependenton][$d->condition])) {
1434 $result[$d->dependenton][$d->condition] = [];
1436 if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
1437 $result[$d->dependenton][$d->condition][$d->value] = [];
1439 $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
1441 return $result;
1446 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1448 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1450 class admin_settingpage implements part_of_admin_tree, linkable_settings_page {
1452 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1453 public $name;
1455 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1456 public $visiblename;
1458 /** @var mixed An array of admin_setting objects that are part of this setting page. */
1459 public $settings;
1461 /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
1462 protected $dependencies = [];
1464 /** @var array The role capability/permission a user must have to access this external page. */
1465 public $req_capability;
1467 /** @var object The context in which capability/permission should be checked, default is site context. */
1468 public $context;
1470 /** @var bool hidden in admin tree block. */
1471 public $hidden;
1473 /** @var mixed string of paths or array of strings of paths */
1474 public $path;
1476 /** @var array list of visible names of page parents */
1477 public $visiblepath;
1480 * see admin_settingpage for details of this function
1482 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1483 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1484 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1485 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1486 * @param stdClass $context The context the page relates to. Not sure what happens
1487 * if you specify something other than system or front page. Defaults to system.
1489 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1490 $this->settings = new stdClass();
1491 $this->name = $name;
1492 $this->visiblename = $visiblename;
1493 if (is_array($req_capability)) {
1494 $this->req_capability = $req_capability;
1495 } else {
1496 $this->req_capability = array($req_capability);
1498 $this->hidden = $hidden;
1499 $this->context = $context;
1503 * Get the URL to view this page.
1505 * @return moodle_url
1507 public function get_settings_page_url(): moodle_url {
1508 return new moodle_url(
1509 '/admin/settings.php',
1511 'section' => $this->name,
1517 * see admin_category
1519 * @param string $name
1520 * @param bool $findpath
1521 * @return mixed Object (this) if name == this->name, else returns null
1523 public function locate($name, $findpath=false) {
1524 if ($this->name == $name) {
1525 if ($findpath) {
1526 $this->visiblepath = array($this->visiblename);
1527 $this->path = array($this->name);
1529 return $this;
1530 } else {
1531 $return = NULL;
1532 return $return;
1537 * Search string in settings page.
1539 * @param string $query
1540 * @return array
1542 public function search($query) {
1543 $found = array();
1545 foreach ($this->settings as $setting) {
1546 if ($setting->is_related($query)) {
1547 $found[] = $setting;
1551 if ($found) {
1552 $result = new stdClass();
1553 $result->page = $this;
1554 $result->settings = $found;
1555 return array($this->name => $result);
1558 $found = false;
1559 if (strpos(strtolower($this->name), $query) !== false) {
1560 $found = true;
1561 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1562 $found = true;
1564 if ($found) {
1565 $result = new stdClass();
1566 $result->page = $this;
1567 $result->settings = array();
1568 return array($this->name => $result);
1569 } else {
1570 return array();
1575 * This function always returns false, required by interface
1577 * @param string $name
1578 * @return bool Always false
1580 public function prune($name) {
1581 return false;
1585 * adds an admin_setting to this admin_settingpage
1587 * 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
1588 * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1590 * @param object $setting is the admin_setting object you want to add
1591 * @return bool true if successful, false if not
1593 public function add($setting) {
1594 if (!($setting instanceof admin_setting)) {
1595 debugging('error - not a setting instance');
1596 return false;
1599 $name = $setting->name;
1600 if ($setting->plugin) {
1601 $name = $setting->plugin . $name;
1603 $this->settings->{$name} = $setting;
1604 return true;
1608 * Hide the named setting if the specified condition is matched.
1610 * @param string $settingname
1611 * @param string $dependenton
1612 * @param string $condition
1613 * @param string $value
1615 public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
1616 $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
1618 // Reformat the dependency name to the plugin | name format used in the display.
1619 $dependenton = str_replace('/', ' | ', $dependenton);
1621 // Let the setting know, so it can be displayed underneath.
1622 $findname = str_replace('/', '', $settingname);
1623 foreach ($this->settings as $name => $setting) {
1624 if ($name === $findname) {
1625 $setting->add_dependent_on($dependenton);
1631 * see admin_externalpage
1633 * @return bool Returns true for yes false for no
1635 public function check_access() {
1636 global $CFG;
1637 $context = empty($this->context) ? context_system::instance() : $this->context;
1638 foreach($this->req_capability as $cap) {
1639 if (has_capability($cap, $context)) {
1640 return true;
1643 return false;
1647 * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1648 * @return string Returns an XHTML string
1650 public function output_html() {
1651 $adminroot = admin_get_root();
1652 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1653 foreach($this->settings as $setting) {
1654 $fullname = $setting->get_full_name();
1655 if (array_key_exists($fullname, $adminroot->errors)) {
1656 $data = $adminroot->errors[$fullname]->data;
1657 } else {
1658 $data = $setting->get_setting();
1659 // do not use defaults if settings not available - upgrade settings handles the defaults!
1661 $return .= $setting->output_html($data);
1663 $return .= '</fieldset>';
1664 return $return;
1668 * Is this settings page hidden in admin tree block?
1670 * @return bool True if hidden
1672 public function is_hidden() {
1673 return $this->hidden;
1677 * Show we display Save button at the page bottom?
1678 * @return bool
1680 public function show_save() {
1681 foreach($this->settings as $setting) {
1682 if (empty($setting->nosave)) {
1683 return true;
1686 return false;
1690 * Should any of the settings on this page be shown / hidden based on conditions?
1691 * @return bool
1693 public function has_dependencies() {
1694 return (bool)$this->dependencies;
1698 * Format the setting show/hide conditions ready to initialise the page javascript
1699 * @return array
1701 public function get_dependencies_for_javascript() {
1702 if (!$this->has_dependencies()) {
1703 return [];
1705 return admin_settingdependency::prepare_for_javascript($this->dependencies);
1711 * Admin settings class. Only exists on setting pages.
1712 * Read & write happens at this level; no authentication.
1714 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1716 abstract class admin_setting {
1717 /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1718 public $name;
1719 /** @var lang_string|string localised name */
1720 public $visiblename;
1721 /** @var string localised long description in Markdown format */
1722 public $description;
1723 /** @var mixed Can be string or array of string */
1724 public $defaultsetting;
1725 /** @var string */
1726 public $updatedcallback;
1727 /** @var mixed can be String or Null. Null means main config table */
1728 public $plugin; // null means main config table
1729 /** @var bool true indicates this setting does not actually save anything, just information */
1730 public $nosave = false;
1731 /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1732 public $affectsmodinfo = false;
1733 /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1734 private $flags = array();
1735 /** @var bool Whether this field must be forced LTR. */
1736 private $forceltr = null;
1737 /** @var array list of other settings that may cause this setting to be hidden */
1738 private $dependenton = [];
1739 /** @var bool Whether this setting uses a custom form control */
1740 protected $customcontrol = false;
1741 /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
1742 public $paramtype;
1745 * Constructor
1746 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1747 * or 'myplugin/mysetting' for ones in config_plugins.
1748 * @param string $visiblename localised name
1749 * @param string $description localised long description
1750 * @param mixed $defaultsetting string or array depending on implementation
1752 public function __construct($name, $visiblename, $description, $defaultsetting) {
1753 $this->parse_setting_name($name);
1754 $this->visiblename = $visiblename;
1755 $this->description = $description;
1756 $this->defaultsetting = $defaultsetting;
1760 * Generic function to add a flag to this admin setting.
1762 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1763 * @param bool $default - The default for the flag
1764 * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1765 * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1767 protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1768 if (empty($this->flags[$shortname])) {
1769 $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1770 } else {
1771 $this->flags[$shortname]->set_options($enabled, $default);
1776 * Set the enabled options flag on this admin setting.
1778 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1779 * @param bool $default - The default for the flag
1781 public function set_enabled_flag_options($enabled, $default) {
1782 $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1786 * Set the advanced options flag on this admin setting.
1788 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1789 * @param bool $default - The default for the flag
1791 public function set_advanced_flag_options($enabled, $default) {
1792 $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1797 * Set the locked options flag on this admin setting.
1799 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1800 * @param bool $default - The default for the flag
1802 public function set_locked_flag_options($enabled, $default) {
1803 $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1807 * Set the required options flag on this admin setting.
1809 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
1810 * @param bool $default - The default for the flag.
1812 public function set_required_flag_options($enabled, $default) {
1813 $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
1817 * Is this option forced in config.php?
1819 * @return bool
1821 public function is_readonly(): bool {
1822 global $CFG;
1824 if (empty($this->plugin)) {
1825 if ($this->is_forceable() && array_key_exists($this->name, $CFG->config_php_settings)) {
1826 return true;
1828 } else {
1829 if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
1830 and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
1831 return true;
1834 return false;
1838 * Get the currently saved value for a setting flag
1840 * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1841 * @return bool
1843 public function get_setting_flag_value(admin_setting_flag $flag) {
1844 $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1845 if (!isset($value)) {
1846 $value = $flag->get_default();
1849 return !empty($value);
1853 * Get the list of defaults for the flags on this setting.
1855 * @param array of strings describing the defaults for this setting. This is appended to by this function.
1857 public function get_setting_flag_defaults(& $defaults) {
1858 foreach ($this->flags as $flag) {
1859 if ($flag->is_enabled() && $flag->get_default()) {
1860 $defaults[] = $flag->get_displayname();
1866 * Output the input fields for the advanced and locked flags on this setting.
1868 * @param bool $adv - The current value of the advanced flag.
1869 * @param bool $locked - The current value of the locked flag.
1870 * @return string $output - The html for the flags.
1872 public function output_setting_flags() {
1873 $output = '';
1875 foreach ($this->flags as $flag) {
1876 if ($flag->is_enabled()) {
1877 $output .= $flag->output_setting_flag($this);
1881 if (!empty($output)) {
1882 return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1884 return $output;
1888 * Write the values of the flags for this admin setting.
1890 * @param array $data - The data submitted from the form or null to set the default value for new installs.
1891 * @return bool - true if successful.
1893 public function write_setting_flags($data) {
1894 $result = true;
1895 foreach ($this->flags as $flag) {
1896 $result = $result && $flag->write_setting_flag($this, $data);
1898 return $result;
1902 * Set up $this->name and potentially $this->plugin
1904 * Set up $this->name and possibly $this->plugin based on whether $name looks
1905 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1906 * on the names, that is, output a developer debug warning if the name
1907 * contains anything other than [a-zA-Z0-9_]+.
1909 * @param string $name the setting name passed in to the constructor.
1911 private function parse_setting_name($name) {
1912 $bits = explode('/', $name);
1913 if (count($bits) > 2) {
1914 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1916 $this->name = array_pop($bits);
1917 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1918 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1920 if (!empty($bits)) {
1921 $this->plugin = array_pop($bits);
1922 if ($this->plugin === 'moodle') {
1923 $this->plugin = null;
1924 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1925 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1931 * Returns the fullname prefixed by the plugin
1932 * @return string
1934 public function get_full_name() {
1935 return 's_'.$this->plugin.'_'.$this->name;
1939 * Returns the ID string based on plugin and name
1940 * @return string
1942 public function get_id() {
1943 return 'id_s_'.$this->plugin.'_'.$this->name;
1947 * @param bool $affectsmodinfo If true, changes to this setting will
1948 * cause the course cache to be rebuilt
1950 public function set_affects_modinfo($affectsmodinfo) {
1951 $this->affectsmodinfo = $affectsmodinfo;
1955 * Returns the config if possible
1957 * @return mixed returns config if successful else null
1959 public function config_read($name) {
1960 global $CFG;
1961 if (!empty($this->plugin)) {
1962 $value = get_config($this->plugin, $name);
1963 return $value === false ? NULL : $value;
1965 } else {
1966 if (isset($CFG->$name)) {
1967 return $CFG->$name;
1968 } else {
1969 return NULL;
1975 * Used to set a config pair and log change
1977 * @param string $name
1978 * @param mixed $value Gets converted to string if not null
1979 * @return bool Write setting to config table
1981 public function config_write($name, $value) {
1982 global $DB, $USER, $CFG;
1984 if ($this->nosave) {
1985 return true;
1988 // make sure it is a real change
1989 $oldvalue = get_config($this->plugin, $name);
1990 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1991 $value = is_null($value) ? null : (string)$value;
1993 if ($oldvalue === $value) {
1994 return true;
1997 // store change
1998 set_config($name, $value, $this->plugin);
2000 // Some admin settings affect course modinfo
2001 if ($this->affectsmodinfo) {
2002 // Clear course cache for all courses
2003 rebuild_course_cache(0, true);
2006 $this->add_to_config_log($name, $oldvalue, $value);
2008 return true; // BC only
2012 * Log config changes if necessary.
2013 * @param string $name
2014 * @param string $oldvalue
2015 * @param string $value
2017 protected function add_to_config_log($name, $oldvalue, $value) {
2018 add_to_config_log($name, $oldvalue, $value, $this->plugin);
2022 * Returns current value of this setting
2023 * @return mixed array or string depending on instance, NULL means not set yet
2025 public abstract function get_setting();
2028 * Returns default setting if exists
2029 * @return mixed array or string depending on instance; NULL means no default, user must supply
2031 public function get_defaultsetting() {
2032 $adminroot = admin_get_root(false, false);
2033 if (!empty($adminroot->custom_defaults)) {
2034 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
2035 if (isset($adminroot->custom_defaults[$plugin])) {
2036 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
2037 return $adminroot->custom_defaults[$plugin][$this->name];
2041 return $this->defaultsetting;
2045 * Store new setting
2047 * @param mixed $data string or array, must not be NULL
2048 * @return string empty string if ok, string error message otherwise
2050 public abstract function write_setting($data);
2053 * Return part of form with setting
2054 * This function should always be overwritten
2056 * @param mixed $data array or string depending on setting
2057 * @param string $query
2058 * @return string
2060 public function output_html($data, $query='') {
2061 // should be overridden
2062 return;
2066 * Function called if setting updated - cleanup, cache reset, etc.
2067 * @param string $functionname Sets the function name
2068 * @return void
2070 public function set_updatedcallback($functionname) {
2071 $this->updatedcallback = $functionname;
2075 * Execute postupdatecallback if necessary.
2076 * @param mixed $original original value before write_setting()
2077 * @return bool true if changed, false if not.
2079 public function post_write_settings($original) {
2080 // Comparison must work for arrays too.
2081 if (serialize($original) === serialize($this->get_setting())) {
2082 return false;
2085 $callbackfunction = $this->updatedcallback;
2086 if (!empty($callbackfunction) and is_callable($callbackfunction)) {
2087 $callbackfunction($this->get_full_name());
2089 return true;
2093 * Is setting related to query text - used when searching
2094 * @param string $query
2095 * @return bool
2097 public function is_related($query) {
2098 if (strpos(strtolower($this->name), $query) !== false) {
2099 return true;
2101 if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
2102 return true;
2104 if (strpos(core_text::strtolower($this->description), $query) !== false) {
2105 return true;
2107 $current = $this->get_setting();
2108 if (!is_null($current)) {
2109 if (is_string($current)) {
2110 if (strpos(core_text::strtolower($current), $query) !== false) {
2111 return true;
2115 $default = $this->get_defaultsetting();
2116 if (!is_null($default)) {
2117 if (is_string($default)) {
2118 if (strpos(core_text::strtolower($default), $query) !== false) {
2119 return true;
2123 return false;
2127 * Get whether this should be displayed in LTR mode.
2129 * @return bool|null
2131 public function get_force_ltr() {
2132 return $this->forceltr;
2136 * Set whether to force LTR or not.
2138 * @param bool $value True when forced, false when not force, null when unknown.
2140 public function set_force_ltr($value) {
2141 $this->forceltr = $value;
2145 * Add a setting to the list of those that could cause this one to be hidden
2146 * @param string $dependenton
2148 public function add_dependent_on($dependenton) {
2149 $this->dependenton[] = $dependenton;
2153 * Get a list of the settings that could cause this one to be hidden.
2154 * @return array
2156 public function get_dependent_on() {
2157 return $this->dependenton;
2161 * Whether this setting uses a custom form control.
2162 * This function is especially useful to decide if we should render a label element for this setting or not.
2164 * @return bool
2166 public function has_custom_form_control(): bool {
2167 return $this->customcontrol;
2171 * Whether the setting can be overridden in config.php.
2173 * Returning true will allow the setting to be defined and overridden in config.php.
2174 * Returning false will prevent the config setting from being overridden even when it gets defined in config.php.
2176 * @return bool
2178 public function is_forceable(): bool {
2179 return true;
2184 * An additional option that can be applied to an admin setting.
2185 * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
2187 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2189 class admin_setting_flag {
2190 /** @var bool Flag to indicate if this option can be toggled for this setting */
2191 private $enabled = false;
2192 /** @var bool Flag to indicate if this option defaults to true or false */
2193 private $default = false;
2194 /** @var string Short string used to create setting name - e.g. 'adv' */
2195 private $shortname = '';
2196 /** @var string String used as the label for this flag */
2197 private $displayname = '';
2198 /** @const Checkbox for this flag is displayed in admin page */
2199 const ENABLED = true;
2200 /** @const Checkbox for this flag is not displayed in admin page */
2201 const DISABLED = false;
2204 * Constructor
2206 * @param bool $enabled Can this option can be toggled.
2207 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2208 * @param bool $default The default checked state for this setting option.
2209 * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
2210 * @param string $displayname The displayname of this flag. Used as a label for the flag.
2212 public function __construct($enabled, $default, $shortname, $displayname) {
2213 $this->shortname = $shortname;
2214 $this->displayname = $displayname;
2215 $this->set_options($enabled, $default);
2219 * Update the values of this setting options class
2221 * @param bool $enabled Can this option can be toggled.
2222 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2223 * @param bool $default The default checked state for this setting option.
2225 public function set_options($enabled, $default) {
2226 $this->enabled = $enabled;
2227 $this->default = $default;
2231 * Should this option appear in the interface and be toggleable?
2233 * @return bool Is it enabled?
2235 public function is_enabled() {
2236 return $this->enabled;
2240 * Should this option be checked by default?
2242 * @return bool Is it on by default?
2244 public function get_default() {
2245 return $this->default;
2249 * Return the short name for this flag. e.g. 'adv' or 'locked'
2251 * @return string
2253 public function get_shortname() {
2254 return $this->shortname;
2258 * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
2260 * @return string
2262 public function get_displayname() {
2263 return $this->displayname;
2267 * Save the submitted data for this flag - or set it to the default if $data is null.
2269 * @param admin_setting $setting - The admin setting for this flag
2270 * @param array $data - The data submitted from the form or null to set the default value for new installs.
2271 * @return bool
2273 public function write_setting_flag(admin_setting $setting, $data) {
2274 $result = true;
2275 if ($this->is_enabled()) {
2276 if (!isset($data)) {
2277 $value = $this->get_default();
2278 } else {
2279 $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
2281 $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
2284 return $result;
2289 * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
2291 * @param admin_setting $setting - The admin setting for this flag
2292 * @return string - The html for the checkbox.
2294 public function output_setting_flag(admin_setting $setting) {
2295 global $OUTPUT;
2297 $value = $setting->get_setting_flag_value($this);
2299 $context = new stdClass();
2300 $context->id = $setting->get_id() . '_' . $this->get_shortname();
2301 $context->name = $setting->get_full_name() . '_' . $this->get_shortname();
2302 $context->value = 1;
2303 $context->checked = $value ? true : false;
2304 $context->label = $this->get_displayname();
2306 return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
2312 * No setting - just heading and text.
2314 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2316 class admin_setting_heading extends admin_setting {
2319 * not a setting, just text
2320 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2321 * @param string $heading heading
2322 * @param string $information text in box
2324 public function __construct($name, $heading, $information) {
2325 $this->nosave = true;
2326 parent::__construct($name, $heading, $information, '');
2330 * Always returns true
2331 * @return bool Always returns true
2333 public function get_setting() {
2334 return true;
2338 * Always returns true
2339 * @return bool Always returns true
2341 public function get_defaultsetting() {
2342 return true;
2346 * Never write settings
2347 * @return string Always returns an empty string
2349 public function write_setting($data) {
2350 // do not write any setting
2351 return '';
2355 * Returns an HTML string
2356 * @return string Returns an HTML string
2358 public function output_html($data, $query='') {
2359 global $OUTPUT;
2360 $context = new stdClass();
2361 $context->title = $this->visiblename;
2362 $context->description = $this->description;
2363 $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
2364 return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
2369 * No setting - just name and description in same row.
2371 * @copyright 2018 onwards Amaia Anabitarte
2372 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2374 class admin_setting_description extends admin_setting {
2377 * Not a setting, just text
2379 * @param string $name
2380 * @param string $visiblename
2381 * @param string $description
2383 public function __construct($name, $visiblename, $description) {
2384 $this->nosave = true;
2385 parent::__construct($name, $visiblename, $description, '');
2389 * Always returns true
2391 * @return bool Always returns true
2393 public function get_setting() {
2394 return true;
2398 * Always returns true
2400 * @return bool Always returns true
2402 public function get_defaultsetting() {
2403 return true;
2407 * Never write settings
2409 * @param mixed $data Gets converted to str for comparison against yes value
2410 * @return string Always returns an empty string
2412 public function write_setting($data) {
2413 // Do not write any setting.
2414 return '';
2418 * Returns an HTML string
2420 * @param string $data
2421 * @param string $query
2422 * @return string Returns an HTML string
2424 public function output_html($data, $query='') {
2425 global $OUTPUT;
2427 $context = new stdClass();
2428 $context->title = $this->visiblename;
2429 $context->description = $this->description;
2431 return $OUTPUT->render_from_template('core_admin/setting_description', $context);
2438 * The most flexible setting, the user enters text.
2440 * This type of field should be used for config settings which are using
2441 * English words and are not localised (passwords, database name, list of values, ...).
2443 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2445 class admin_setting_configtext extends admin_setting {
2447 /** @var int default field size */
2448 public $size;
2451 * Config text constructor
2453 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2454 * @param string $visiblename localised
2455 * @param string $description long localised info
2456 * @param string $defaultsetting
2457 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2458 * @param int $size default field size
2460 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
2461 $this->paramtype = $paramtype;
2462 if (!is_null($size)) {
2463 $this->size = $size;
2464 } else {
2465 $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
2467 parent::__construct($name, $visiblename, $description, $defaultsetting);
2471 * Get whether this should be displayed in LTR mode.
2473 * Try to guess from the PARAM type unless specifically set.
2475 public function get_force_ltr() {
2476 $forceltr = parent::get_force_ltr();
2477 if ($forceltr === null) {
2478 return !is_rtl_compatible($this->paramtype);
2480 return $forceltr;
2484 * Return the setting
2486 * @return mixed returns config if successful else null
2488 public function get_setting() {
2489 return $this->config_read($this->name);
2492 public function write_setting($data) {
2493 if ($this->paramtype === PARAM_INT and $data === '') {
2494 // do not complain if '' used instead of 0
2495 $data = 0;
2497 // $data is a string
2498 $validated = $this->validate($data);
2499 if ($validated !== true) {
2500 return $validated;
2502 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2506 * Validate data before storage
2507 * @param string data
2508 * @return mixed true if ok string if error found
2510 public function validate($data) {
2511 // allow paramtype to be a custom regex if it is the form of /pattern/
2512 if (preg_match('#^/.*/$#', $this->paramtype)) {
2513 if (preg_match($this->paramtype, $data)) {
2514 return true;
2515 } else {
2516 return get_string('validateerror', 'admin');
2519 } else if ($this->paramtype === PARAM_RAW) {
2520 return true;
2522 } else {
2523 $cleaned = clean_param($data, $this->paramtype);
2524 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2525 return true;
2526 } else {
2527 return get_string('validateerror', 'admin');
2533 * Return an XHTML string for the setting
2534 * @return string Returns an XHTML string
2536 public function output_html($data, $query='') {
2537 global $OUTPUT;
2539 $default = $this->get_defaultsetting();
2540 $context = (object) [
2541 'size' => $this->size,
2542 'id' => $this->get_id(),
2543 'name' => $this->get_full_name(),
2544 'value' => $data,
2545 'forceltr' => $this->get_force_ltr(),
2546 'readonly' => $this->is_readonly(),
2548 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
2550 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2555 * Text input with a maximum length constraint.
2557 * @copyright 2015 onwards Ankit Agarwal
2558 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2560 class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
2562 /** @var int maximum number of chars allowed. */
2563 protected $maxlength;
2566 * Config text constructor
2568 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
2569 * or 'myplugin/mysetting' for ones in config_plugins.
2570 * @param string $visiblename localised
2571 * @param string $description long localised info
2572 * @param string $defaultsetting
2573 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2574 * @param int $size default field size
2575 * @param mixed $maxlength int maxlength allowed, 0 for infinite.
2577 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
2578 $size=null, $maxlength = 0) {
2579 $this->maxlength = $maxlength;
2580 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
2584 * Validate data before storage
2586 * @param string $data data
2587 * @return mixed true if ok string if error found
2589 public function validate($data) {
2590 $parentvalidation = parent::validate($data);
2591 if ($parentvalidation === true) {
2592 if ($this->maxlength > 0) {
2593 // Max length check.
2594 $length = core_text::strlen($data);
2595 if ($length > $this->maxlength) {
2596 return get_string('maximumchars', 'moodle', $this->maxlength);
2598 return true;
2599 } else {
2600 return true; // No max length check needed.
2602 } else {
2603 return $parentvalidation;
2609 * General text area without html editor.
2611 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2613 class admin_setting_configtextarea extends admin_setting_configtext {
2614 private $rows;
2615 private $cols;
2618 * @param string $name
2619 * @param string $visiblename
2620 * @param string $description
2621 * @param mixed $defaultsetting string or array
2622 * @param mixed $paramtype
2623 * @param string $cols The number of columns to make the editor
2624 * @param string $rows The number of rows to make the editor
2626 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2627 $this->rows = $rows;
2628 $this->cols = $cols;
2629 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2633 * Returns an XHTML string for the editor
2635 * @param string $data
2636 * @param string $query
2637 * @return string XHTML string for the editor
2639 public function output_html($data, $query='') {
2640 global $OUTPUT;
2642 $default = $this->get_defaultsetting();
2643 $defaultinfo = $default;
2644 if (!is_null($default) and $default !== '') {
2645 $defaultinfo = "\n".$default;
2648 $context = (object) [
2649 'cols' => $this->cols,
2650 'rows' => $this->rows,
2651 'id' => $this->get_id(),
2652 'name' => $this->get_full_name(),
2653 'value' => $data,
2654 'forceltr' => $this->get_force_ltr(),
2655 'readonly' => $this->is_readonly(),
2657 $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
2659 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
2664 * General text area with html editor.
2666 class admin_setting_confightmleditor extends admin_setting_configtextarea {
2669 * @param string $name
2670 * @param string $visiblename
2671 * @param string $description
2672 * @param mixed $defaultsetting string or array
2673 * @param mixed $paramtype
2675 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2676 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
2677 $this->set_force_ltr(false);
2678 editors_head_setup();
2682 * Returns an XHTML string for the editor
2684 * @param string $data
2685 * @param string $query
2686 * @return string XHTML string for the editor
2688 public function output_html($data, $query='') {
2689 $editor = editors_get_preferred_editor(FORMAT_HTML);
2690 $editor->set_text($data);
2691 $editor->use_editor($this->get_id(), array('noclean'=>true));
2692 return parent::output_html($data, $query);
2696 * Checks if data has empty html.
2698 * @param string $data
2699 * @return string Empty when no errors.
2701 public function write_setting($data) {
2702 if (trim(html_to_text($data)) === '') {
2703 $data = '';
2705 return parent::write_setting($data);
2711 * Password field, allows unmasking of password
2713 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2715 class admin_setting_configpasswordunmask extends admin_setting_configtext {
2718 * Constructor
2719 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2720 * @param string $visiblename localised
2721 * @param string $description long localised info
2722 * @param string $defaultsetting default password
2724 public function __construct($name, $visiblename, $description, $defaultsetting) {
2725 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2729 * Log config changes if necessary.
2730 * @param string $name
2731 * @param string $oldvalue
2732 * @param string $value
2734 protected function add_to_config_log($name, $oldvalue, $value) {
2735 if ($value !== '') {
2736 $value = '********';
2738 if ($oldvalue !== '' and $oldvalue !== null) {
2739 $oldvalue = '********';
2741 parent::add_to_config_log($name, $oldvalue, $value);
2745 * Returns HTML for the field.
2747 * @param string $data Value for the field
2748 * @param string $query Passed as final argument for format_admin_setting
2749 * @return string Rendered HTML
2751 public function output_html($data, $query='') {
2752 global $OUTPUT;
2754 $context = (object) [
2755 'id' => $this->get_id(),
2756 'name' => $this->get_full_name(),
2757 'size' => $this->size,
2758 'value' => $this->is_readonly() ? null : $data,
2759 'forceltr' => $this->get_force_ltr(),
2760 'readonly' => $this->is_readonly(),
2762 $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
2763 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
2768 * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
2770 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2771 * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
2773 class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
2776 * Constructor
2778 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2779 * @param string $visiblename localised
2780 * @param string $description long localised info
2781 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
2783 public function __construct($name, $visiblename, $description, $defaultsetting) {
2784 parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
2785 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
2790 * Admin setting class for encrypted values using secure encryption.
2792 * @copyright 2019 The Open University
2793 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2795 class admin_setting_encryptedpassword extends admin_setting {
2798 * Constructor. Same as parent except that the default value is always an empty string.
2800 * @param string $name Internal name used in config table
2801 * @param string $visiblename Name shown on form
2802 * @param string $description Description that appears below field
2804 public function __construct(string $name, string $visiblename, string $description) {
2805 parent::__construct($name, $visiblename, $description, '');
2808 public function get_setting() {
2809 return $this->config_read($this->name);
2812 public function write_setting($data) {
2813 $data = trim($data);
2814 if ($data === '') {
2815 // Value can really be set to nothing.
2816 $savedata = '';
2817 } else {
2818 // Encrypt value before saving it.
2819 $savedata = \core\encryption::encrypt($data);
2821 return ($this->config_write($this->name, $savedata) ? '' : get_string('errorsetting', 'admin'));
2824 public function output_html($data, $query='') {
2825 global $OUTPUT;
2827 $default = $this->get_defaultsetting();
2828 $context = (object) [
2829 'id' => $this->get_id(),
2830 'name' => $this->get_full_name(),
2831 'set' => $data !== '',
2832 'novalue' => $this->get_setting() === null
2834 $element = $OUTPUT->render_from_template('core_admin/setting_encryptedpassword', $context);
2836 return format_admin_setting($this, $this->visiblename, $element, $this->description,
2837 true, '', $default, $query);
2842 * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2843 * Note: Only advanced makes sense right now - locked does not.
2845 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2847 class admin_setting_configempty extends admin_setting_configtext {
2850 * @param string $name
2851 * @param string $visiblename
2852 * @param string $description
2854 public function __construct($name, $visiblename, $description) {
2855 parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2859 * Returns an XHTML string for the hidden field
2861 * @param string $data
2862 * @param string $query
2863 * @return string XHTML string for the editor
2865 public function output_html($data, $query='') {
2866 global $OUTPUT;
2868 $context = (object) [
2869 'id' => $this->get_id(),
2870 'name' => $this->get_full_name()
2872 $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
2874 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
2880 * Path to directory
2882 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2884 class admin_setting_configfile extends admin_setting_configtext {
2886 * Constructor
2887 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2888 * @param string $visiblename localised
2889 * @param string $description long localised info
2890 * @param string $defaultdirectory default directory location
2892 public function __construct($name, $visiblename, $description, $defaultdirectory) {
2893 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2897 * Returns XHTML for the field
2899 * Returns XHTML for the field and also checks whether the file
2900 * specified in $data exists using file_exists()
2902 * @param string $data File name and path to use in value attr
2903 * @param string $query
2904 * @return string XHTML field
2906 public function output_html($data, $query='') {
2907 global $CFG, $OUTPUT;
2909 $default = $this->get_defaultsetting();
2910 $context = (object) [
2911 'id' => $this->get_id(),
2912 'name' => $this->get_full_name(),
2913 'size' => $this->size,
2914 'value' => $data,
2915 'showvalidity' => !empty($data),
2916 'valid' => $data && file_exists($data),
2917 'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(),
2918 'forceltr' => $this->get_force_ltr(),
2921 if ($context->readonly) {
2922 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2925 $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
2927 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2931 * Checks if execpatch has been disabled in config.php
2933 public function write_setting($data) {
2934 global $CFG;
2935 if (!empty($CFG->preventexecpath)) {
2936 if ($this->get_setting() === null) {
2937 // Use default during installation.
2938 $data = $this->get_defaultsetting();
2939 if ($data === null) {
2940 $data = '';
2942 } else {
2943 return '';
2946 return parent::write_setting($data);
2953 * Path to executable file
2955 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2957 class admin_setting_configexecutable extends admin_setting_configfile {
2960 * Returns an XHTML field
2962 * @param string $data This is the value for the field
2963 * @param string $query
2964 * @return string XHTML field
2966 public function output_html($data, $query='') {
2967 global $CFG, $OUTPUT;
2968 $default = $this->get_defaultsetting();
2969 require_once("$CFG->libdir/filelib.php");
2971 $context = (object) [
2972 'id' => $this->get_id(),
2973 'name' => $this->get_full_name(),
2974 'size' => $this->size,
2975 'value' => $data,
2976 'showvalidity' => !empty($data),
2977 'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
2978 'readonly' => !empty($CFG->preventexecpath),
2979 'forceltr' => $this->get_force_ltr()
2982 if (!empty($CFG->preventexecpath)) {
2983 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2986 $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
2988 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2994 * Path to directory
2996 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2998 class admin_setting_configdirectory extends admin_setting_configfile {
3001 * Returns an XHTML field
3003 * @param string $data This is the value for the field
3004 * @param string $query
3005 * @return string XHTML
3007 public function output_html($data, $query='') {
3008 global $CFG, $OUTPUT;
3009 $default = $this->get_defaultsetting();
3011 $context = (object) [
3012 'id' => $this->get_id(),
3013 'name' => $this->get_full_name(),
3014 'size' => $this->size,
3015 'value' => $data,
3016 'showvalidity' => !empty($data),
3017 'valid' => $data && file_exists($data) && is_dir($data),
3018 'readonly' => !empty($CFG->preventexecpath),
3019 'forceltr' => $this->get_force_ltr()
3022 if (!empty($CFG->preventexecpath)) {
3023 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
3026 $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
3028 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
3034 * Checkbox
3036 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3038 class admin_setting_configcheckbox extends admin_setting {
3039 /** @var string Value used when checked */
3040 public $yes;
3041 /** @var string Value used when not checked */
3042 public $no;
3045 * Constructor
3046 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3047 * @param string $visiblename localised
3048 * @param string $description long localised info
3049 * @param string $defaultsetting
3050 * @param string $yes value used when checked
3051 * @param string $no value used when not checked
3053 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
3054 parent::__construct($name, $visiblename, $description, $defaultsetting);
3055 $this->yes = (string)$yes;
3056 $this->no = (string)$no;
3060 * Retrieves the current setting using the objects name
3062 * @return string
3064 public function get_setting() {
3065 return $this->config_read($this->name);
3069 * Sets the value for the setting
3071 * Sets the value for the setting to either the yes or no values
3072 * of the object by comparing $data to yes
3074 * @param mixed $data Gets converted to str for comparison against yes value
3075 * @return string empty string or error
3077 public function write_setting($data) {
3078 if ((string)$data === $this->yes) { // convert to strings before comparison
3079 $data = $this->yes;
3080 } else {
3081 $data = $this->no;
3083 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3087 * Returns an XHTML checkbox field
3089 * @param string $data If $data matches yes then checkbox is checked
3090 * @param string $query
3091 * @return string XHTML field
3093 public function output_html($data, $query='') {
3094 global $OUTPUT;
3096 $context = (object) [
3097 'id' => $this->get_id(),
3098 'name' => $this->get_full_name(),
3099 'no' => $this->no,
3100 'value' => $this->yes,
3101 'checked' => (string) $data === $this->yes,
3102 'readonly' => $this->is_readonly(),
3105 $default = $this->get_defaultsetting();
3106 if (!is_null($default)) {
3107 if ((string)$default === $this->yes) {
3108 $defaultinfo = get_string('checkboxyes', 'admin');
3109 } else {
3110 $defaultinfo = get_string('checkboxno', 'admin');
3112 } else {
3113 $defaultinfo = NULL;
3116 $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
3118 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3124 * Multiple checkboxes, each represents different value, stored in csv format
3126 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3128 class admin_setting_configmulticheckbox extends admin_setting {
3129 /** @var callable|null Loader function for choices */
3130 protected $choiceloader = null;
3132 /** @var array Array of choices value=>label. */
3133 public $choices;
3136 * Constructor: uses parent::__construct
3138 * The $choices parameter may be either an array of $value => $label format,
3139 * e.g. [1 => get_string('yes')], or a callback function which takes no parameters and
3140 * returns an array in that format.
3142 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3143 * @param string $visiblename localised
3144 * @param string $description long localised info
3145 * @param array $defaultsetting array of selected
3146 * @param array|callable $choices array of $value => $label for each checkbox, or a callback
3148 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3149 if (is_array($choices)) {
3150 $this->choices = $choices;
3152 if (is_callable($choices)) {
3153 $this->choiceloader = $choices;
3155 parent::__construct($name, $visiblename, $description, $defaultsetting);
3159 * This function may be used in ancestors for lazy loading of choices
3161 * Override this method if loading of choices is expensive, such
3162 * as when it requires multiple db requests.
3164 * @return bool true if loaded, false if error
3166 public function load_choices() {
3167 if ($this->choiceloader) {
3168 if (!is_array($this->choices)) {
3169 $this->choices = call_user_func($this->choiceloader);
3172 return true;
3176 * Is setting related to query text - used when searching
3178 * @param string $query
3179 * @return bool true on related, false on not or failure
3181 public function is_related($query) {
3182 if (!$this->load_choices() or empty($this->choices)) {
3183 return false;
3185 if (parent::is_related($query)) {
3186 return true;
3189 foreach ($this->choices as $desc) {
3190 if (strpos(core_text::strtolower($desc), $query) !== false) {
3191 return true;
3194 return false;
3198 * Returns the current setting if it is set
3200 * @return mixed null if null, else an array
3202 public function get_setting() {
3203 $result = $this->config_read($this->name);
3205 if (is_null($result)) {
3206 return NULL;
3208 if ($result === '') {
3209 return array();
3211 $enabled = explode(',', $result);
3212 $setting = array();
3213 foreach ($enabled as $option) {
3214 $setting[$option] = 1;
3216 return $setting;
3220 * Saves the setting(s) provided in $data
3222 * @param array $data An array of data, if not array returns empty str
3223 * @return mixed empty string on useless data or bool true=success, false=failed
3225 public function write_setting($data) {
3226 if (!is_array($data)) {
3227 return ''; // ignore it
3229 if (!$this->load_choices() or empty($this->choices)) {
3230 return '';
3232 unset($data['xxxxx']);
3233 $result = array();
3234 foreach ($data as $key => $value) {
3235 if ($value and array_key_exists($key, $this->choices)) {
3236 $result[] = $key;
3239 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
3243 * Returns XHTML field(s) as required by choices
3245 * Relies on data being an array should data ever be another valid vartype with
3246 * acceptable value this may cause a warning/error
3247 * if (!is_array($data)) would fix the problem
3249 * @todo Add vartype handling to ensure $data is an array
3251 * @param array $data An array of checked values
3252 * @param string $query
3253 * @return string XHTML field
3255 public function output_html($data, $query='') {
3256 global $OUTPUT;
3258 if (!$this->load_choices() or empty($this->choices)) {
3259 return '';
3262 $default = $this->get_defaultsetting();
3263 if (is_null($default)) {
3264 $default = array();
3266 if (is_null($data)) {
3267 $data = array();
3270 $context = (object) [
3271 'id' => $this->get_id(),
3272 'name' => $this->get_full_name(),
3273 'readonly' => $this->is_readonly(),
3276 $options = array();
3277 $defaults = array();
3278 foreach ($this->choices as $key => $description) {
3279 if (!empty($default[$key])) {
3280 $defaults[] = $description;
3283 $options[] = [
3284 'key' => $key,
3285 'checked' => !empty($data[$key]),
3286 'label' => highlightfast($query, $description)
3290 if (is_null($default)) {
3291 $defaultinfo = null;
3292 } else if (!empty($defaults)) {
3293 $defaultinfo = implode(', ', $defaults);
3294 } else {
3295 $defaultinfo = get_string('none');
3298 $context->options = $options;
3299 $context->hasoptions = !empty($options);
3301 $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
3303 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
3310 * Multiple checkboxes 2, value stored as string 00101011
3312 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3314 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
3317 * Returns the setting if set
3319 * @return mixed null if not set, else an array of set settings
3321 public function get_setting() {
3322 $result = $this->config_read($this->name);
3323 if (is_null($result)) {
3324 return NULL;
3326 if (!$this->load_choices()) {
3327 return NULL;
3329 $result = str_pad($result, count($this->choices), '0');
3330 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
3331 $setting = array();
3332 foreach ($this->choices as $key=>$unused) {
3333 $value = array_shift($result);
3334 if ($value) {
3335 $setting[$key] = 1;
3338 return $setting;
3342 * Save setting(s) provided in $data param
3344 * @param array $data An array of settings to save
3345 * @return mixed empty string for bad data or bool true=>success, false=>error
3347 public function write_setting($data) {
3348 if (!is_array($data)) {
3349 return ''; // ignore it
3351 if (!$this->load_choices() or empty($this->choices)) {
3352 return '';
3354 $result = '';
3355 foreach ($this->choices as $key=>$unused) {
3356 if (!empty($data[$key])) {
3357 $result .= '1';
3358 } else {
3359 $result .= '0';
3362 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
3368 * Select one value from list
3370 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3372 class admin_setting_configselect extends admin_setting {
3373 /** @var array Array of choices value=>label */
3374 public $choices;
3375 /** @var array Array of choices grouped using optgroups */
3376 public $optgroups;
3377 /** @var callable|null Loader function for choices */
3378 protected $choiceloader = null;
3379 /** @var callable|null Validation function */
3380 protected $validatefunction = null;
3383 * Constructor.
3385 * If you want to lazy-load the choices, pass a callback function that returns a choice
3386 * array for the $choices parameter.
3388 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3389 * @param string $visiblename localised
3390 * @param string $description long localised info
3391 * @param string|int $defaultsetting
3392 * @param array|callable|null $choices array of $value=>$label for each selection, or callback
3394 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3395 // Look for optgroup and single options.
3396 if (is_array($choices)) {
3397 $this->choices = [];
3398 foreach ($choices as $key => $val) {
3399 if (is_array($val)) {
3400 $this->optgroups[$key] = $val;
3401 $this->choices = array_merge($this->choices, $val);
3402 } else {
3403 $this->choices[$key] = $val;
3407 if (is_callable($choices)) {
3408 $this->choiceloader = $choices;
3411 parent::__construct($name, $visiblename, $description, $defaultsetting);
3415 * Sets a validate function.
3417 * The callback will be passed one parameter, the new setting value, and should return either
3418 * an empty string '' if the value is OK, or an error message if not.
3420 * @param callable|null $validatefunction Validate function or null to clear
3421 * @since Moodle 3.10
3423 public function set_validate_function(?callable $validatefunction = null) {
3424 $this->validatefunction = $validatefunction;
3428 * This function may be used in ancestors for lazy loading of choices
3430 * Override this method if loading of choices is expensive, such
3431 * as when it requires multiple db requests.
3433 * @return bool true if loaded, false if error
3435 public function load_choices() {
3436 if ($this->choiceloader) {
3437 if (!is_array($this->choices)) {
3438 $this->choices = call_user_func($this->choiceloader);
3440 return true;
3442 return true;
3446 * Check if this is $query is related to a choice
3448 * @param string $query
3449 * @return bool true if related, false if not
3451 public function is_related($query) {
3452 if (parent::is_related($query)) {
3453 return true;
3455 if (!$this->load_choices()) {
3456 return false;
3458 foreach ($this->choices as $key=>$value) {
3459 if (strpos(core_text::strtolower($key), $query) !== false) {
3460 return true;
3462 if (strpos(core_text::strtolower($value), $query) !== false) {
3463 return true;
3466 return false;
3470 * Return the setting
3472 * @return mixed returns config if successful else null
3474 public function get_setting() {
3475 return $this->config_read($this->name);
3479 * Save a setting
3481 * @param string $data
3482 * @return string empty of error string
3484 public function write_setting($data) {
3485 if (!$this->load_choices() or empty($this->choices)) {
3486 return '';
3488 if (!array_key_exists($data, $this->choices)) {
3489 return ''; // ignore it
3492 // Validate the new setting.
3493 $error = $this->validate_setting($data);
3494 if ($error) {
3495 return $error;
3498 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3502 * Validate the setting. This uses the callback function if provided; subclasses could override
3503 * to carry out validation directly in the class.
3505 * @param string $data New value being set
3506 * @return string Empty string if valid, or error message text
3507 * @since Moodle 3.10
3509 protected function validate_setting(string $data): string {
3510 // If validation function is specified, call it now.
3511 if ($this->validatefunction) {
3512 return call_user_func($this->validatefunction, $data);
3513 } else {
3514 return '';
3519 * Returns XHTML select field
3521 * Ensure the options are loaded, and generate the XHTML for the select
3522 * element and any warning message. Separating this out from output_html
3523 * makes it easier to subclass this class.
3525 * @param string $data the option to show as selected.
3526 * @param string $current the currently selected option in the database, null if none.
3527 * @param string $default the default selected option.
3528 * @return array the HTML for the select element, and a warning message.
3529 * @deprecated since Moodle 3.2
3531 public function output_select_html($data, $current, $default, $extraname = '') {
3532 debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
3536 * Returns XHTML select field and wrapping div(s)
3538 * @see output_select_html()
3540 * @param string $data the option to show as selected
3541 * @param string $query
3542 * @return string XHTML field and wrapping div
3544 public function output_html($data, $query='') {
3545 global $OUTPUT;
3547 $default = $this->get_defaultsetting();
3548 $current = $this->get_setting();
3550 if (!$this->load_choices() || empty($this->choices)) {
3551 return '';
3554 $context = (object) [
3555 'id' => $this->get_id(),
3556 'name' => $this->get_full_name(),
3559 if (!is_null($default) && array_key_exists($default, $this->choices)) {
3560 $defaultinfo = $this->choices[$default];
3561 } else {
3562 $defaultinfo = NULL;
3565 // Warnings.
3566 $warning = '';
3567 if ($current === null) {
3568 // First run.
3569 } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
3570 // No warning.
3571 } else if (!array_key_exists($current, $this->choices)) {
3572 $warning = get_string('warningcurrentsetting', 'admin', $current);
3573 if (!is_null($default) && $data == $current) {
3574 $data = $default; // Use default instead of first value when showing the form.
3578 $options = [];
3579 $template = 'core_admin/setting_configselect';
3581 if (!empty($this->optgroups)) {
3582 $optgroups = [];
3583 foreach ($this->optgroups as $label => $choices) {
3584 $optgroup = array('label' => $label, 'options' => []);
3585 foreach ($choices as $value => $name) {
3586 $optgroup['options'][] = [
3587 'value' => $value,
3588 'name' => $name,
3589 'selected' => (string) $value == $data
3591 unset($this->choices[$value]);
3593 $optgroups[] = $optgroup;
3595 $context->options = $options;
3596 $context->optgroups = $optgroups;
3597 $template = 'core_admin/setting_configselect_optgroup';
3600 foreach ($this->choices as $value => $name) {
3601 $options[] = [
3602 'value' => $value,
3603 'name' => $name,
3604 'selected' => (string) $value == $data
3607 $context->options = $options;
3608 $context->readonly = $this->is_readonly();
3610 $element = $OUTPUT->render_from_template($template, $context);
3612 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
3617 * Select multiple items from list
3619 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3621 class admin_setting_configmultiselect extends admin_setting_configselect {
3623 * Constructor
3624 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3625 * @param string $visiblename localised
3626 * @param string $description long localised info
3627 * @param array $defaultsetting array of selected items
3628 * @param array $choices array of $value=>$label for each list item
3630 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3631 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
3635 * Returns the select setting(s)
3637 * @return mixed null or array. Null if no settings else array of setting(s)
3639 public function get_setting() {
3640 $result = $this->config_read($this->name);
3641 if (is_null($result)) {
3642 return NULL;
3644 if ($result === '') {
3645 return array();
3647 return explode(',', $result);
3651 * Saves setting(s) provided through $data
3653 * Potential bug in the works should anyone call with this function
3654 * using a vartype that is not an array
3656 * @param array $data
3658 public function write_setting($data) {
3659 if (!is_array($data)) {
3660 return ''; //ignore it
3662 if (!$this->load_choices() or empty($this->choices)) {
3663 return '';
3666 unset($data['xxxxx']);
3668 $save = array();
3669 foreach ($data as $value) {
3670 if (!array_key_exists($value, $this->choices)) {
3671 continue; // ignore it
3673 $save[] = $value;
3676 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3680 * Is setting related to query text - used when searching
3682 * @param string $query
3683 * @return bool true if related, false if not
3685 public function is_related($query) {
3686 if (!$this->load_choices() or empty($this->choices)) {
3687 return false;
3689 if (parent::is_related($query)) {
3690 return true;
3693 foreach ($this->choices as $desc) {
3694 if (strpos(core_text::strtolower($desc), $query) !== false) {
3695 return true;
3698 return false;
3702 * Returns XHTML multi-select field
3704 * @todo Add vartype handling to ensure $data is an array
3705 * @param array $data Array of values to select by default
3706 * @param string $query
3707 * @return string XHTML multi-select field
3709 public function output_html($data, $query='') {
3710 global $OUTPUT;
3712 if (!$this->load_choices() or empty($this->choices)) {
3713 return '';
3716 $default = $this->get_defaultsetting();
3717 if (is_null($default)) {
3718 $default = array();
3720 if (is_null($data)) {
3721 $data = array();
3724 $context = (object) [
3725 'id' => $this->get_id(),
3726 'name' => $this->get_full_name(),
3727 'size' => min(10, count($this->choices))
3730 $defaults = [];
3731 $options = [];
3732 $template = 'core_admin/setting_configmultiselect';
3734 if (!empty($this->optgroups)) {
3735 $optgroups = [];
3736 foreach ($this->optgroups as $label => $choices) {
3737 $optgroup = array('label' => $label, 'options' => []);
3738 foreach ($choices as $value => $name) {
3739 if (in_array($value, $default)) {
3740 $defaults[] = $name;
3742 $optgroup['options'][] = [
3743 'value' => $value,
3744 'name' => $name,
3745 'selected' => in_array($value, $data)
3747 unset($this->choices[$value]);
3749 $optgroups[] = $optgroup;
3751 $context->optgroups = $optgroups;
3752 $template = 'core_admin/setting_configmultiselect_optgroup';
3755 foreach ($this->choices as $value => $name) {
3756 if (in_array($value, $default)) {
3757 $defaults[] = $name;
3759 $options[] = [
3760 'value' => $value,
3761 'name' => $name,
3762 'selected' => in_array($value, $data)
3765 $context->options = $options;
3766 $context->readonly = $this->is_readonly();
3768 if (is_null($default)) {
3769 $defaultinfo = NULL;
3770 } if (!empty($defaults)) {
3771 $defaultinfo = implode(', ', $defaults);
3772 } else {
3773 $defaultinfo = get_string('none');
3776 $element = $OUTPUT->render_from_template($template, $context);
3778 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3783 * Time selector
3785 * This is a liiitle bit messy. we're using two selects, but we're returning
3786 * them as an array named after $name (so we only use $name2 internally for the setting)
3788 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3790 class admin_setting_configtime extends admin_setting {
3791 /** @var string Used for setting second select (minutes) */
3792 public $name2;
3795 * Constructor
3796 * @param string $hoursname setting for hours
3797 * @param string $minutesname setting for hours
3798 * @param string $visiblename localised
3799 * @param string $description long localised info
3800 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3802 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3803 $this->name2 = $minutesname;
3804 parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3808 * Get the selected time
3810 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3812 public function get_setting() {
3813 $result1 = $this->config_read($this->name);
3814 $result2 = $this->config_read($this->name2);
3815 if (is_null($result1) or is_null($result2)) {
3816 return NULL;
3819 return array('h' => $result1, 'm' => $result2);
3823 * Store the time (hours and minutes)
3825 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3826 * @return bool true if success, false if not
3828 public function write_setting($data) {
3829 if (!is_array($data)) {
3830 return '';
3833 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3834 return ($result ? '' : get_string('errorsetting', 'admin'));
3838 * Returns XHTML time select fields
3840 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3841 * @param string $query
3842 * @return string XHTML time select fields and wrapping div(s)
3844 public function output_html($data, $query='') {
3845 global $OUTPUT;
3847 $default = $this->get_defaultsetting();
3848 if (is_array($default)) {
3849 $defaultinfo = $default['h'].':'.$default['m'];
3850 } else {
3851 $defaultinfo = NULL;
3854 $context = (object) [
3855 'id' => $this->get_id(),
3856 'name' => $this->get_full_name(),
3857 'readonly' => $this->is_readonly(),
3858 'hours' => array_map(function($i) use ($data) {
3859 return [
3860 'value' => $i,
3861 'name' => $i,
3862 'selected' => $i == $data['h']
3864 }, range(0, 23)),
3865 'minutes' => array_map(function($i) use ($data) {
3866 return [
3867 'value' => $i,
3868 'name' => $i,
3869 'selected' => $i == $data['m']
3871 }, range(0, 59, 5))
3874 $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
3876 return format_admin_setting($this, $this->visiblename, $element, $this->description,
3877 $this->get_id() . 'h', '', $defaultinfo, $query);
3884 * Seconds duration setting.
3886 * @copyright 2012 Petr Skoda (http://skodak.org)
3887 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3889 class admin_setting_configduration extends admin_setting {
3891 /** @var int default duration unit */
3892 protected $defaultunit;
3893 /** @var callable|null Validation function */
3894 protected $validatefunction = null;
3896 /** @var int The minimum allowed value */
3897 protected int $minduration = 0;
3899 /** @var null|int The maximum allowed value */
3900 protected null|int $maxduration = null;
3903 * Constructor
3904 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3905 * or 'myplugin/mysetting' for ones in config_plugins.
3906 * @param string $visiblename localised name
3907 * @param string $description localised long description
3908 * @param mixed $defaultsetting string or array depending on implementation
3909 * @param int $defaultunit - day, week, etc. (in seconds)
3911 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3912 if (is_number($defaultsetting)) {
3913 $defaultsetting = self::parse_seconds($defaultsetting);
3915 $units = self::get_units();
3916 if (isset($units[$defaultunit])) {
3917 $this->defaultunit = $defaultunit;
3918 } else {
3919 $this->defaultunit = 86400;
3921 parent::__construct($name, $visiblename, $description, $defaultsetting);
3925 * Set the minimum allowed value.
3926 * This must be at least 0.
3928 * @param int $duration
3930 public function set_min_duration(int $duration): void {
3931 if ($duration < 0) {
3932 throw new coding_exception('The minimum duration must be at least 0.');
3935 $this->minduration = $duration;
3939 * Set the maximum allowed value.
3941 * A value of null will disable the maximum duration value.
3943 * @param int|null $duration
3945 public function set_max_duration(?int $duration): void {
3946 $this->maxduration = $duration;
3950 * Sets a validate function.
3952 * The callback will be passed one parameter, the new setting value, and should return either
3953 * an empty string '' if the value is OK, or an error message if not.
3955 * @param callable|null $validatefunction Validate function or null to clear
3956 * @since Moodle 3.10
3958 public function set_validate_function(?callable $validatefunction = null) {
3959 $this->validatefunction = $validatefunction;
3963 * Validate the setting. This uses the callback function if provided; subclasses could override
3964 * to carry out validation directly in the class.
3966 * @param int $data New value being set
3967 * @return string Empty string if valid, or error message text
3968 * @since Moodle 3.10
3970 protected function validate_setting(int $data): string {
3971 if ($data < $this->minduration) {
3972 return get_string(
3973 'configduration_low',
3974 'admin',
3975 self::get_duration_text($this->minduration, get_string('numseconds', 'core', 0))
3979 if ($this->maxduration && $data > $this->maxduration) {
3980 return get_string('configduration_high', 'admin', self::get_duration_text($this->maxduration));
3983 // If validation function is specified, call it now.
3984 if ($this->validatefunction) {
3985 return call_user_func($this->validatefunction, $data);
3987 return '';
3991 * Returns selectable units.
3992 * @static
3993 * @return array
3995 protected static function get_units() {
3996 return array(
3997 604800 => get_string('weeks'),
3998 86400 => get_string('days'),
3999 3600 => get_string('hours'),
4000 60 => get_string('minutes'),
4001 1 => get_string('seconds'),
4006 * Converts seconds to some more user friendly string.
4007 * @static
4008 * @param int $seconds
4009 * @param null|string The value to use when the duration is empty. If not specified, a "None" value is used.
4010 * @return string
4012 protected static function get_duration_text(int $seconds, ?string $emptyvalue = null): string {
4013 if (empty($seconds)) {
4014 if ($emptyvalue !== null) {
4015 return $emptyvalue;
4017 return get_string('none');
4019 $data = self::parse_seconds($seconds);
4020 switch ($data['u']) {
4021 case (60*60*24*7):
4022 return get_string('numweeks', '', $data['v']);
4023 case (60*60*24):
4024 return get_string('numdays', '', $data['v']);
4025 case (60*60):
4026 return get_string('numhours', '', $data['v']);
4027 case (60):
4028 return get_string('numminutes', '', $data['v']);
4029 default:
4030 return get_string('numseconds', '', $data['v']*$data['u']);
4035 * Finds suitable units for given duration.
4036 * @static
4037 * @param int $seconds
4038 * @return array
4040 protected static function parse_seconds($seconds) {
4041 foreach (self::get_units() as $unit => $unused) {
4042 if ($seconds % $unit === 0) {
4043 return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
4046 return array('v'=>(int)$seconds, 'u'=>1);
4050 * Get the selected duration as array.
4052 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
4054 public function get_setting() {
4055 $seconds = $this->config_read($this->name);
4056 if (is_null($seconds)) {
4057 return null;
4060 return self::parse_seconds($seconds);
4064 * Store the duration as seconds.
4066 * @param array $data Must be form 'h'=>xx, 'm'=>xx
4067 * @return bool true if success, false if not
4069 public function write_setting($data) {
4070 if (!is_array($data)) {
4071 return '';
4074 $unit = (int)$data['u'];
4075 $value = (int)$data['v'];
4076 $seconds = $value * $unit;
4078 // Validate the new setting.
4079 $error = $this->validate_setting($seconds);
4080 if ($error) {
4081 return $error;
4084 $result = $this->config_write($this->name, $seconds);
4085 return ($result ? '' : get_string('errorsetting', 'admin'));
4089 * Returns duration text+select fields.
4091 * @param array $data Must be form 'v'=>xx, 'u'=>xx
4092 * @param string $query
4093 * @return string duration text+select fields and wrapping div(s)
4095 public function output_html($data, $query='') {
4096 global $OUTPUT;
4098 $default = $this->get_defaultsetting();
4099 if (is_number($default)) {
4100 $defaultinfo = self::get_duration_text($default);
4101 } else if (is_array($default)) {
4102 $defaultinfo = self::get_duration_text($default['v']*$default['u']);
4103 } else {
4104 $defaultinfo = null;
4107 $inputid = $this->get_id() . 'v';
4108 $units = array_filter(self::get_units(), function($unit): bool {
4109 if (!$this->maxduration) {
4110 // No duration limit. All units are valid.
4111 return true;
4114 return $unit <= $this->maxduration;
4115 }, ARRAY_FILTER_USE_KEY);
4117 $defaultunit = $this->defaultunit;
4119 $context = (object) [
4120 'id' => $this->get_id(),
4121 'name' => $this->get_full_name(),
4122 'value' => $data['v'] ?? '',
4123 'readonly' => $this->is_readonly(),
4124 'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
4125 return [
4126 'value' => $unit,
4127 'name' => $units[$unit],
4128 'selected' => isset($data) && (($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u'])
4130 }, array_keys($units))
4133 $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
4135 return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
4141 * Seconds duration setting with an advanced checkbox, that controls a additional
4142 * $name.'_adv' setting.
4144 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4145 * @copyright 2014 The Open University
4147 class admin_setting_configduration_with_advanced extends admin_setting_configduration {
4149 * Constructor
4150 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
4151 * or 'myplugin/mysetting' for ones in config_plugins.
4152 * @param string $visiblename localised name
4153 * @param string $description localised long description
4154 * @param array $defaultsetting array of int value, and bool whether it is
4155 * is advanced by default.
4156 * @param int $defaultunit - day, week, etc. (in seconds)
4158 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
4159 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
4160 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
4166 * Used to validate a textarea used for ip addresses
4168 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4169 * @copyright 2011 Petr Skoda (http://skodak.org)
4171 class admin_setting_configiplist extends admin_setting_configtextarea {
4174 * Validate the contents of the textarea as IP addresses
4176 * Used to validate a new line separated list of IP addresses collected from
4177 * a textarea control
4179 * @param string $data A list of IP Addresses separated by new lines
4180 * @return mixed bool true for success or string:error on failure
4182 public function validate($data) {
4183 if(!empty($data)) {
4184 $lines = explode("\n", $data);
4185 } else {
4186 return true;
4188 $result = true;
4189 $badips = array();
4190 foreach ($lines as $line) {
4191 $tokens = explode('#', $line);
4192 $ip = trim($tokens[0]);
4193 if (empty($ip)) {
4194 continue;
4196 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
4197 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
4198 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
4199 } else {
4200 $result = false;
4201 $badips[] = $ip;
4204 if($result) {
4205 return true;
4206 } else {
4207 return get_string('validateiperror', 'admin', join(', ', $badips));
4213 * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
4215 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4216 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4218 class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
4221 * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
4222 * Used to validate a new line separated list of entries collected from a textarea control.
4224 * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
4225 * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
4226 * via the get_setting() method, which has been overriden.
4228 * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
4229 * @return mixed bool true for success or string:error on failure
4231 public function validate($data) {
4232 if (empty($data)) {
4233 return true;
4235 $entries = explode("\n", $data);
4236 $badentries = [];
4238 foreach ($entries as $key => $entry) {
4239 $entry = trim($entry);
4240 if (empty($entry)) {
4241 return get_string('validateemptylineerror', 'admin');
4244 // Validate each string entry against the supported formats.
4245 if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
4246 || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
4247 || \core\ip_utils::is_domain_matching_pattern($entry)) {
4248 continue;
4251 // Otherwise, the entry is invalid.
4252 $badentries[] = $entry;
4255 if ($badentries) {
4256 return get_string('validateerrorlist', 'admin', join(', ', $badentries));
4258 return true;
4262 * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
4264 * @param string $data the setting data, as sent from the web form.
4265 * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
4267 protected function ace_encode($data) {
4268 if (empty($data)) {
4269 return $data;
4271 $entries = explode("\n", $data);
4272 foreach ($entries as $key => $entry) {
4273 $entry = trim($entry);
4274 // This regex matches any string that has non-ascii character.
4275 if (preg_match('/[^\x00-\x7f]/', $entry)) {
4276 // If we can convert the unicode string to an idn, do so.
4277 // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
4278 $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4279 $entries[$key] = $val ? $val : $entry;
4282 return implode("\n", $entries);
4286 * Decode any ascii-encoded domain names back to their utf-8 representation for display.
4288 * @param string $data the setting data, as found in the database.
4289 * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
4291 protected function ace_decode($data) {
4292 $entries = explode("\n", $data);
4293 foreach ($entries as $key => $entry) {
4294 $entry = trim($entry);
4295 if (strpos($entry, 'xn--') !== false) {
4296 $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4299 return implode("\n", $entries);
4303 * Override, providing utf8-decoding for ascii-encoded IDN strings.
4305 * @return mixed returns punycode-converted setting string if successful, else null.
4307 public function get_setting() {
4308 // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
4309 $data = $this->config_read($this->name);
4310 if (function_exists('idn_to_utf8') && !is_null($data)) {
4311 $data = $this->ace_decode($data);
4313 return $data;
4317 * Override, providing ascii-encoding for utf8 (native) IDN strings.
4319 * @param string $data
4320 * @return string
4322 public function write_setting($data) {
4323 if ($this->paramtype === PARAM_INT and $data === '') {
4324 // Do not complain if '' used instead of 0.
4325 $data = 0;
4328 // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
4329 if (function_exists('idn_to_ascii')) {
4330 $data = $this->ace_encode($data);
4333 $validated = $this->validate($data);
4334 if ($validated !== true) {
4335 return $validated;
4337 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4342 * Used to validate a textarea used for port numbers.
4344 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4345 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4347 class admin_setting_configportlist extends admin_setting_configtextarea {
4350 * Validate the contents of the textarea as port numbers.
4351 * Used to validate a new line separated list of ports collected from a textarea control.
4353 * @param string $data A list of ports separated by new lines
4354 * @return mixed bool true for success or string:error on failure
4356 public function validate($data) {
4357 if (empty($data)) {
4358 return true;
4360 $ports = explode("\n", $data);
4361 $badentries = [];
4362 foreach ($ports as $port) {
4363 $port = trim($port);
4364 if (empty($port)) {
4365 return get_string('validateemptylineerror', 'admin');
4368 // Is the string a valid integer number?
4369 if (strval(intval($port)) !== $port || intval($port) <= 0) {
4370 $badentries[] = $port;
4373 if ($badentries) {
4374 return get_string('validateerrorlist', 'admin', $badentries);
4376 return true;
4382 * An admin setting for selecting one or more users who have a capability
4383 * in the system context
4385 * An admin setting for selecting one or more users, who have a particular capability
4386 * in the system context. Warning, make sure the list will never be too long. There is
4387 * no paging or searching of this list.
4389 * To correctly get a list of users from this config setting, you need to call the
4390 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
4392 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4394 class admin_setting_users_with_capability extends admin_setting_configmultiselect {
4395 /** @var string The capabilities name */
4396 protected $capability;
4397 /** @var int include admin users too */
4398 protected $includeadmins;
4401 * Constructor.
4403 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4404 * @param string $visiblename localised name
4405 * @param string $description localised long description
4406 * @param array $defaultsetting array of usernames
4407 * @param string $capability string capability name.
4408 * @param bool $includeadmins include administrators
4410 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
4411 $this->capability = $capability;
4412 $this->includeadmins = $includeadmins;
4413 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4417 * Load all of the uses who have the capability into choice array
4419 * @return bool Always returns true
4421 function load_choices() {
4422 if (is_array($this->choices)) {
4423 return true;
4425 list($sort, $sortparams) = users_order_by_sql('u');
4426 if (!empty($sortparams)) {
4427 throw new coding_exception('users_order_by_sql returned some query parameters. ' .
4428 'This is unexpected, and a problem because there is no way to pass these ' .
4429 'parameters to get_users_by_capability. See MDL-34657.');
4431 $userfieldsapi = \core_user\fields::for_name();
4432 $userfields = 'u.id, u.username, ' . $userfieldsapi->get_sql('u', false, '', '', false)->selects;
4433 $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
4434 $this->choices = array(
4435 '$@NONE@$' => get_string('nobody'),
4436 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4438 if ($this->includeadmins) {
4439 $admins = get_admins();
4440 foreach ($admins as $user) {
4441 $this->choices[$user->id] = fullname($user);
4444 if (is_array($users)) {
4445 foreach ($users as $user) {
4446 $this->choices[$user->id] = fullname($user);
4449 return true;
4453 * Returns the default setting for class
4455 * @return mixed Array, or string. Empty string if no default
4457 public function get_defaultsetting() {
4458 $this->load_choices();
4459 $defaultsetting = parent::get_defaultsetting();
4460 if (empty($defaultsetting)) {
4461 return array('$@NONE@$');
4462 } else if (array_key_exists($defaultsetting, $this->choices)) {
4463 return $defaultsetting;
4464 } else {
4465 return '';
4470 * Returns the current setting
4472 * @return mixed array or string
4474 public function get_setting() {
4475 $result = parent::get_setting();
4476 if ($result === null) {
4477 // this is necessary for settings upgrade
4478 return null;
4480 if (empty($result)) {
4481 $result = array('$@NONE@$');
4483 return $result;
4487 * Save the chosen setting provided as $data
4489 * @param array $data
4490 * @return mixed string or array
4492 public function write_setting($data) {
4493 // If all is selected, remove any explicit options.
4494 if (in_array('$@ALL@$', $data)) {
4495 $data = array('$@ALL@$');
4497 // None never needs to be written to the DB.
4498 if (in_array('$@NONE@$', $data)) {
4499 unset($data[array_search('$@NONE@$', $data)]);
4501 return parent::write_setting($data);
4507 * Special checkbox for calendar - resets SESSION vars.
4509 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4511 class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
4513 * Calls the parent::__construct with default values
4515 * name => calendar_adminseesall
4516 * visiblename => get_string('adminseesall', 'admin')
4517 * description => get_string('helpadminseesall', 'admin')
4518 * defaultsetting => 0
4520 public function __construct() {
4521 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
4522 get_string('helpadminseesall', 'admin'), '0');
4526 * Stores the setting passed in $data
4528 * @param mixed gets converted to string for comparison
4529 * @return string empty string or error message
4531 public function write_setting($data) {
4532 global $SESSION;
4533 return parent::write_setting($data);
4538 * Special select for settings that are altered in setup.php and can not be altered on the fly
4540 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4542 class admin_setting_special_selectsetup extends admin_setting_configselect {
4544 * Reads the setting directly from the database
4546 * @return mixed
4548 public function get_setting() {
4549 // read directly from db!
4550 return get_config(NULL, $this->name);
4554 * Save the setting passed in $data
4556 * @param string $data The setting to save
4557 * @return string empty or error message
4559 public function write_setting($data) {
4560 global $CFG;
4561 // do not change active CFG setting!
4562 $current = $CFG->{$this->name};
4563 $result = parent::write_setting($data);
4564 $CFG->{$this->name} = $current;
4565 return $result;
4571 * Special select for frontpage - stores data in course table
4573 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4575 class admin_setting_sitesetselect extends admin_setting_configselect {
4577 * Returns the site name for the selected site
4579 * @see get_site()
4580 * @return string The site name of the selected site
4582 public function get_setting() {
4583 $site = course_get_format(get_site())->get_course();
4584 return $site->{$this->name};
4588 * Updates the database and save the setting
4590 * @param string data
4591 * @return string empty or error message
4593 public function write_setting($data) {
4594 global $DB, $SITE, $COURSE;
4595 if (!in_array($data, array_keys($this->choices))) {
4596 return get_string('errorsetting', 'admin');
4598 $record = new stdClass();
4599 $record->id = SITEID;
4600 $temp = $this->name;
4601 $record->$temp = $data;
4602 $record->timemodified = time();
4604 course_get_format($SITE)->update_course_format_options($record);
4605 $DB->update_record('course', $record);
4607 // Reset caches.
4608 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4609 if ($SITE->id == $COURSE->id) {
4610 $COURSE = $SITE;
4612 core_courseformat\base::reset_course_cache($SITE->id);
4614 return '';
4619 * admin_setting_sitesetselect is not meant to be overridden in config.php.
4621 * @return bool
4623 public function is_forceable(): bool {
4624 return false;
4630 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
4631 * block to hidden.
4633 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4635 class admin_setting_bloglevel extends admin_setting_configselect {
4637 * Updates the database and save the setting
4639 * @param string data
4640 * @return string empty or error message
4642 public function write_setting($data) {
4643 global $DB, $CFG;
4644 if ($data == 0) {
4645 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
4646 foreach ($blogblocks as $block) {
4647 $DB->set_field('block', 'visible', 0, array('id' => $block->id));
4649 } else {
4650 // reenable all blocks only when switching from disabled blogs
4651 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
4652 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
4653 foreach ($blogblocks as $block) {
4654 $DB->set_field('block', 'visible', 1, array('id' => $block->id));
4658 return parent::write_setting($data);
4664 * Special select - lists on the frontpage - hacky
4666 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4668 class admin_setting_courselist_frontpage extends admin_setting {
4670 /** @var array Array of choices value=>label. */
4671 public $choices;
4674 * Construct override, requires one param
4676 * @param bool $loggedin Is the user logged in
4678 public function __construct($loggedin) {
4679 global $CFG;
4680 require_once($CFG->dirroot.'/course/lib.php');
4681 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
4682 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
4683 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
4684 $defaults = array(FRONTPAGEALLCOURSELIST);
4685 parent::__construct($name, $visiblename, $description, $defaults);
4689 * Loads the choices available
4691 * @return bool always returns true
4693 public function load_choices() {
4694 if (is_array($this->choices)) {
4695 return true;
4697 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
4698 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
4699 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
4700 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
4701 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
4702 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
4703 'none' => get_string('none'));
4704 if ($this->name === 'frontpage') {
4705 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
4707 return true;
4711 * Returns the selected settings
4713 * @param mixed array or setting or null
4715 public function get_setting() {
4716 $result = $this->config_read($this->name);
4717 if (is_null($result)) {
4718 return NULL;
4720 if ($result === '') {
4721 return array();
4723 return explode(',', $result);
4727 * Save the selected options
4729 * @param array $data
4730 * @return mixed empty string (data is not an array) or bool true=success false=failure
4732 public function write_setting($data) {
4733 if (!is_array($data)) {
4734 return '';
4736 $this->load_choices();
4737 $save = array();
4738 foreach($data as $datum) {
4739 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
4740 continue;
4742 $save[$datum] = $datum; // no duplicates
4744 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
4748 * Return XHTML select field and wrapping div
4750 * @todo Add vartype handling to make sure $data is an array
4751 * @param array $data Array of elements to select by default
4752 * @return string XHTML select field and wrapping div
4754 public function output_html($data, $query='') {
4755 global $OUTPUT;
4757 $this->load_choices();
4758 $currentsetting = array();
4759 foreach ($data as $key) {
4760 if ($key != 'none' and array_key_exists($key, $this->choices)) {
4761 $currentsetting[] = $key; // already selected first
4765 $context = (object) [
4766 'id' => $this->get_id(),
4767 'name' => $this->get_full_name(),
4770 $options = $this->choices;
4771 $selects = [];
4772 for ($i = 0; $i < count($this->choices) - 1; $i++) {
4773 if (!array_key_exists($i, $currentsetting)) {
4774 $currentsetting[$i] = 'none';
4776 $selects[] = [
4777 'key' => $i,
4778 'options' => array_map(function($option) use ($options, $currentsetting, $i) {
4779 return [
4780 'name' => $options[$option],
4781 'value' => $option,
4782 'selected' => $currentsetting[$i] == $option
4784 }, array_keys($options))
4787 $context->selects = $selects;
4789 $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
4791 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
4797 * Special checkbox for frontpage - stores data in course table
4799 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4801 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
4803 * Returns the current sites name
4805 * @return string
4807 public function get_setting() {
4808 $site = course_get_format(get_site())->get_course();
4809 return $site->{$this->name};
4813 * Save the selected setting
4815 * @param string $data The selected site
4816 * @return string empty string or error message
4818 public function write_setting($data) {
4819 global $DB, $SITE, $COURSE;
4820 $record = new stdClass();
4821 $record->id = $SITE->id;
4822 $record->{$this->name} = ($data == '1' ? 1 : 0);
4823 $record->timemodified = time();
4825 course_get_format($SITE)->update_course_format_options($record);
4826 $DB->update_record('course', $record);
4828 // Reset caches.
4829 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4830 if ($SITE->id == $COURSE->id) {
4831 $COURSE = $SITE;
4833 core_courseformat\base::reset_course_cache($SITE->id);
4835 return '';
4839 * admin_setting_sitesetcheckbox is not meant to be overridden in config.php.
4841 * @return bool
4843 public function is_forceable(): bool {
4844 return false;
4849 * Special text for frontpage - stores data in course table.
4850 * Empty string means not set here. Manual setting is required.
4852 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4854 class admin_setting_sitesettext extends admin_setting_configtext {
4857 * Constructor.
4859 public function __construct() {
4860 call_user_func_array([parent::class, '__construct'], func_get_args());
4861 $this->set_force_ltr(false);
4865 * Return the current setting
4867 * @return mixed string or null
4869 public function get_setting() {
4870 $site = course_get_format(get_site())->get_course();
4871 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
4875 * Validate the selected data
4877 * @param string $data The selected value to validate
4878 * @return mixed true or message string
4880 public function validate($data) {
4881 global $DB, $SITE;
4882 $cleaned = clean_param($data, PARAM_TEXT);
4883 if ($cleaned === '') {
4884 return get_string('required');
4886 if ($this->name ==='shortname' &&
4887 $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
4888 return get_string('shortnametaken', 'error', $data);
4890 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
4891 return true;
4892 } else {
4893 return get_string('validateerror', 'admin');
4898 * Save the selected setting
4900 * @param string $data The selected value
4901 * @return string empty or error message
4903 public function write_setting($data) {
4904 global $DB, $SITE, $COURSE;
4905 $data = trim($data);
4906 $validated = $this->validate($data);
4907 if ($validated !== true) {
4908 return $validated;
4911 $record = new stdClass();
4912 $record->id = $SITE->id;
4913 $record->{$this->name} = $data;
4914 $record->timemodified = time();
4916 course_get_format($SITE)->update_course_format_options($record);
4917 $DB->update_record('course', $record);
4919 // Reset caches.
4920 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4921 if ($SITE->id == $COURSE->id) {
4922 $COURSE = $SITE;
4924 core_courseformat\base::reset_course_cache($SITE->id);
4926 return '';
4930 * admin_setting_sitesettext is not meant to be overridden in config.php.
4932 * @return bool
4934 public function is_forceable(): bool {
4935 return false;
4941 * This type of field should be used for mandatory config settings.
4943 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4945 class admin_setting_requiredtext extends admin_setting_configtext {
4948 * Validate data before storage.
4950 * @param string $data The string to be validated.
4951 * @return bool|string true for success or error string if invalid.
4953 public function validate($data) {
4954 $cleaned = clean_param($data, PARAM_TEXT);
4955 if ($cleaned === '') {
4956 return get_string('required');
4959 return parent::validate($data);
4964 * Special text editor for site description.
4966 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4968 class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
4971 * Calls parent::__construct with specific arguments
4973 public function __construct() {
4974 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
4975 PARAM_RAW, 60, 15);
4979 * Return the current setting
4980 * @return string The current setting
4982 public function get_setting() {
4983 $site = course_get_format(get_site())->get_course();
4984 return $site->{$this->name};
4988 * Save the new setting
4990 * @param string $data The new value to save
4991 * @return string empty or error message
4993 public function write_setting($data) {
4994 global $DB, $SITE, $COURSE;
4995 $record = new stdClass();
4996 $record->id = $SITE->id;
4997 $record->{$this->name} = $data;
4998 $record->timemodified = time();
5000 course_get_format($SITE)->update_course_format_options($record);
5001 $DB->update_record('course', $record);
5003 // Reset caches.
5004 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
5005 if ($SITE->id == $COURSE->id) {
5006 $COURSE = $SITE;
5008 core_courseformat\base::reset_course_cache($SITE->id);
5010 return '';
5014 * admin_setting_special_frontpagedesc is not meant to be overridden in config.php.
5016 * @return bool
5018 public function is_forceable(): bool {
5019 return false;
5025 * Administration interface for emoticon_manager settings.
5027 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5029 class admin_setting_emoticons extends admin_setting {
5032 * Calls parent::__construct with specific args
5034 public function __construct() {
5035 global $CFG;
5037 $manager = get_emoticon_manager();
5038 $defaults = $this->prepare_form_data($manager->default_emoticons());
5039 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
5043 * Return the current setting(s)
5045 * @return array Current settings array
5047 public function get_setting() {
5048 global $CFG;
5050 $manager = get_emoticon_manager();
5052 $config = $this->config_read($this->name);
5053 if (is_null($config)) {
5054 return null;
5057 $config = $manager->decode_stored_config($config);
5058 if (is_null($config)) {
5059 return null;
5062 return $this->prepare_form_data($config);
5066 * Save selected settings
5068 * @param array $data Array of settings to save
5069 * @return bool
5071 public function write_setting($data) {
5073 $manager = get_emoticon_manager();
5074 $emoticons = $this->process_form_data($data);
5076 if ($emoticons === false) {
5077 return false;
5080 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
5081 return ''; // success
5082 } else {
5083 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
5088 * Return XHTML field(s) for options
5090 * @param array $data Array of options to set in HTML
5091 * @return string XHTML string for the fields and wrapping div(s)
5093 public function output_html($data, $query='') {
5094 global $OUTPUT;
5096 $context = (object) [
5097 'name' => $this->get_full_name(),
5098 'emoticons' => [],
5099 'forceltr' => true,
5102 $i = 0;
5103 foreach ($data as $field => $value) {
5105 // When $i == 0: text.
5106 // When $i == 1: imagename.
5107 // When $i == 2: imagecomponent.
5108 // When $i == 3: altidentifier.
5109 // When $i == 4: altcomponent.
5110 $fields[$i] = (object) [
5111 'field' => $field,
5112 'value' => $value,
5113 'index' => $i
5115 $i++;
5117 if ($i > 4) {
5118 $icon = null;
5119 if (!empty($fields[1]->value)) {
5120 if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
5121 $alt = get_string($fields[3]->value, $fields[4]->value);
5122 } else {
5123 $alt = $fields[0]->value;
5125 $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
5127 $context->emoticons[] = [
5128 'fields' => $fields,
5129 'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
5131 $fields = [];
5132 $i = 0;
5136 $context->reseturl = new moodle_url('/admin/resetemoticons.php');
5137 $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
5138 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5142 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
5144 * @see self::process_form_data()
5145 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
5146 * @return array of form fields and their values
5148 protected function prepare_form_data(array $emoticons) {
5150 $form = array();
5151 $i = 0;
5152 foreach ($emoticons as $emoticon) {
5153 $form['text'.$i] = $emoticon->text;
5154 $form['imagename'.$i] = $emoticon->imagename;
5155 $form['imagecomponent'.$i] = $emoticon->imagecomponent;
5156 $form['altidentifier'.$i] = $emoticon->altidentifier;
5157 $form['altcomponent'.$i] = $emoticon->altcomponent;
5158 $i++;
5160 // add one more blank field set for new object
5161 $form['text'.$i] = '';
5162 $form['imagename'.$i] = '';
5163 $form['imagecomponent'.$i] = '';
5164 $form['altidentifier'.$i] = '';
5165 $form['altcomponent'.$i] = '';
5167 return $form;
5171 * Converts the data from admin settings form into an array of emoticon objects
5173 * @see self::prepare_form_data()
5174 * @param array $data array of admin form fields and values
5175 * @return false|array of emoticon objects
5177 protected function process_form_data(array $form) {
5179 $count = count($form); // number of form field values
5181 if ($count % 5) {
5182 // we must get five fields per emoticon object
5183 return false;
5186 $emoticons = array();
5187 for ($i = 0; $i < $count / 5; $i++) {
5188 $emoticon = new stdClass();
5189 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
5190 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
5191 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
5192 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
5193 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
5195 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
5196 // prevent from breaking http://url.addresses by accident
5197 $emoticon->text = '';
5200 if (strlen($emoticon->text) < 2) {
5201 // do not allow single character emoticons
5202 $emoticon->text = '';
5205 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
5206 // emoticon text must contain some non-alphanumeric character to prevent
5207 // breaking HTML tags
5208 $emoticon->text = '';
5211 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
5212 $emoticons[] = $emoticon;
5215 return $emoticons;
5222 * Special setting for limiting of the list of available languages.
5224 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5226 class admin_setting_langlist extends admin_setting_configtext {
5228 * Calls parent::__construct with specific arguments
5230 public function __construct() {
5231 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
5235 * Validate that each language identifier exists on the site
5237 * @param string $data
5238 * @return bool|string True if validation successful, otherwise error string
5240 public function validate($data) {
5241 $parentcheck = parent::validate($data);
5242 if ($parentcheck !== true) {
5243 return $parentcheck;
5246 if ($data === '') {
5247 return true;
5250 // Normalize language identifiers.
5251 $langcodes = array_map('trim', explode(',', $data));
5252 foreach ($langcodes as $langcode) {
5253 // If the langcode contains optional alias, split it out.
5254 [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2);
5256 if (!get_string_manager()->translation_exists($langcode)) {
5257 return get_string('invalidlanguagecode', 'error', $langcode);
5261 return true;
5265 * Save the new setting
5267 * @param string $data The new setting
5268 * @return bool
5270 public function write_setting($data) {
5271 $return = parent::write_setting($data);
5272 get_string_manager()->reset_caches();
5273 return $return;
5279 * Allows to specify comma separated list of known country codes.
5281 * This is a simple subclass of the plain input text field with added validation so that all the codes are actually
5282 * known codes.
5284 * @package core
5285 * @category admin
5286 * @copyright 2020 David Mudrák <david@moodle.com>
5287 * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5289 class admin_setting_countrycodes extends admin_setting_configtext {
5292 * Construct the instance of the setting.
5294 * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'.
5295 * @param lang_string|string $visiblename Language string with the field label text.
5296 * @param lang_string|string $description Language string with the field description text.
5297 * @param string $defaultsetting Default value of the setting.
5298 * @param int $size Input text field size.
5300 public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) {
5301 parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size);
5305 * Validate the setting value before storing it.
5307 * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or
5308 * a comma separated list of such words.
5310 * @param string $data Value inserted into the setting field.
5311 * @return bool|string True if the value is OK, error string otherwise.
5313 public function validate($data) {
5315 $parentcheck = parent::validate($data);
5317 if ($parentcheck !== true) {
5318 return $parentcheck;
5321 if ($data === '') {
5322 return true;
5325 $allcountries = get_string_manager()->get_list_of_countries(true);
5327 foreach (explode(',', $data) as $code) {
5328 if (!isset($allcountries[$code])) {
5329 return get_string('invalidcountrycode', 'core_error', $code);
5333 return true;
5339 * Selection of one of the recognised countries using the list
5340 * returned by {@link get_list_of_countries()}.
5342 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5344 class admin_settings_country_select extends admin_setting_configselect {
5345 protected $includeall;
5346 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
5347 $this->includeall = $includeall;
5348 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
5352 * Lazy-load the available choices for the select box
5354 public function load_choices() {
5355 global $CFG;
5356 if (is_array($this->choices)) {
5357 return true;
5359 $this->choices = array_merge(
5360 array('0' => get_string('choosedots')),
5361 get_string_manager()->get_list_of_countries($this->includeall));
5362 return true;
5368 * admin_setting_configselect for the default number of sections in a course,
5369 * simply so we can lazy-load the choices.
5371 * @copyright 2011 The Open University
5372 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5374 class admin_settings_num_course_sections extends admin_setting_configselect {
5375 public function __construct($name, $visiblename, $description, $defaultsetting) {
5376 parent::__construct($name, $visiblename, $description, $defaultsetting, array());
5379 /** Lazy-load the available choices for the select box */
5380 public function load_choices() {
5381 $max = get_config('moodlecourse', 'maxsections');
5382 if (!isset($max) || !is_numeric($max)) {
5383 $max = 52;
5385 for ($i = 0; $i <= $max; $i++) {
5386 $this->choices[$i] = "$i";
5388 return true;
5394 * Course category selection
5396 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5398 class admin_settings_coursecat_select extends admin_setting_configselect_autocomplete {
5400 * Calls parent::__construct with specific arguments
5402 public function __construct($name, $visiblename, $description, $defaultsetting = 1) {
5403 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices = null);
5407 * Load the available choices for the select box
5409 * @return bool
5411 public function load_choices() {
5412 if (is_array($this->choices)) {
5413 return true;
5415 $this->choices = core_course_category::make_categories_list('', 0, ' / ');
5416 return true;
5422 * Special control for selecting days to backup
5424 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5426 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
5428 * Calls parent::__construct with specific arguments
5430 public function __construct() {
5431 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
5432 $this->plugin = 'backup';
5436 * Load the available choices for the select box
5438 * @return bool Always returns true
5440 public function load_choices() {
5441 if (is_array($this->choices)) {
5442 return true;
5444 $this->choices = array();
5445 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5446 foreach ($days as $day) {
5447 $this->choices[$day] = get_string($day, 'calendar');
5449 return true;
5454 * Special setting for backup auto destination.
5456 * @package core
5457 * @subpackage admin
5458 * @copyright 2014 Frédéric Massart - FMCorz.net
5459 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5461 class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
5464 * Calls parent::__construct with specific arguments.
5466 public function __construct() {
5467 parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
5471 * Check if the directory must be set, depending on backup/backup_auto_storage.
5473 * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
5474 * there will be conflicts if this validation happens before the other one.
5476 * @param string $data Form data.
5477 * @return string Empty when no errors.
5479 public function write_setting($data) {
5480 $storage = (int) get_config('backup', 'backup_auto_storage');
5481 if ($storage !== 0) {
5482 if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
5483 // The directory must exist and be writable.
5484 return get_string('backuperrorinvaliddestination');
5487 return parent::write_setting($data);
5493 * Special debug setting
5495 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5497 class admin_setting_special_debug extends admin_setting_configselect {
5499 * Calls parent::__construct with specific arguments
5501 public function __construct() {
5502 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
5506 * Load the available choices for the select box
5508 * @return bool
5510 public function load_choices() {
5511 if (is_array($this->choices)) {
5512 return true;
5514 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
5515 DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
5516 DEBUG_NORMAL => get_string('debugnormal', 'admin'),
5517 DEBUG_ALL => get_string('debugall', 'admin'),
5518 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
5519 return true;
5525 * Special admin control
5527 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5529 class admin_setting_special_calendar_weekend extends admin_setting {
5531 * Calls parent::__construct with specific arguments
5533 public function __construct() {
5534 $name = 'calendar_weekend';
5535 $visiblename = get_string('calendar_weekend', 'admin');
5536 $description = get_string('helpweekenddays', 'admin');
5537 $default = array ('0', '6'); // Saturdays and Sundays
5538 parent::__construct($name, $visiblename, $description, $default);
5542 * Gets the current settings as an array
5544 * @return mixed Null if none, else array of settings
5546 public function get_setting() {
5547 $result = $this->config_read($this->name);
5548 if (is_null($result)) {
5549 return NULL;
5551 if ($result === '') {
5552 return array();
5554 $settings = array();
5555 for ($i=0; $i<7; $i++) {
5556 if ($result & (1 << $i)) {
5557 $settings[] = $i;
5560 return $settings;
5564 * Save the new settings
5566 * @param array $data Array of new settings
5567 * @return bool
5569 public function write_setting($data) {
5570 if (!is_array($data)) {
5571 return '';
5573 unset($data['xxxxx']);
5574 $result = 0;
5575 foreach($data as $index) {
5576 $result |= 1 << $index;
5578 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
5582 * Return XHTML to display the control
5584 * @param array $data array of selected days
5585 * @param string $query
5586 * @return string XHTML for display (field + wrapping div(s)
5588 public function output_html($data, $query='') {
5589 global $OUTPUT;
5591 // The order matters very much because of the implied numeric keys.
5592 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5593 $context = (object) [
5594 'name' => $this->get_full_name(),
5595 'id' => $this->get_id(),
5596 'days' => array_map(function($index) use ($days, $data) {
5597 return [
5598 'index' => $index,
5599 'label' => get_string($days[$index], 'calendar'),
5600 'checked' => in_array($index, $data)
5602 }, array_keys($days))
5605 $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
5607 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5614 * Admin setting that allows a user to pick a behaviour.
5616 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5618 class admin_setting_question_behaviour extends admin_setting_configselect {
5620 * @param string $name name of config variable
5621 * @param string $visiblename display name
5622 * @param string $description description
5623 * @param string $default default.
5625 public function __construct($name, $visiblename, $description, $default) {
5626 parent::__construct($name, $visiblename, $description, $default, null);
5630 * Load list of behaviours as choices
5631 * @return bool true => success, false => error.
5633 public function load_choices() {
5634 global $CFG;
5635 require_once($CFG->dirroot . '/question/engine/lib.php');
5636 $this->choices = question_engine::get_behaviour_options('');
5637 return true;
5643 * Admin setting that allows a user to pick appropriate roles for something.
5645 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5647 class admin_setting_pickroles extends admin_setting_configmulticheckbox {
5648 /** @var array Array of capabilities which identify roles */
5649 private $types;
5652 * @param string $name Name of config variable
5653 * @param string $visiblename Display name
5654 * @param string $description Description
5655 * @param array $types Array of archetypes which identify
5656 * roles that will be enabled by default.
5658 public function __construct($name, $visiblename, $description, $types) {
5659 parent::__construct($name, $visiblename, $description, NULL, NULL);
5660 $this->types = $types;
5664 * Load roles as choices
5666 * @return bool true=>success, false=>error
5668 public function load_choices() {
5669 global $CFG, $DB;
5670 if (during_initial_install()) {
5671 return false;
5673 if (is_array($this->choices)) {
5674 return true;
5676 if ($roles = get_all_roles()) {
5677 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
5678 return true;
5679 } else {
5680 return false;
5685 * Return the default setting for this control
5687 * @return array Array of default settings
5689 public function get_defaultsetting() {
5690 global $CFG;
5692 if (during_initial_install()) {
5693 return null;
5695 $result = array();
5696 foreach($this->types as $archetype) {
5697 if ($caproles = get_archetype_roles($archetype)) {
5698 foreach ($caproles as $caprole) {
5699 $result[$caprole->id] = 1;
5703 return $result;
5709 * Admin setting that is a list of installed filter plugins.
5711 * @copyright 2015 The Open University
5712 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5714 class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
5717 * Constructor
5719 * @param string $name unique ascii name, either 'mysetting' for settings
5720 * that in config, or 'myplugin/mysetting' for ones in config_plugins.
5721 * @param string $visiblename localised name
5722 * @param string $description localised long description
5723 * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
5725 public function __construct($name, $visiblename, $description, $default) {
5726 if (empty($default)) {
5727 $default = array();
5729 $this->load_choices();
5730 foreach ($default as $plugin) {
5731 if (!isset($this->choices[$plugin])) {
5732 unset($default[$plugin]);
5735 parent::__construct($name, $visiblename, $description, $default, null);
5738 public function load_choices() {
5739 if (is_array($this->choices)) {
5740 return true;
5742 $this->choices = array();
5744 foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
5745 $this->choices[$plugin] = filter_get_name($plugin);
5747 return true;
5753 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
5755 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5757 class admin_setting_configtext_with_advanced extends admin_setting_configtext {
5759 * Constructor
5760 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5761 * @param string $visiblename localised
5762 * @param string $description long localised info
5763 * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
5764 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
5765 * @param int $size default field size
5767 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
5768 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
5769 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5775 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
5777 * @copyright 2009 Petr Skoda (http://skodak.org)
5778 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5780 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
5783 * Constructor
5784 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5785 * @param string $visiblename localised
5786 * @param string $description long localised info
5787 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
5788 * @param string $yes value used when checked
5789 * @param string $no value used when not checked
5791 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5792 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5793 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5800 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
5802 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
5804 * @copyright 2010 Sam Hemelryk
5805 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5807 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
5809 * Constructor
5810 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5811 * @param string $visiblename localised
5812 * @param string $description long localised info
5813 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5814 * @param string $yes value used when checked
5815 * @param string $no value used when not checked
5817 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5818 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5819 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5825 * Autocomplete as you type form element.
5827 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5829 class admin_setting_configselect_autocomplete extends admin_setting_configselect {
5830 /** @var boolean $tags Should we allow typing new entries to the field? */
5831 protected $tags = false;
5832 /** @var string $ajax Name of an AMD module to send/process ajax requests. */
5833 protected $ajax = '';
5834 /** @var string $placeholder Placeholder text for an empty list. */
5835 protected $placeholder = '';
5836 /** @var bool $casesensitive Whether the search has to be case-sensitive. */
5837 protected $casesensitive = false;
5838 /** @var bool $showsuggestions Show suggestions by default - but this can be turned off. */
5839 protected $showsuggestions = true;
5840 /** @var string $noselectionstring String that is shown when there are no selections. */
5841 protected $noselectionstring = '';
5844 * Returns XHTML select field and wrapping div(s)
5846 * @see output_select_html()
5848 * @param string $data the option to show as selected
5849 * @param string $query
5850 * @return string XHTML field and wrapping div
5852 public function output_html($data, $query='') {
5853 global $PAGE;
5855 $html = parent::output_html($data, $query);
5857 if ($html === '') {
5858 return $html;
5861 $this->placeholder = get_string('search');
5863 $params = array('#' . $this->get_id(), $this->tags, $this->ajax,
5864 $this->placeholder, $this->casesensitive, $this->showsuggestions, $this->noselectionstring);
5866 // Load autocomplete wrapper for select2 library.
5867 $PAGE->requires->js_call_amd('core/form-autocomplete', 'enhance', $params);
5869 return $html;
5874 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
5876 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5878 class admin_setting_configselect_with_advanced extends admin_setting_configselect {
5880 * Calls parent::__construct with specific arguments
5882 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5883 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5884 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5890 * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
5892 * @copyright 2017 Marina Glancy
5893 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5895 class admin_setting_configselect_with_lock extends admin_setting_configselect {
5897 * Constructor
5898 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
5899 * or 'myplugin/mysetting' for ones in config_plugins.
5900 * @param string $visiblename localised
5901 * @param string $description long localised info
5902 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5903 * @param array $choices array of $value=>$label for each selection
5905 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5906 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5907 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5913 * Graded roles in gradebook
5915 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5917 class admin_setting_special_gradebookroles extends admin_setting_pickroles {
5919 * Calls parent::__construct with specific arguments
5921 public function __construct() {
5922 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
5923 get_string('configgradebookroles', 'admin'),
5924 array('student'));
5931 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5933 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
5935 * Saves the new settings passed in $data
5937 * @param string $data
5938 * @return mixed string or Array
5940 public function write_setting($data) {
5941 global $CFG, $DB;
5943 $oldvalue = $this->config_read($this->name);
5944 $return = parent::write_setting($data);
5945 $newvalue = $this->config_read($this->name);
5947 if ($oldvalue !== $newvalue) {
5948 // force full regrading
5949 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
5952 return $return;
5958 * Which roles to show on course description page
5960 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5962 class admin_setting_special_coursecontact extends admin_setting_pickroles {
5964 * Calls parent::__construct with specific arguments
5966 public function __construct() {
5967 parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
5968 get_string('coursecontact_desc', 'admin'),
5969 array('editingteacher'));
5970 $this->set_updatedcallback(function (){
5971 cache::make('core', 'coursecontacts')->purge();
5979 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5981 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
5983 * Calls parent::__construct with specific arguments
5985 public function __construct() {
5986 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
5987 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
5991 * Old syntax of class constructor. Deprecated in PHP7.
5993 * @deprecated since Moodle 3.1
5995 public function admin_setting_special_gradelimiting() {
5996 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
5997 self::__construct();
6001 * Force site regrading
6003 function regrade_all() {
6004 global $CFG;
6005 require_once("$CFG->libdir/gradelib.php");
6006 grade_force_site_regrading();
6010 * Saves the new settings
6012 * @param mixed $data
6013 * @return string empty string or error message
6015 function write_setting($data) {
6016 $previous = $this->get_setting();
6018 if ($previous === null) {
6019 if ($data) {
6020 $this->regrade_all();
6022 } else {
6023 if ($data != $previous) {
6024 $this->regrade_all();
6027 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
6033 * Special setting for $CFG->grade_minmaxtouse.
6035 * @package core
6036 * @copyright 2015 Frédéric Massart - FMCorz.net
6037 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6039 class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
6042 * Constructor.
6044 public function __construct() {
6045 parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
6046 new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
6047 array(
6048 GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
6049 GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
6055 * Saves the new setting.
6057 * @param mixed $data
6058 * @return string empty string or error message
6060 function write_setting($data) {
6061 global $CFG;
6063 $previous = $this->get_setting();
6064 $result = parent::write_setting($data);
6066 // If saved and the value has changed.
6067 if (empty($result) && $previous != $data) {
6068 require_once($CFG->libdir . '/gradelib.php');
6069 grade_force_site_regrading();
6072 return $result;
6079 * Primary grade export plugin - has state tracking.
6081 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6083 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
6085 * Calls parent::__construct with specific arguments
6087 public function __construct() {
6088 parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
6089 get_string('configgradeexport', 'admin'), array(), NULL);
6093 * Load the available choices for the multicheckbox
6095 * @return bool always returns true
6097 public function load_choices() {
6098 if (is_array($this->choices)) {
6099 return true;
6101 $this->choices = array();
6103 if ($plugins = core_component::get_plugin_list('gradeexport')) {
6104 foreach($plugins as $plugin => $unused) {
6105 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
6108 return true;
6114 * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
6116 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6118 class admin_setting_special_gradepointdefault extends admin_setting_configtext {
6120 * Config gradepointmax constructor
6122 * @param string $name Overidden by "gradepointmax"
6123 * @param string $visiblename Overridden by "gradepointmax" language string.
6124 * @param string $description Overridden by "gradepointmax_help" language string.
6125 * @param string $defaultsetting Not used, overridden by 100.
6126 * @param mixed $paramtype Overridden by PARAM_INT.
6127 * @param int $size Overridden by 5.
6129 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
6130 $name = 'gradepointdefault';
6131 $visiblename = get_string('gradepointdefault', 'grades');
6132 $description = get_string('gradepointdefault_help', 'grades');
6133 $defaultsetting = 100;
6134 $paramtype = PARAM_INT;
6135 $size = 5;
6136 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
6140 * Validate data before storage
6141 * @param string $data The submitted data
6142 * @return bool|string true if ok, string if error found
6144 public function validate($data) {
6145 global $CFG;
6146 if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
6147 return true;
6148 } else {
6149 return get_string('gradepointdefault_validateerror', 'grades');
6156 * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
6158 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6160 class admin_setting_special_gradepointmax extends admin_setting_configtext {
6163 * Config gradepointmax constructor
6165 * @param string $name Overidden by "gradepointmax"
6166 * @param string $visiblename Overridden by "gradepointmax" language string.
6167 * @param string $description Overridden by "gradepointmax_help" language string.
6168 * @param string $defaultsetting Not used, overridden by 100.
6169 * @param mixed $paramtype Overridden by PARAM_INT.
6170 * @param int $size Overridden by 5.
6172 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
6173 $name = 'gradepointmax';
6174 $visiblename = get_string('gradepointmax', 'grades');
6175 $description = get_string('gradepointmax_help', 'grades');
6176 $defaultsetting = 100;
6177 $paramtype = PARAM_INT;
6178 $size = 5;
6179 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
6183 * Save the selected setting
6185 * @param string $data The selected site
6186 * @return string empty string or error message
6188 public function write_setting($data) {
6189 if ($data === '') {
6190 $data = (int)$this->defaultsetting;
6191 } else {
6192 $data = $data;
6194 return parent::write_setting($data);
6198 * Validate data before storage
6199 * @param string $data The submitted data
6200 * @return bool|string true if ok, string if error found
6202 public function validate($data) {
6203 if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
6204 return true;
6205 } else {
6206 return get_string('gradepointmax_validateerror', 'grades');
6211 * Return an XHTML string for the setting
6212 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6213 * @param string $query search query to be highlighted
6214 * @return string XHTML to display control
6216 public function output_html($data, $query = '') {
6217 global $OUTPUT;
6219 $default = $this->get_defaultsetting();
6220 $context = (object) [
6221 'size' => $this->size,
6222 'id' => $this->get_id(),
6223 'name' => $this->get_full_name(),
6224 'value' => $data,
6225 'attributes' => [
6226 'maxlength' => 5
6228 'forceltr' => $this->get_force_ltr()
6230 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
6232 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
6238 * Grade category settings
6240 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6242 class admin_setting_gradecat_combo extends admin_setting {
6244 /** @var array Array of choices value=>label. */
6245 public $choices;
6248 * Sets choices and calls parent::__construct with passed arguments
6249 * @param string $name
6250 * @param string $visiblename
6251 * @param string $description
6252 * @param mixed $defaultsetting string or array depending on implementation
6253 * @param array $choices An array of choices for the control
6255 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
6256 $this->choices = $choices;
6257 parent::__construct($name, $visiblename, $description, $defaultsetting);
6261 * Return the current setting(s) array
6263 * @return array Array of value=>xx, forced=>xx, adv=>xx
6265 public function get_setting() {
6266 global $CFG;
6268 $value = $this->config_read($this->name);
6269 $flag = $this->config_read($this->name.'_flag');
6271 if (is_null($value) or is_null($flag)) {
6272 return NULL;
6275 $flag = (int)$flag;
6276 $forced = (boolean)(1 & $flag); // first bit
6277 $adv = (boolean)(2 & $flag); // second bit
6279 return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
6283 * Save the new settings passed in $data
6285 * @todo Add vartype handling to ensure $data is array
6286 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6287 * @return string empty or error message
6289 public function write_setting($data) {
6290 global $CFG;
6292 $value = $data['value'];
6293 $forced = empty($data['forced']) ? 0 : 1;
6294 $adv = empty($data['adv']) ? 0 : 2;
6295 $flag = ($forced | $adv); //bitwise or
6297 if (!in_array($value, array_keys($this->choices))) {
6298 return 'Error setting ';
6301 $oldvalue = $this->config_read($this->name);
6302 $oldflag = (int)$this->config_read($this->name.'_flag');
6303 $oldforced = (1 & $oldflag); // first bit
6305 $result1 = $this->config_write($this->name, $value);
6306 $result2 = $this->config_write($this->name.'_flag', $flag);
6308 // force regrade if needed
6309 if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
6310 require_once($CFG->libdir.'/gradelib.php');
6311 grade_category::updated_forced_settings();
6314 if ($result1 and $result2) {
6315 return '';
6316 } else {
6317 return get_string('errorsetting', 'admin');
6322 * Return XHTML to display the field and wrapping div
6324 * @todo Add vartype handling to ensure $data is array
6325 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
6326 * @param string $query
6327 * @return string XHTML to display control
6329 public function output_html($data, $query='') {
6330 global $OUTPUT;
6332 $value = $data['value'];
6334 $default = $this->get_defaultsetting();
6335 if (!is_null($default)) {
6336 $defaultinfo = array();
6337 if (isset($this->choices[$default['value']])) {
6338 $defaultinfo[] = $this->choices[$default['value']];
6340 if (!empty($default['forced'])) {
6341 $defaultinfo[] = get_string('force');
6343 if (!empty($default['adv'])) {
6344 $defaultinfo[] = get_string('advanced');
6346 $defaultinfo = implode(', ', $defaultinfo);
6348 } else {
6349 $defaultinfo = NULL;
6352 $options = $this->choices;
6353 $context = (object) [
6354 'id' => $this->get_id(),
6355 'name' => $this->get_full_name(),
6356 'forced' => !empty($data['forced']),
6357 'advanced' => !empty($data['adv']),
6358 'options' => array_map(function($option) use ($options, $value) {
6359 return [
6360 'value' => $option,
6361 'name' => $options[$option],
6362 'selected' => $option == $value
6364 }, array_keys($options)),
6367 $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
6369 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
6375 * Selection of grade report in user profiles
6377 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6379 class admin_setting_grade_profilereport extends admin_setting_configselect {
6381 * Calls parent::__construct with specific arguments
6383 public function __construct() {
6384 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
6388 * Loads an array of choices for the configselect control
6390 * @return bool always return true
6392 public function load_choices() {
6393 if (is_array($this->choices)) {
6394 return true;
6396 $this->choices = array();
6398 global $CFG;
6399 require_once($CFG->libdir.'/gradelib.php');
6401 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6402 if (file_exists($plugindir.'/lib.php')) {
6403 require_once($plugindir.'/lib.php');
6404 $functionname = 'grade_report_'.$plugin.'_profilereport';
6405 if (function_exists($functionname)) {
6406 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
6410 return true;
6415 * Provides a selection of grade reports to be used for "grades".
6417 * @copyright 2015 Adrian Greeve <adrian@moodle.com>
6418 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6420 class admin_setting_my_grades_report extends admin_setting_configselect {
6423 * Calls parent::__construct with specific arguments.
6425 public function __construct() {
6426 parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
6427 new lang_string('mygrades_desc', 'grades'), 'overview', null);
6431 * Loads an array of choices for the configselect control.
6433 * @return bool always returns true.
6435 public function load_choices() {
6436 global $CFG; // Remove this line and behold the horror of behat test failures!
6437 $this->choices = array();
6438 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6439 if (file_exists($plugindir . '/lib.php')) {
6440 require_once($plugindir . '/lib.php');
6441 // Check to see if the class exists. Check the correct plugin convention first.
6442 if (class_exists('gradereport_' . $plugin)) {
6443 $classname = 'gradereport_' . $plugin;
6444 } else if (class_exists('grade_report_' . $plugin)) {
6445 // We are using the old plugin naming convention.
6446 $classname = 'grade_report_' . $plugin;
6447 } else {
6448 continue;
6450 if ($classname::supports_mygrades()) {
6451 $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
6455 // Add an option to specify an external url.
6456 $this->choices['external'] = get_string('externalurl', 'grades');
6457 return true;
6462 * Special class for register auth selection
6464 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6466 class admin_setting_special_registerauth extends admin_setting_configselect {
6468 * Calls parent::__construct with specific arguments
6470 public function __construct() {
6471 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
6475 * Returns the default option
6477 * @return string empty or default option
6479 public function get_defaultsetting() {
6480 $this->load_choices();
6481 $defaultsetting = parent::get_defaultsetting();
6482 if (array_key_exists($defaultsetting, $this->choices)) {
6483 return $defaultsetting;
6484 } else {
6485 return '';
6490 * Loads the possible choices for the array
6492 * @return bool always returns true
6494 public function load_choices() {
6495 global $CFG;
6497 if (is_array($this->choices)) {
6498 return true;
6500 $this->choices = array();
6501 $this->choices[''] = get_string('disable');
6503 $authsenabled = get_enabled_auth_plugins();
6505 foreach ($authsenabled as $auth) {
6506 $authplugin = get_auth_plugin($auth);
6507 if (!$authplugin->can_signup()) {
6508 continue;
6510 // Get the auth title (from core or own auth lang files)
6511 $authtitle = $authplugin->get_title();
6512 $this->choices[$auth] = $authtitle;
6514 return true;
6520 * General plugins manager
6522 class admin_page_pluginsoverview extends admin_externalpage {
6525 * Sets basic information about the external page
6527 public function __construct() {
6528 global $CFG;
6529 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
6530 "$CFG->wwwroot/$CFG->admin/plugins.php");
6535 * Module manage page
6537 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6539 class admin_page_managemods extends admin_externalpage {
6541 * Calls parent::__construct with specific arguments
6543 public function __construct() {
6544 global $CFG;
6545 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
6549 * Try to find the specified module
6551 * @param string $query The module to search for
6552 * @return array
6554 public function search($query) {
6555 global $CFG, $DB;
6556 if ($result = parent::search($query)) {
6557 return $result;
6560 $found = false;
6561 if ($modules = $DB->get_records('modules')) {
6562 foreach ($modules as $module) {
6563 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
6564 continue;
6566 if (strpos($module->name, $query) !== false) {
6567 $found = true;
6568 break;
6570 $strmodulename = get_string('modulename', $module->name);
6571 if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
6572 $found = true;
6573 break;
6577 if ($found) {
6578 $result = new stdClass();
6579 $result->page = $this;
6580 $result->settings = array();
6581 return array($this->name => $result);
6582 } else {
6583 return array();
6590 * Special class for enrol plugins management.
6592 * @copyright 2010 Petr Skoda {@link http://skodak.org}
6593 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6595 class admin_setting_manageenrols extends admin_setting {
6597 * Calls parent::__construct with specific arguments
6599 public function __construct() {
6600 $this->nosave = true;
6601 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
6605 * Always returns true, does nothing
6607 * @return true
6609 public function get_setting() {
6610 return true;
6614 * Always returns true, does nothing
6616 * @return true
6618 public function get_defaultsetting() {
6619 return true;
6623 * Always returns '', does not write anything
6625 * @return string Always returns ''
6627 public function write_setting($data) {
6628 // do not write any setting
6629 return '';
6633 * Checks if $query is one of the available enrol plugins
6635 * @param string $query The string to search for
6636 * @return bool Returns true if found, false if not
6638 public function is_related($query) {
6639 if (parent::is_related($query)) {
6640 return true;
6643 $query = core_text::strtolower($query);
6644 $enrols = enrol_get_plugins(false);
6645 foreach ($enrols as $name=>$enrol) {
6646 $localised = get_string('pluginname', 'enrol_'.$name);
6647 if (strpos(core_text::strtolower($name), $query) !== false) {
6648 return true;
6650 if (strpos(core_text::strtolower($localised), $query) !== false) {
6651 return true;
6654 return false;
6658 * Builds the XHTML to display the control
6660 * @param string $data Unused
6661 * @param string $query
6662 * @return string
6664 public function output_html($data, $query='') {
6665 global $CFG, $OUTPUT, $DB, $PAGE;
6667 // Display strings.
6668 $strup = get_string('up');
6669 $strdown = get_string('down');
6670 $strsettings = get_string('settings');
6671 $strenable = get_string('enable');
6672 $strdisable = get_string('disable');
6673 $struninstall = get_string('uninstallplugin', 'core_admin');
6674 $strusage = get_string('enrolusage', 'enrol');
6675 $strversion = get_string('version');
6676 $strtest = get_string('testsettings', 'core_enrol');
6678 $pluginmanager = core_plugin_manager::instance();
6680 $enrols_available = enrol_get_plugins(false);
6681 $active_enrols = enrol_get_plugins(true);
6683 $allenrols = array();
6684 foreach ($active_enrols as $key=>$enrol) {
6685 $allenrols[$key] = true;
6687 foreach ($enrols_available as $key=>$enrol) {
6688 $allenrols[$key] = true;
6690 // Now find all borked plugins and at least allow then to uninstall.
6691 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
6692 foreach ($condidates as $candidate) {
6693 if (empty($allenrols[$candidate])) {
6694 $allenrols[$candidate] = true;
6698 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
6699 $return .= $OUTPUT->box_start('generalbox enrolsui');
6701 $table = new html_table();
6702 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
6703 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6704 $table->id = 'courseenrolmentplugins';
6705 $table->attributes['class'] = 'admintable generaltable';
6706 $table->data = array();
6708 // Iterate through enrol plugins and add to the display table.
6709 $updowncount = 1;
6710 $enrolcount = count($active_enrols);
6711 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
6712 $printed = array();
6713 foreach($allenrols as $enrol => $unused) {
6714 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
6715 $version = get_config('enrol_'.$enrol, 'version');
6716 if ($version === false) {
6717 $version = '';
6720 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
6721 $name = get_string('pluginname', 'enrol_'.$enrol);
6722 } else {
6723 $name = $enrol;
6725 // Usage.
6726 $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
6727 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
6728 $usage = "$ci / $cp";
6730 // Hide/show links.
6731 $class = '';
6732 if (isset($active_enrols[$enrol])) {
6733 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
6734 $hideshow = "<a href=\"$aurl\">";
6735 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
6736 $enabled = true;
6737 $displayname = $name;
6738 } else if (isset($enrols_available[$enrol])) {
6739 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
6740 $hideshow = "<a href=\"$aurl\">";
6741 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
6742 $enabled = false;
6743 $displayname = $name;
6744 $class = 'dimmed_text';
6745 } else {
6746 $hideshow = '';
6747 $enabled = false;
6748 $displayname = '<span class="notifyproblem">'.$name.'</span>';
6750 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
6751 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
6752 } else {
6753 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
6756 // Up/down link (only if enrol is enabled).
6757 $updown = '';
6758 if ($enabled) {
6759 if ($updowncount > 1) {
6760 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
6761 $updown .= "<a href=\"$aurl\">";
6762 $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
6763 } else {
6764 $updown .= $OUTPUT->spacer() . '&nbsp;';
6766 if ($updowncount < $enrolcount) {
6767 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
6768 $updown .= "<a href=\"$aurl\">";
6769 $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
6770 } else {
6771 $updown .= $OUTPUT->spacer() . '&nbsp;';
6773 ++$updowncount;
6776 // Add settings link.
6777 if (!$version) {
6778 $settings = '';
6779 } else if ($surl = $plugininfo->get_settings_url()) {
6780 $settings = html_writer::link($surl, $strsettings);
6781 } else {
6782 $settings = '';
6785 // Add uninstall info.
6786 $uninstall = '';
6787 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
6788 $uninstall = html_writer::link($uninstallurl, $struninstall);
6791 $test = '';
6792 if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
6793 $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey()));
6794 $test = html_writer::link($testsettingsurl, $strtest);
6797 // Add a row to the table.
6798 $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
6799 if ($class) {
6800 $row->attributes['class'] = $class;
6802 $table->data[] = $row;
6804 $printed[$enrol] = true;
6807 $return .= html_writer::table($table);
6808 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
6809 $return .= $OUTPUT->box_end();
6810 return highlight($query, $return);
6816 * Blocks manage page
6818 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6820 class admin_page_manageblocks extends admin_externalpage {
6822 * Calls parent::__construct with specific arguments
6824 public function __construct() {
6825 global $CFG;
6826 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
6830 * Search for a specific block
6832 * @param string $query The string to search for
6833 * @return array
6835 public function search($query) {
6836 global $CFG, $DB;
6837 if ($result = parent::search($query)) {
6838 return $result;
6841 $found = false;
6842 if ($blocks = $DB->get_records('block')) {
6843 foreach ($blocks as $block) {
6844 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
6845 continue;
6847 if (strpos($block->name, $query) !== false) {
6848 $found = true;
6849 break;
6851 $strblockname = get_string('pluginname', 'block_'.$block->name);
6852 if (strpos(core_text::strtolower($strblockname), $query) !== false) {
6853 $found = true;
6854 break;
6858 if ($found) {
6859 $result = new stdClass();
6860 $result->page = $this;
6861 $result->settings = array();
6862 return array($this->name => $result);
6863 } else {
6864 return array();
6870 * Message outputs configuration
6872 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6874 class admin_page_managemessageoutputs extends admin_externalpage {
6876 * Calls parent::__construct with specific arguments
6878 public function __construct() {
6879 global $CFG;
6880 parent::__construct('managemessageoutputs',
6881 get_string('defaultmessageoutputs', 'message'),
6882 new moodle_url('/admin/message.php')
6887 * Search for a specific message processor
6889 * @param string $query The string to search for
6890 * @return array
6892 public function search($query) {
6893 global $CFG, $DB;
6894 if ($result = parent::search($query)) {
6895 return $result;
6898 $found = false;
6899 if ($processors = get_message_processors()) {
6900 foreach ($processors as $processor) {
6901 if (!$processor->available) {
6902 continue;
6904 if (strpos($processor->name, $query) !== false) {
6905 $found = true;
6906 break;
6908 $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
6909 if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
6910 $found = true;
6911 break;
6915 if ($found) {
6916 $result = new stdClass();
6917 $result->page = $this;
6918 $result->settings = array();
6919 return array($this->name => $result);
6920 } else {
6921 return array();
6927 * Manage question behaviours page
6929 * @copyright 2011 The Open University
6930 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6932 class admin_page_manageqbehaviours extends admin_externalpage {
6934 * Constructor
6936 public function __construct() {
6937 global $CFG;
6938 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
6939 new moodle_url('/admin/qbehaviours.php'));
6943 * Search question behaviours for the specified string
6945 * @param string $query The string to search for in question behaviours
6946 * @return array
6948 public function search($query) {
6949 global $CFG;
6950 if ($result = parent::search($query)) {
6951 return $result;
6954 $found = false;
6955 require_once($CFG->dirroot . '/question/engine/lib.php');
6956 foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
6957 if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
6958 $query) !== false) {
6959 $found = true;
6960 break;
6963 if ($found) {
6964 $result = new stdClass();
6965 $result->page = $this;
6966 $result->settings = array();
6967 return array($this->name => $result);
6968 } else {
6969 return array();
6976 * Question type manage page
6978 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6980 class admin_page_manageqtypes extends admin_externalpage {
6982 * Calls parent::__construct with specific arguments
6984 public function __construct() {
6985 global $CFG;
6986 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
6987 new moodle_url('/admin/qtypes.php'));
6991 * Search question types for the specified string
6993 * @param string $query The string to search for in question types
6994 * @return array
6996 public function search($query) {
6997 global $CFG;
6998 if ($result = parent::search($query)) {
6999 return $result;
7002 $found = false;
7003 require_once($CFG->dirroot . '/question/engine/bank.php');
7004 foreach (question_bank::get_all_qtypes() as $qtype) {
7005 if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
7006 $found = true;
7007 break;
7010 if ($found) {
7011 $result = new stdClass();
7012 $result->page = $this;
7013 $result->settings = array();
7014 return array($this->name => $result);
7015 } else {
7016 return array();
7022 class admin_page_manageportfolios extends admin_externalpage {
7024 * Calls parent::__construct with specific arguments
7026 public function __construct() {
7027 global $CFG;
7028 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
7029 "$CFG->wwwroot/$CFG->admin/portfolio.php");
7033 * Searches page for the specified string.
7034 * @param string $query The string to search for
7035 * @return bool True if it is found on this page
7037 public function search($query) {
7038 global $CFG;
7039 if ($result = parent::search($query)) {
7040 return $result;
7043 $found = false;
7044 $portfolios = core_component::get_plugin_list('portfolio');
7045 foreach ($portfolios as $p => $dir) {
7046 if (strpos($p, $query) !== false) {
7047 $found = true;
7048 break;
7051 if (!$found) {
7052 foreach (portfolio_instances(false, false) as $instance) {
7053 $title = $instance->get('name');
7054 if (strpos(core_text::strtolower($title), $query) !== false) {
7055 $found = true;
7056 break;
7061 if ($found) {
7062 $result = new stdClass();
7063 $result->page = $this;
7064 $result->settings = array();
7065 return array($this->name => $result);
7066 } else {
7067 return array();
7073 class admin_page_managerepositories extends admin_externalpage {
7075 * Calls parent::__construct with specific arguments
7077 public function __construct() {
7078 global $CFG;
7079 parent::__construct('managerepositories', get_string('manage',
7080 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
7084 * Searches page for the specified string.
7085 * @param string $query The string to search for
7086 * @return bool True if it is found on this page
7088 public function search($query) {
7089 global $CFG;
7090 if ($result = parent::search($query)) {
7091 return $result;
7094 $found = false;
7095 $repositories= core_component::get_plugin_list('repository');
7096 foreach ($repositories as $p => $dir) {
7097 if (strpos($p, $query) !== false) {
7098 $found = true;
7099 break;
7102 if (!$found) {
7103 foreach (repository::get_types() as $instance) {
7104 $title = $instance->get_typename();
7105 if (strpos(core_text::strtolower($title), $query) !== false) {
7106 $found = true;
7107 break;
7112 if ($found) {
7113 $result = new stdClass();
7114 $result->page = $this;
7115 $result->settings = array();
7116 return array($this->name => $result);
7117 } else {
7118 return array();
7125 * Special class for authentication administration.
7127 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7129 class admin_setting_manageauths extends admin_setting {
7131 * Calls parent::__construct with specific arguments
7133 public function __construct() {
7134 $this->nosave = true;
7135 parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
7139 * Always returns true
7141 * @return true
7143 public function get_setting() {
7144 return true;
7148 * Always returns true
7150 * @return true
7152 public function get_defaultsetting() {
7153 return true;
7157 * Always returns '' and doesn't write anything
7159 * @return string Always returns ''
7161 public function write_setting($data) {
7162 // do not write any setting
7163 return '';
7167 * Search to find if Query is related to auth plugin
7169 * @param string $query The string to search for
7170 * @return bool true for related false for not
7172 public function is_related($query) {
7173 if (parent::is_related($query)) {
7174 return true;
7177 $authsavailable = core_component::get_plugin_list('auth');
7178 foreach ($authsavailable as $auth => $dir) {
7179 if (strpos($auth, $query) !== false) {
7180 return true;
7182 $authplugin = get_auth_plugin($auth);
7183 $authtitle = $authplugin->get_title();
7184 if (strpos(core_text::strtolower($authtitle), $query) !== false) {
7185 return true;
7188 return false;
7192 * Return XHTML to display control
7194 * @param mixed $data Unused
7195 * @param string $query
7196 * @return string highlight
7198 public function output_html($data, $query='') {
7199 global $CFG, $OUTPUT, $DB;
7201 // display strings
7202 $txt = get_strings(array('authenticationplugins', 'users', 'administration',
7203 'settings', 'edit', 'name', 'enable', 'disable',
7204 'up', 'down', 'none', 'users'));
7205 $txt->updown = "$txt->up/$txt->down";
7206 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7207 $txt->testsettings = get_string('testsettings', 'core_auth');
7209 $authsavailable = core_component::get_plugin_list('auth');
7210 get_enabled_auth_plugins(true); // fix the list of enabled auths
7211 if (empty($CFG->auth)) {
7212 $authsenabled = array();
7213 } else {
7214 $authsenabled = explode(',', $CFG->auth);
7217 // construct the display array, with enabled auth plugins at the top, in order
7218 $displayauths = array();
7219 $registrationauths = array();
7220 $registrationauths[''] = $txt->disable;
7221 $authplugins = array();
7222 foreach ($authsenabled as $auth) {
7223 $authplugin = get_auth_plugin($auth);
7224 $authplugins[$auth] = $authplugin;
7225 /// Get the auth title (from core or own auth lang files)
7226 $authtitle = $authplugin->get_title();
7227 /// Apply titles
7228 $displayauths[$auth] = $authtitle;
7229 if ($authplugin->can_signup()) {
7230 $registrationauths[$auth] = $authtitle;
7234 foreach ($authsavailable as $auth => $dir) {
7235 if (array_key_exists($auth, $displayauths)) {
7236 continue; //already in the list
7238 $authplugin = get_auth_plugin($auth);
7239 $authplugins[$auth] = $authplugin;
7240 /// Get the auth title (from core or own auth lang files)
7241 $authtitle = $authplugin->get_title();
7242 /// Apply titles
7243 $displayauths[$auth] = $authtitle;
7244 if ($authplugin->can_signup()) {
7245 $registrationauths[$auth] = $authtitle;
7249 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
7250 $return .= $OUTPUT->box_start('generalbox authsui');
7252 $table = new html_table();
7253 $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
7254 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7255 $table->data = array();
7256 $table->attributes['class'] = 'admintable generaltable';
7257 $table->id = 'manageauthtable';
7259 //add always enabled plugins first
7260 $displayname = $displayauths['manual'];
7261 $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
7262 $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
7263 $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
7264 $displayname = $displayauths['nologin'];
7265 $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
7266 $table->data[] = array($displayname, $usercount, '', '', '', '', '');
7269 // iterate through auth plugins and add to the display table
7270 $updowncount = 1;
7271 $authcount = count($authsenabled);
7272 $url = "auth.php?sesskey=" . sesskey();
7273 foreach ($displayauths as $auth => $name) {
7274 if ($auth == 'manual' or $auth == 'nologin') {
7275 continue;
7277 $class = '';
7278 // hide/show link
7279 if (in_array($auth, $authsenabled)) {
7280 $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
7281 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
7282 $enabled = true;
7283 $displayname = $name;
7285 else {
7286 $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
7287 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
7288 $enabled = false;
7289 $displayname = $name;
7290 $class = 'dimmed_text';
7293 $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
7295 // up/down link (only if auth is enabled)
7296 $updown = '';
7297 if ($enabled) {
7298 if ($updowncount > 1) {
7299 $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
7300 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
7302 else {
7303 $updown .= $OUTPUT->spacer() . '&nbsp;';
7305 if ($updowncount < $authcount) {
7306 $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
7307 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
7309 else {
7310 $updown .= $OUTPUT->spacer() . '&nbsp;';
7312 ++ $updowncount;
7315 // settings link
7316 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
7317 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
7318 } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
7319 throw new \coding_exception('config.html is no longer supported, please use settings.php instead.');
7320 } else {
7321 $settings = '';
7324 // Uninstall link.
7325 $uninstall = '';
7326 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
7327 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7330 $test = '';
7331 if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
7332 $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey()));
7333 $test = html_writer::link($testurl, $txt->testsettings);
7336 // Add a row to the table.
7337 $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
7338 if ($class) {
7339 $row->attributes['class'] = $class;
7341 $table->data[] = $row;
7343 $return .= html_writer::table($table);
7344 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
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 * Course formats manager. Allows to enable/disable formats and jump to settings
7534 class admin_setting_manageformats extends admin_setting {
7537 * Calls parent::__construct with specific arguments
7539 public function __construct() {
7540 $this->nosave = true;
7541 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
7545 * Always returns true
7547 * @return true
7549 public function get_setting() {
7550 return true;
7554 * Always returns true
7556 * @return true
7558 public function get_defaultsetting() {
7559 return true;
7563 * Always returns '' and doesn't write anything
7565 * @param mixed $data string or array, must not be NULL
7566 * @return string Always returns ''
7568 public function write_setting($data) {
7569 // do not write any setting
7570 return '';
7574 * Search to find if Query is related to format plugin
7576 * @param string $query The string to search for
7577 * @return bool true for related false for not
7579 public function is_related($query) {
7580 if (parent::is_related($query)) {
7581 return true;
7583 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7584 foreach ($formats as $format) {
7585 if (strpos($format->component, $query) !== false ||
7586 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7587 return true;
7590 return false;
7594 * Return XHTML to display control
7596 * @param mixed $data Unused
7597 * @param string $query
7598 * @return string highlight
7600 public function output_html($data, $query='') {
7601 global $CFG, $OUTPUT;
7602 $return = '';
7603 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
7604 $return .= $OUTPUT->box_start('generalbox formatsui');
7606 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7608 // display strings
7609 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7610 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7611 $txt->updown = "$txt->up/$txt->down";
7613 $table = new html_table();
7614 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7615 $table->align = array('left', 'center', 'center', 'center', 'center');
7616 $table->attributes['class'] = 'manageformattable generaltable admintable';
7617 $table->data = array();
7619 $cnt = 0;
7620 $defaultformat = get_config('moodlecourse', 'format');
7621 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7622 foreach ($formats as $format) {
7623 $url = new moodle_url('/admin/courseformats.php',
7624 array('sesskey' => sesskey(), 'format' => $format->name));
7625 $isdefault = '';
7626 $class = '';
7627 if ($format->is_enabled()) {
7628 $strformatname = $format->displayname;
7629 if ($defaultformat === $format->name) {
7630 $hideshow = $txt->default;
7631 } else {
7632 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7633 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7635 } else {
7636 $strformatname = $format->displayname;
7637 $class = 'dimmed_text';
7638 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7639 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7641 $updown = '';
7642 if ($cnt) {
7643 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7644 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7645 } else {
7646 $updown .= $spacer;
7648 if ($cnt < count($formats) - 1) {
7649 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7650 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7651 } else {
7652 $updown .= $spacer;
7654 $cnt++;
7655 $settings = '';
7656 if ($format->get_settings_url()) {
7657 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7659 $uninstall = '';
7660 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
7661 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7663 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7664 if ($class) {
7665 $row->attributes['class'] = $class;
7667 $table->data[] = $row;
7669 $return .= html_writer::table($table);
7670 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
7671 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
7672 $return .= $OUTPUT->box_end();
7673 return highlight($query, $return);
7678 * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
7680 * @package core
7681 * @copyright 2018 Toni Barbera
7682 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7684 class admin_setting_managecustomfields extends admin_setting {
7687 * Calls parent::__construct with specific arguments
7689 public function __construct() {
7690 $this->nosave = true;
7691 parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
7695 * Always returns true
7697 * @return true
7699 public function get_setting() {
7700 return true;
7704 * Always returns true
7706 * @return true
7708 public function get_defaultsetting() {
7709 return true;
7713 * Always returns '' and doesn't write anything
7715 * @param mixed $data string or array, must not be NULL
7716 * @return string Always returns ''
7718 public function write_setting($data) {
7719 // Do not write any setting.
7720 return '';
7724 * Search to find if Query is related to format plugin
7726 * @param string $query The string to search for
7727 * @return bool true for related false for not
7729 public function is_related($query) {
7730 if (parent::is_related($query)) {
7731 return true;
7733 $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7734 foreach ($formats as $format) {
7735 if (strpos($format->component, $query) !== false ||
7736 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7737 return true;
7740 return false;
7744 * Return XHTML to display control
7746 * @param mixed $data Unused
7747 * @param string $query
7748 * @return string highlight
7750 public function output_html($data, $query='') {
7751 global $CFG, $OUTPUT;
7752 $return = '';
7753 $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
7754 $return .= $OUTPUT->box_start('generalbox customfieldsui');
7756 $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7758 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
7759 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7760 $txt->updown = "$txt->up/$txt->down";
7762 $table = new html_table();
7763 $table->head = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
7764 $table->align = array('left', 'center', 'center', 'center');
7765 $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
7766 $table->data = array();
7768 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7769 foreach ($fields as $field) {
7770 $url = new moodle_url('/admin/customfields.php',
7771 array('sesskey' => sesskey(), 'field' => $field->name));
7773 if ($field->is_enabled()) {
7774 $strfieldname = $field->displayname;
7775 $class = '';
7776 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7777 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7778 } else {
7779 $strfieldname = $field->displayname;
7780 $class = 'dimmed_text';
7781 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7782 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7784 $settings = '';
7785 if ($field->get_settings_url()) {
7786 $settings = html_writer::link($field->get_settings_url(), $txt->settings);
7788 $uninstall = '';
7789 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
7790 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7792 $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
7793 $row->attributes['class'] = $class;
7794 $table->data[] = $row;
7796 $return .= html_writer::table($table);
7797 $return .= $OUTPUT->box_end();
7798 return highlight($query, $return);
7803 * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
7805 * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net)
7806 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7808 class admin_setting_managedataformats extends admin_setting {
7811 * Calls parent::__construct with specific arguments
7813 public function __construct() {
7814 $this->nosave = true;
7815 parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
7819 * Always returns true
7821 * @return true
7823 public function get_setting() {
7824 return true;
7828 * Always returns true
7830 * @return true
7832 public function get_defaultsetting() {
7833 return true;
7837 * Always returns '' and doesn't write anything
7839 * @param mixed $data string or array, must not be NULL
7840 * @return string Always returns ''
7842 public function write_setting($data) {
7843 // Do not write any setting.
7844 return '';
7848 * Search to find if Query is related to format plugin
7850 * @param string $query The string to search for
7851 * @return bool true for related false for not
7853 public function is_related($query) {
7854 if (parent::is_related($query)) {
7855 return true;
7857 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7858 foreach ($formats as $format) {
7859 if (strpos($format->component, $query) !== false ||
7860 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7861 return true;
7864 return false;
7868 * Return XHTML to display control
7870 * @param mixed $data Unused
7871 * @param string $query
7872 * @return string highlight
7874 public function output_html($data, $query='') {
7875 global $CFG, $OUTPUT;
7876 $return = '';
7878 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7880 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7881 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7882 $txt->updown = "$txt->up/$txt->down";
7884 $table = new html_table();
7885 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7886 $table->align = array('left', 'center', 'center', 'center', 'center');
7887 $table->attributes['class'] = 'manageformattable generaltable admintable';
7888 $table->data = array();
7890 $cnt = 0;
7891 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7892 $totalenabled = 0;
7893 foreach ($formats as $format) {
7894 if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
7895 $totalenabled++;
7898 foreach ($formats as $format) {
7899 $status = $format->get_status();
7900 $url = new moodle_url('/admin/dataformats.php',
7901 array('sesskey' => sesskey(), 'name' => $format->name));
7903 $class = '';
7904 if ($format->is_enabled()) {
7905 $strformatname = $format->displayname;
7906 if ($totalenabled == 1&& $format->is_enabled()) {
7907 $hideshow = '';
7908 } else {
7909 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7910 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7912 } else {
7913 $class = 'dimmed_text';
7914 $strformatname = $format->displayname;
7915 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7916 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7919 $updown = '';
7920 if ($cnt) {
7921 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7922 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7923 } else {
7924 $updown .= $spacer;
7926 if ($cnt < count($formats) - 1) {
7927 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7928 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7929 } else {
7930 $updown .= $spacer;
7933 $uninstall = '';
7934 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
7935 $uninstall = get_string('status_missing', 'core_plugin');
7936 } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
7937 $uninstall = get_string('status_new', 'core_plugin');
7938 } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
7939 if ($totalenabled != 1 || !$format->is_enabled()) {
7940 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7944 $settings = '';
7945 if ($format->get_settings_url()) {
7946 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7949 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7950 if ($class) {
7951 $row->attributes['class'] = $class;
7953 $table->data[] = $row;
7954 $cnt++;
7956 $return .= html_writer::table($table);
7957 return highlight($query, $return);
7962 * Special class for filter administration.
7964 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7966 class admin_page_managefilters extends admin_externalpage {
7968 * Calls parent::__construct with specific arguments
7970 public function __construct() {
7971 global $CFG;
7972 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
7976 * Searches all installed filters for specified filter
7978 * @param string $query The filter(string) to search for
7979 * @param string $query
7981 public function search($query) {
7982 global $CFG;
7983 if ($result = parent::search($query)) {
7984 return $result;
7987 $found = false;
7988 $filternames = filter_get_all_installed();
7989 foreach ($filternames as $path => $strfiltername) {
7990 if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
7991 $found = true;
7992 break;
7994 if (strpos($path, $query) !== false) {
7995 $found = true;
7996 break;
8000 if ($found) {
8001 $result = new stdClass;
8002 $result->page = $this;
8003 $result->settings = array();
8004 return array($this->name => $result);
8005 } else {
8006 return array();
8012 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8013 * Requires a get_rank method on the plugininfo class for sorting.
8015 * @copyright 2017 Damyon Wiese
8016 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8018 abstract class admin_setting_manage_plugins extends admin_setting {
8021 * Get the admin settings section name (just a unique string)
8023 * @return string
8025 public function get_section_name() {
8026 return 'manage' . $this->get_plugin_type() . 'plugins';
8030 * Get the admin settings section title (use get_string).
8032 * @return string
8034 abstract public function get_section_title();
8037 * Get the type of plugin to manage.
8039 * @return string
8041 abstract public function get_plugin_type();
8044 * Get the name of the second column.
8046 * @return string
8048 public function get_info_column_name() {
8049 return '';
8053 * Get the type of plugin to manage.
8055 * @param plugininfo The plugin info class.
8056 * @return string
8058 abstract public function get_info_column($plugininfo);
8061 * Calls parent::__construct with specific arguments
8063 public function __construct() {
8064 $this->nosave = true;
8065 parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
8069 * Always returns true, does nothing
8071 * @return true
8073 public function get_setting() {
8074 return true;
8078 * Always returns true, does nothing
8080 * @return true
8082 public function get_defaultsetting() {
8083 return true;
8087 * Always returns '', does not write anything
8089 * @param mixed $data
8090 * @return string Always returns ''
8092 public function write_setting($data) {
8093 // Do not write any setting.
8094 return '';
8098 * Checks if $query is one of the available plugins of this type
8100 * @param string $query The string to search for
8101 * @return bool Returns true if found, false if not
8103 public function is_related($query) {
8104 if (parent::is_related($query)) {
8105 return true;
8108 $query = core_text::strtolower($query);
8109 $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
8110 foreach ($plugins as $name => $plugin) {
8111 $localised = $plugin->displayname;
8112 if (strpos(core_text::strtolower($name), $query) !== false) {
8113 return true;
8115 if (strpos(core_text::strtolower($localised), $query) !== false) {
8116 return true;
8119 return false;
8123 * The URL for the management page for this plugintype.
8125 * @return moodle_url
8127 protected function get_manage_url() {
8128 return new moodle_url('/admin/updatesetting.php');
8132 * Builds the HTML to display the control.
8134 * @param string $data Unused
8135 * @param string $query
8136 * @return string
8138 public function output_html($data, $query = '') {
8139 global $CFG, $OUTPUT, $DB, $PAGE;
8141 $context = (object) [
8142 'manageurl' => new moodle_url($this->get_manage_url(), [
8143 'type' => $this->get_plugin_type(),
8144 'sesskey' => sesskey(),
8146 'infocolumnname' => $this->get_info_column_name(),
8147 'plugins' => [],
8150 $pluginmanager = core_plugin_manager::instance();
8151 $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
8152 $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
8153 $plugins = array_merge($enabled, $allplugins);
8154 foreach ($plugins as $key => $plugin) {
8155 $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
8157 $pluginkey = (object) [
8158 'plugin' => $plugin->displayname,
8159 'enabled' => $plugin->is_enabled(),
8160 'togglelink' => '',
8161 'moveuplink' => '',
8162 'movedownlink' => '',
8163 'settingslink' => $plugin->get_settings_url(),
8164 'uninstalllink' => '',
8165 'info' => '',
8168 // Enable/Disable link.
8169 $togglelink = new moodle_url($pluginlink);
8170 if ($plugin->is_enabled()) {
8171 $toggletarget = false;
8172 $togglelink->param('action', 'disable');
8174 if (count($context->plugins)) {
8175 // This is not the first plugin.
8176 $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
8179 if (count($enabled) > count($context->plugins) + 1) {
8180 // This is not the last plugin.
8181 $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
8184 $pluginkey->info = $this->get_info_column($plugin);
8185 } else {
8186 $toggletarget = true;
8187 $togglelink->param('action', 'enable');
8190 $pluginkey->toggletarget = $toggletarget;
8191 $pluginkey->togglelink = $togglelink;
8193 $frankenstyle = $plugin->type . '_' . $plugin->name;
8194 if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
8195 // This plugin supports uninstallation.
8196 $pluginkey->uninstalllink = $uninstalllink;
8199 if (!empty($this->get_info_column_name())) {
8200 // This plugintype has an info column.
8201 $pluginkey->info = $this->get_info_column($plugin);
8204 $context->plugins[] = $pluginkey;
8207 $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
8208 return highlight($query, $str);
8213 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8214 * Requires a get_rank method on the plugininfo class for sorting.
8216 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
8217 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8219 class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
8220 public function get_section_title() {
8221 return get_string('type_fileconverter_plural', 'plugin');
8224 public function get_plugin_type() {
8225 return 'fileconverter';
8228 public function get_info_column_name() {
8229 return get_string('supportedconversions', 'plugin');
8232 public function get_info_column($plugininfo) {
8233 return $plugininfo->get_supported_conversions();
8238 * Special class for media player plugins management.
8240 * @copyright 2016 Marina Glancy
8241 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8243 class admin_setting_managemediaplayers extends admin_setting {
8245 * Calls parent::__construct with specific arguments
8247 public function __construct() {
8248 $this->nosave = true;
8249 parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
8253 * Always returns true, does nothing
8255 * @return true
8257 public function get_setting() {
8258 return true;
8262 * Always returns true, does nothing
8264 * @return true
8266 public function get_defaultsetting() {
8267 return true;
8271 * Always returns '', does not write anything
8273 * @param mixed $data
8274 * @return string Always returns ''
8276 public function write_setting($data) {
8277 // Do not write any setting.
8278 return '';
8282 * Checks if $query is one of the available enrol plugins
8284 * @param string $query The string to search for
8285 * @return bool Returns true if found, false if not
8287 public function is_related($query) {
8288 if (parent::is_related($query)) {
8289 return true;
8292 $query = core_text::strtolower($query);
8293 $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
8294 foreach ($plugins as $name => $plugin) {
8295 $localised = $plugin->displayname;
8296 if (strpos(core_text::strtolower($name), $query) !== false) {
8297 return true;
8299 if (strpos(core_text::strtolower($localised), $query) !== false) {
8300 return true;
8303 return false;
8307 * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8308 * @return \core\plugininfo\media[]
8310 protected function get_sorted_plugins() {
8311 $pluginmanager = core_plugin_manager::instance();
8313 $plugins = $pluginmanager->get_plugins_of_type('media');
8314 $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8316 // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8317 \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
8319 $order = array_values($enabledplugins);
8320 $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
8322 $sortedplugins = array();
8323 foreach ($order as $name) {
8324 $sortedplugins[$name] = $plugins[$name];
8327 return $sortedplugins;
8331 * Builds the XHTML to display the control
8333 * @param string $data Unused
8334 * @param string $query
8335 * @return string
8337 public function output_html($data, $query='') {
8338 global $CFG, $OUTPUT, $DB, $PAGE;
8340 // Display strings.
8341 $strup = get_string('up');
8342 $strdown = get_string('down');
8343 $strsettings = get_string('settings');
8344 $strenable = get_string('enable');
8345 $strdisable = get_string('disable');
8346 $struninstall = get_string('uninstallplugin', 'core_admin');
8347 $strversion = get_string('version');
8348 $strname = get_string('name');
8349 $strsupports = get_string('supports', 'core_media');
8351 $pluginmanager = core_plugin_manager::instance();
8353 $plugins = $this->get_sorted_plugins();
8354 $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8356 $return = $OUTPUT->box_start('generalbox mediaplayersui');
8358 $table = new html_table();
8359 $table->head = array($strname, $strsupports, $strversion,
8360 $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
8361 $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
8362 'centeralign', 'centeralign', 'centeralign', 'centeralign');
8363 $table->id = 'mediaplayerplugins';
8364 $table->attributes['class'] = 'admintable generaltable';
8365 $table->data = array();
8367 // Iterate through media plugins and add to the display table.
8368 $updowncount = 1;
8369 $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
8370 $printed = array();
8371 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8373 $usedextensions = [];
8374 foreach ($plugins as $name => $plugin) {
8375 $url->param('media', $name);
8376 /** @var \core\plugininfo\media $plugininfo */
8377 $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
8378 $version = $plugininfo->versiondb;
8379 $supports = $plugininfo->supports($usedextensions);
8381 // Hide/show links.
8382 $class = '';
8383 if (!$plugininfo->is_installed_and_upgraded()) {
8384 $hideshow = '';
8385 $enabled = false;
8386 $displayname = '<span class="notifyproblem">'.$name.'</span>';
8387 } else {
8388 $enabled = $plugininfo->is_enabled();
8389 if ($enabled) {
8390 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
8391 $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
8392 } else {
8393 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
8394 $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
8395 $class = 'dimmed_text';
8397 $displayname = $plugin->displayname;
8398 if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
8399 $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
8402 if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
8403 $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
8404 } else {
8405 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
8408 // Up/down link (only if enrol is enabled).
8409 $updown = '';
8410 if ($enabled) {
8411 if ($updowncount > 1) {
8412 $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
8413 $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
8414 } else {
8415 $updown = $spacer;
8417 if ($updowncount < count($enabledplugins)) {
8418 $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
8419 $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
8420 } else {
8421 $updown .= $spacer;
8423 ++$updowncount;
8426 $uninstall = '';
8427 $status = $plugininfo->get_status();
8428 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8429 $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
8431 if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8432 $uninstall = get_string('status_new', 'core_plugin');
8433 } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
8434 $uninstall .= html_writer::link($uninstallurl, $struninstall);
8437 $settings = '';
8438 if ($plugininfo->get_settings_url()) {
8439 $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
8442 // Add a row to the table.
8443 $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
8444 if ($class) {
8445 $row->attributes['class'] = $class;
8447 $table->data[] = $row;
8449 $printed[$name] = true;
8452 $return .= html_writer::table($table);
8453 $return .= $OUTPUT->box_end();
8454 return highlight($query, $return);
8460 * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
8462 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
8463 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8465 class admin_setting_managecontentbankcontenttypes extends admin_setting {
8468 * Calls parent::__construct with specific arguments
8470 public function __construct() {
8471 $this->nosave = true;
8472 parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
8476 * Always returns true
8478 * @return true
8480 public function get_setting() {
8481 return true;
8485 * Always returns true
8487 * @return true
8489 public function get_defaultsetting() {
8490 return true;
8494 * Always returns '' and doesn't write anything
8496 * @param mixed $data string or array, must not be NULL
8497 * @return string Always returns ''
8499 public function write_setting($data) {
8500 // Do not write any setting.
8501 return '';
8505 * Search to find if Query is related to content bank plugin
8507 * @param string $query The string to search for
8508 * @return bool true for related false for not
8510 public function is_related($query) {
8511 if (parent::is_related($query)) {
8512 return true;
8514 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8515 foreach ($types as $type) {
8516 if (strpos($type->component, $query) !== false ||
8517 strpos(core_text::strtolower($type->displayname), $query) !== false) {
8518 return true;
8521 return false;
8525 * Return XHTML to display control
8527 * @param mixed $data Unused
8528 * @param string $query
8529 * @return string highlight
8531 public function output_html($data, $query='') {
8532 global $CFG, $OUTPUT;
8533 $return = '';
8535 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8536 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
8537 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
8539 $table = new html_table();
8540 $table->head = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
8541 $table->align = array('left', 'center', 'center', 'center', 'center');
8542 $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
8543 $table->data = array();
8544 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8546 $totalenabled = 0;
8547 $count = 0;
8548 foreach ($types as $type) {
8549 if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
8550 $totalenabled++;
8554 foreach ($types as $type) {
8555 $url = new moodle_url('/admin/contentbank.php',
8556 array('sesskey' => sesskey(), 'name' => $type->name));
8558 $class = '';
8559 $strtypename = $type->displayname;
8560 if ($type->is_enabled()) {
8561 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8562 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8563 } else {
8564 $class = 'dimmed_text';
8565 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8566 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8569 $updown = '';
8570 if ($count) {
8571 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8572 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8573 } else {
8574 $updown .= $spacer;
8576 if ($count < count($types) - 1) {
8577 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8578 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8579 } else {
8580 $updown .= $spacer;
8583 $settings = '';
8584 if ($type->get_settings_url()) {
8585 $settings = html_writer::link($type->get_settings_url(), $txt->settings);
8588 $uninstall = '';
8589 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
8590 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8593 $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
8594 if ($class) {
8595 $row->attributes['class'] = $class;
8597 $table->data[] = $row;
8598 $count++;
8600 $return .= html_writer::table($table);
8601 return highlight($query, $return);
8606 * Initialise admin page - this function does require login and permission
8607 * checks specified in page definition.
8609 * This function must be called on each admin page before other code.
8611 * @global moodle_page $PAGE
8613 * @param string $section name of page
8614 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
8615 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
8616 * added to the turn blocks editing on/off form, so this page reloads correctly.
8617 * @param string $actualurl if the actual page being viewed is not the normal one for this
8618 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
8619 * @param array $options Additional options that can be specified for page setup.
8620 * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
8621 * nosearch - Do not display search bar
8623 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
8624 global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
8626 $PAGE->set_context(null); // hack - set context to something, by default to system context
8628 $site = get_site();
8629 require_login(null, false);
8631 if (!empty($options['pagelayout'])) {
8632 // A specific page layout has been requested.
8633 $PAGE->set_pagelayout($options['pagelayout']);
8634 } else if ($section === 'upgradesettings') {
8635 $PAGE->set_pagelayout('maintenance');
8636 } else {
8637 $PAGE->set_pagelayout('admin');
8640 $adminroot = admin_get_root(false, false); // settings not required for external pages
8641 $extpage = $adminroot->locate($section, true);
8643 $hassiteconfig = has_capability('moodle/site:config', context_system::instance());
8644 if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
8645 // The requested section isn't in the admin tree
8646 // It could be because the user has inadequate capapbilities or because the section doesn't exist
8647 if (!$hassiteconfig) {
8648 // The requested section could depend on a different capability
8649 // but most likely the user has inadequate capabilities
8650 throw new \moodle_exception('accessdenied', 'admin');
8651 } else {
8652 throw new \moodle_exception('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
8656 // this eliminates our need to authenticate on the actual pages
8657 if (!$extpage->check_access()) {
8658 throw new \moodle_exception('accessdenied', 'admin');
8659 die;
8662 navigation_node::require_admin_tree();
8664 // $PAGE->set_extra_button($extrabutton); TODO
8666 if (!$actualurl) {
8667 $actualurl = $extpage->url;
8670 $PAGE->set_url($actualurl, $extraurlparams);
8671 if (strpos($PAGE->pagetype, 'admin-') !== 0) {
8672 $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
8675 if (empty($SITE->fullname) || empty($SITE->shortname)) {
8676 // During initial install.
8677 $strinstallation = get_string('installation', 'install');
8678 $strsettings = get_string('settings');
8679 $PAGE->navbar->add($strsettings);
8680 $PAGE->set_title($strinstallation);
8681 $PAGE->set_heading($strinstallation);
8682 $PAGE->set_cacheable(false);
8683 return;
8686 // Locate the current item on the navigation and make it active when found.
8687 $path = $extpage->path;
8688 $node = $PAGE->settingsnav;
8689 while ($node && count($path) > 0) {
8690 $node = $node->get(array_pop($path));
8692 if ($node) {
8693 $node->make_active();
8696 // Normal case.
8697 $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
8698 if ($PAGE->user_allowed_editing() && $adminediting != -1) {
8699 $USER->editing = $adminediting;
8702 if ($PAGE->user_allowed_editing() && !$PAGE->theme->haseditswitch) {
8703 if ($PAGE->user_is_editing()) {
8704 $caption = get_string('blockseditoff');
8705 $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
8706 } else {
8707 $caption = get_string('blocksediton');
8708 $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
8710 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
8713 $PAGE->set_title(implode(moodle_page::TITLE_SEPARATOR, $extpage->visiblepath));
8714 $PAGE->set_heading($SITE->fullname);
8716 if ($hassiteconfig && empty($options['nosearch'])) {
8717 $PAGE->add_header_action($OUTPUT->render_from_template('core_admin/header_search_input', [
8718 'action' => new moodle_url('/admin/search.php'),
8719 'query' => $PAGE->url->get_param('query'),
8720 ]));
8723 // prevent caching in nav block
8724 $PAGE->navigation->clear_cache();
8728 * Returns the reference to admin tree root
8730 * @return object admin_root object
8732 function admin_get_root($reload=false, $requirefulltree=true) {
8733 global $CFG, $DB, $OUTPUT, $ADMIN;
8735 if (is_null($ADMIN)) {
8736 // create the admin tree!
8737 $ADMIN = new admin_root($requirefulltree);
8740 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
8741 $ADMIN->purge_children($requirefulltree);
8744 if (!$ADMIN->loaded) {
8745 // we process this file first to create categories first and in correct order
8746 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
8748 // now we process all other files in admin/settings to build the admin tree
8749 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
8750 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
8751 continue;
8753 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
8754 // plugins are loaded last - they may insert pages anywhere
8755 continue;
8757 require($file);
8759 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
8761 $ADMIN->loaded = true;
8764 return $ADMIN;
8767 /// settings utility functions
8770 * This function applies default settings.
8771 * Because setting the defaults of some settings can enable other settings,
8772 * this function is called recursively until no more new settings are found.
8774 * @param object $node, NULL means complete tree, null by default
8775 * @param bool $unconditional if true overrides all values with defaults, true by default
8776 * @param array $admindefaultsettings default admin settings to apply. Used recursively
8777 * @param array $settingsoutput The names and values of the changed settings. Used recursively
8778 * @return array $settingsoutput The names and values of the changed settings
8780 function admin_apply_default_settings($node=null, $unconditional=true, $admindefaultsettings=array(), $settingsoutput=array()) {
8781 $counter = 0;
8783 // This function relies heavily on config cache, so we need to enable in-memory caches if it
8784 // is used during install when normal caching is disabled.
8785 $token = new \core_cache\allow_temporary_caches();
8787 if (is_null($node)) {
8788 core_plugin_manager::reset_caches();
8789 $node = admin_get_root(true, true);
8790 $counter = count($settingsoutput);
8793 if ($node instanceof admin_category) {
8794 $entries = array_keys($node->children);
8795 foreach ($entries as $entry) {
8796 $settingsoutput = admin_apply_default_settings(
8797 $node->children[$entry], $unconditional, $admindefaultsettings, $settingsoutput
8801 } else if ($node instanceof admin_settingpage) {
8802 foreach ($node->settings as $setting) {
8803 if ($setting->nosave) {
8804 // Not a real setting, must be a heading or description.
8805 continue;
8807 if (!$unconditional && !is_null($setting->get_setting())) {
8808 // Do not override existing defaults.
8809 continue;
8811 $defaultsetting = $setting->get_defaultsetting();
8812 if (is_null($defaultsetting)) {
8813 // No value yet - default maybe applied after admin user creation or in upgradesettings.
8814 continue;
8817 $settingname = $node->name . '_' . $setting->name; // Get a unique name for the setting.
8819 if (!array_key_exists($settingname, $admindefaultsettings)) { // Only update a setting if not already processed.
8820 $admindefaultsettings[$settingname] = $settingname;
8821 $settingsoutput[$settingname] = $defaultsetting;
8823 // Set the default for this setting.
8824 $setting->write_setting($defaultsetting);
8825 $setting->write_setting_flags(null);
8826 } else {
8827 unset($admindefaultsettings[$settingname]); // Remove processed settings.
8832 // Call this function recursively until all settings are processed.
8833 if (($node instanceof admin_root) && ($counter != count($settingsoutput))) {
8834 $settingsoutput = admin_apply_default_settings(null, $unconditional, $admindefaultsettings, $settingsoutput);
8836 // Just in case somebody modifies the list of active plugins directly.
8837 core_plugin_manager::reset_caches();
8839 return $settingsoutput;
8843 * Store changed settings, this function updates the errors variable in $ADMIN
8845 * @param object $formdata from form
8846 * @return int number of changed settings
8848 function admin_write_settings($formdata) {
8849 global $CFG, $SITE, $DB;
8851 $olddbsessions = !empty($CFG->dbsessions);
8852 $formdata = (array)$formdata;
8854 $data = array();
8855 foreach ($formdata as $fullname=>$value) {
8856 if (strpos($fullname, 's_') !== 0) {
8857 continue; // not a config value
8859 $data[$fullname] = $value;
8862 $adminroot = admin_get_root();
8863 $settings = admin_find_write_settings($adminroot, $data);
8865 $count = 0;
8866 foreach ($settings as $fullname=>$setting) {
8867 /** @var $setting admin_setting */
8868 $original = $setting->get_setting();
8869 $error = $setting->write_setting($data[$fullname]);
8870 if ($error !== '') {
8871 $adminroot->errors[$fullname] = new stdClass();
8872 $adminroot->errors[$fullname]->data = $data[$fullname];
8873 $adminroot->errors[$fullname]->id = $setting->get_id();
8874 $adminroot->errors[$fullname]->error = $error;
8875 } else {
8876 $setting->write_setting_flags($data);
8878 if ($setting->post_write_settings($original)) {
8879 $count++;
8883 if ($olddbsessions != !empty($CFG->dbsessions)) {
8884 require_logout();
8887 // Now update $SITE - just update the fields, in case other people have a
8888 // a reference to it (e.g. $PAGE, $COURSE).
8889 $newsite = $DB->get_record('course', array('id'=>$SITE->id));
8890 foreach (get_object_vars($newsite) as $field => $value) {
8891 $SITE->$field = $value;
8894 // now reload all settings - some of them might depend on the changed
8895 admin_get_root(true);
8896 return $count;
8900 * Internal recursive function - finds all settings from submitted form
8902 * @param object $node Instance of admin_category, or admin_settingpage
8903 * @param array $data
8904 * @return array
8906 function admin_find_write_settings($node, $data) {
8907 $return = array();
8909 if (empty($data)) {
8910 return $return;
8913 if ($node instanceof admin_category) {
8914 if ($node->check_access()) {
8915 $entries = array_keys($node->children);
8916 foreach ($entries as $entry) {
8917 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
8921 } else if ($node instanceof admin_settingpage) {
8922 if ($node->check_access()) {
8923 foreach ($node->settings as $setting) {
8924 $fullname = $setting->get_full_name();
8925 if (array_key_exists($fullname, $data)) {
8926 $return[$fullname] = $setting;
8933 return $return;
8937 * Internal function - prints the search results
8939 * @param string $query String to search for
8940 * @return string empty or XHTML
8942 function admin_search_settings_html($query) {
8943 global $CFG, $OUTPUT, $PAGE;
8945 if (core_text::strlen($query) < 2) {
8946 return '';
8948 $query = core_text::strtolower($query);
8950 $adminroot = admin_get_root();
8951 $findings = $adminroot->search($query);
8952 $savebutton = false;
8954 $tpldata = (object) [
8955 'actionurl' => $PAGE->url->out(false),
8956 'results' => [],
8957 'sesskey' => sesskey(),
8960 foreach ($findings as $found) {
8961 $page = $found->page;
8962 $settings = $found->settings;
8963 if ($page->is_hidden()) {
8964 // hidden pages are not displayed in search results
8965 continue;
8968 $heading = highlight($query, $page->visiblename);
8969 $headingurl = null;
8970 if ($page instanceof admin_externalpage) {
8971 $headingurl = new moodle_url($page->url);
8972 } else if ($page instanceof admin_settingpage) {
8973 $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
8974 } else {
8975 continue;
8978 // Locate the page in the admin root and populate its visiblepath attribute.
8979 $path = array();
8980 $located = $adminroot->locate($page->name, true);
8981 if ($located) {
8982 foreach ($located->visiblepath as $pathitem) {
8983 array_unshift($path, (string) $pathitem);
8987 $sectionsettings = [];
8988 if (!empty($settings)) {
8989 foreach ($settings as $setting) {
8990 if (empty($setting->nosave)) {
8991 $savebutton = true;
8993 $fullname = $setting->get_full_name();
8994 if (array_key_exists($fullname, $adminroot->errors)) {
8995 $data = $adminroot->errors[$fullname]->data;
8996 } else {
8997 $data = $setting->get_setting();
8998 // do not use defaults if settings not available - upgradesettings handles the defaults!
9000 $sectionsettings[] = $setting->output_html($data, $query);
9004 $tpldata->results[] = (object) [
9005 'title' => $heading,
9006 'path' => $path,
9007 'url' => $headingurl->out(false),
9008 'settings' => $sectionsettings
9012 $tpldata->showsave = $savebutton;
9013 $tpldata->hasresults = !empty($tpldata->results);
9015 return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
9019 * Internal function - returns arrays of html pages with uninitialised settings
9021 * @param object $node Instance of admin_category or admin_settingpage
9022 * @return array
9024 function admin_output_new_settings_by_page($node) {
9025 global $OUTPUT;
9026 $return = array();
9028 if ($node instanceof admin_category) {
9029 $entries = array_keys($node->children);
9030 foreach ($entries as $entry) {
9031 $return += admin_output_new_settings_by_page($node->children[$entry]);
9034 } else if ($node instanceof admin_settingpage) {
9035 $newsettings = array();
9036 foreach ($node->settings as $setting) {
9037 if (is_null($setting->get_setting())) {
9038 $newsettings[] = $setting;
9041 if (count($newsettings) > 0) {
9042 $adminroot = admin_get_root();
9043 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
9044 $page .= '<fieldset class="adminsettings">'."\n";
9045 foreach ($newsettings as $setting) {
9046 $fullname = $setting->get_full_name();
9047 if (array_key_exists($fullname, $adminroot->errors)) {
9048 $data = $adminroot->errors[$fullname]->data;
9049 } else {
9050 $data = $setting->get_setting();
9051 if (is_null($data)) {
9052 $data = $setting->get_defaultsetting();
9055 $page .= '<div class="clearer"><!-- --></div>'."\n";
9056 $page .= $setting->output_html($data);
9058 $page .= '</fieldset>';
9059 $return[$node->name] = $page;
9063 return $return;
9067 * Format admin settings
9069 * @param object $setting
9070 * @param string $title label element
9071 * @param string $form form fragment, html code - not highlighted automatically
9072 * @param string $description
9073 * @param mixed $label link label to id, true by default or string being the label to connect it to
9074 * @param string $warning warning text
9075 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
9076 * @param string $query search query to be highlighted
9077 * @return string XHTML
9079 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
9080 global $CFG, $OUTPUT;
9082 $context = (object) [
9083 'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
9084 'fullname' => $setting->get_full_name(),
9087 // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
9088 if ($label === true) {
9089 $context->labelfor = $setting->get_id();
9090 } else if ($label === false) {
9091 $context->labelfor = '';
9092 } else {
9093 $context->labelfor = $label;
9096 $form .= $setting->output_setting_flags();
9098 $context->warning = $warning;
9099 $context->override = '';
9100 if (empty($setting->plugin)) {
9101 if ($setting->is_forceable() && array_key_exists($setting->name, $CFG->config_php_settings)) {
9102 $context->override = get_string('configoverride', 'admin');
9104 } else {
9105 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
9106 $context->override = get_string('configoverride', 'admin');
9110 $defaults = array();
9111 if (!is_null($defaultinfo)) {
9112 if ($defaultinfo === '') {
9113 $defaultinfo = get_string('emptysettingvalue', 'admin');
9115 $defaults[] = $defaultinfo;
9118 $context->default = null;
9119 $setting->get_setting_flag_defaults($defaults);
9120 if (!empty($defaults)) {
9121 $defaultinfo = implode(', ', $defaults);
9122 $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
9123 $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
9127 $context->error = '';
9128 $adminroot = admin_get_root();
9129 if (array_key_exists($context->fullname, $adminroot->errors)) {
9130 $context->error = $adminroot->errors[$context->fullname]->error;
9133 if ($dependenton = $setting->get_dependent_on()) {
9134 $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
9137 $context->id = 'admin-' . $setting->name;
9138 $context->title = highlightfast($query, $title);
9139 $context->name = highlightfast($query, $context->name);
9140 $context->description = highlight($query, markdown_to_html($description));
9141 $context->element = $form;
9142 $context->forceltr = $setting->get_force_ltr();
9143 $context->customcontrol = $setting->has_custom_form_control();
9145 return $OUTPUT->render_from_template('core_admin/setting', $context);
9149 * Based on find_new_settings{@link ()} in upgradesettings.php
9150 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
9152 * @param object $node Instance of admin_category, or admin_settingpage
9153 * @return boolean true if any settings haven't been initialised, false if they all have
9155 function any_new_admin_settings($node) {
9157 if ($node instanceof admin_category) {
9158 $entries = array_keys($node->children);
9159 foreach ($entries as $entry) {
9160 if (any_new_admin_settings($node->children[$entry])) {
9161 return true;
9165 } else if ($node instanceof admin_settingpage) {
9166 foreach ($node->settings as $setting) {
9167 if ($setting->get_setting() === NULL) {
9168 return true;
9173 return false;
9177 * Given a table and optionally a column name should replaces be done?
9179 * @param string $table name
9180 * @param string $column name
9181 * @return bool success or fail
9183 function db_should_replace($table, $column = '', $additionalskiptables = ''): bool {
9185 // TODO: this is horrible hack, we should have a hook and each plugin should be responsible for proper replacing...
9186 $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions',
9187 'events_queue', 'repository_instance_config', 'block_instances', 'files'];
9189 // Additional skip tables.
9190 if (!empty($additionalskiptables)) {
9191 $skiptables = array_merge($skiptables, explode(',', str_replace(' ', '', $additionalskiptables)));
9194 // Don't process these.
9195 if (in_array($table, $skiptables)) {
9196 return false;
9199 // To be safe never replace inside a table that looks related to logging.
9200 if (preg_match('/(^|_)logs?($|_)/', $table)) {
9201 return false;
9204 // Do column based exclusions.
9205 if (!empty($column)) {
9206 // Don't touch anything that looks like a hash.
9207 if (preg_match('/hash$/', $column)) {
9208 return false;
9212 return true;
9216 * Moved from admin/replace.php so that we can use this in cron
9218 * @param string $search string to look for
9219 * @param string $replace string to replace
9220 * @return bool success or fail
9222 function db_replace($search, $replace, $additionalskiptables = '') {
9223 global $DB, $CFG, $OUTPUT;
9225 // Turn off time limits, sometimes upgrades can be slow.
9226 core_php_time_limit::raise();
9228 if (!$tables = $DB->get_tables() ) { // No tables yet at all.
9229 return false;
9231 foreach ($tables as $table) {
9233 if (!db_should_replace($table, '', $additionalskiptables)) {
9234 continue;
9237 if ($columns = $DB->get_columns($table)) {
9238 $DB->set_debug(true);
9239 foreach ($columns as $column) {
9240 if (!db_should_replace($table, $column->name)) {
9241 continue;
9243 $DB->replace_all_text($table, $column, $search, $replace);
9245 $DB->set_debug(false);
9249 // delete modinfo caches
9250 rebuild_course_cache(0, true);
9252 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
9253 $blocks = core_component::get_plugin_list('block');
9254 foreach ($blocks as $blockname=>$fullblock) {
9255 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
9256 continue;
9259 if (!is_readable($fullblock.'/lib.php')) {
9260 continue;
9263 $function = 'block_'.$blockname.'_global_db_replace';
9264 include_once($fullblock.'/lib.php');
9265 if (!function_exists($function)) {
9266 continue;
9269 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
9270 $function($search, $replace);
9271 echo $OUTPUT->notification("...finished", 'notifysuccess');
9274 // Trigger an event.
9275 $eventargs = [
9276 'context' => context_system::instance(),
9277 'other' => [
9278 'search' => $search,
9279 'replace' => $replace
9282 $event = \core\event\database_text_field_content_replaced::create($eventargs);
9283 $event->trigger();
9285 purge_all_caches();
9287 return true;
9291 * Manage repository settings
9293 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9295 class admin_setting_managerepository extends admin_setting {
9296 /** @var string */
9297 private $baseurl;
9300 * calls parent::__construct with specific arguments
9302 public function __construct() {
9303 global $CFG;
9304 parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
9305 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
9309 * Always returns true, does nothing
9311 * @return true
9313 public function get_setting() {
9314 return true;
9318 * Always returns true does nothing
9320 * @return true
9322 public function get_defaultsetting() {
9323 return true;
9327 * Always returns s_managerepository
9329 * @return string Always return 's_managerepository'
9331 public function get_full_name() {
9332 return 's_managerepository';
9336 * Always returns '' doesn't do anything
9338 public function write_setting($data) {
9339 $url = $this->baseurl . '&amp;new=' . $data;
9340 return '';
9341 // TODO
9342 // Should not use redirect and exit here
9343 // Find a better way to do this.
9344 // redirect($url);
9345 // exit;
9349 * Searches repository plugins for one that matches $query
9351 * @param string $query The string to search for
9352 * @return bool true if found, false if not
9354 public function is_related($query) {
9355 if (parent::is_related($query)) {
9356 return true;
9359 $repositories= core_component::get_plugin_list('repository');
9360 foreach ($repositories as $p => $dir) {
9361 if (strpos($p, $query) !== false) {
9362 return true;
9365 foreach (repository::get_types() as $instance) {
9366 $title = $instance->get_typename();
9367 if (strpos(core_text::strtolower($title), $query) !== false) {
9368 return true;
9371 return false;
9375 * Helper function that generates a moodle_url object
9376 * relevant to the repository
9379 function repository_action_url($repository) {
9380 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
9384 * Builds XHTML to display the control
9386 * @param string $data Unused
9387 * @param string $query
9388 * @return string XHTML
9390 public function output_html($data, $query='') {
9391 global $CFG, $USER, $OUTPUT;
9393 // Get strings that are used
9394 $strshow = get_string('on', 'repository');
9395 $strhide = get_string('off', 'repository');
9396 $strdelete = get_string('disabled', 'repository');
9398 $actionchoicesforexisting = array(
9399 'show' => $strshow,
9400 'hide' => $strhide,
9401 'delete' => $strdelete
9404 $actionchoicesfornew = array(
9405 'newon' => $strshow,
9406 'newoff' => $strhide,
9407 'delete' => $strdelete
9410 $return = '';
9411 $return .= $OUTPUT->box_start('generalbox');
9413 // Set strings that are used multiple times
9414 $settingsstr = get_string('settings');
9415 $disablestr = get_string('disable');
9417 // Table to list plug-ins
9418 $table = new html_table();
9419 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
9420 $table->align = array('left', 'center', 'center', 'center', 'center');
9421 $table->data = array();
9423 // Get list of used plug-ins
9424 $repositorytypes = repository::get_types();
9425 if (!empty($repositorytypes)) {
9426 // Array to store plugins being used
9427 $alreadyplugins = array();
9428 $totalrepositorytypes = count($repositorytypes);
9429 $updowncount = 1;
9430 foreach ($repositorytypes as $i) {
9431 $settings = '';
9432 $typename = $i->get_typename();
9433 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
9434 $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
9435 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
9437 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
9438 // Calculate number of instances in order to display them for the Moodle administrator
9439 if (!empty($instanceoptionnames)) {
9440 $params = array();
9441 $params['context'] = array(context_system::instance());
9442 $params['onlyvisible'] = false;
9443 $params['type'] = $typename;
9444 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
9445 // site instances
9446 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
9447 $params['context'] = array();
9448 $instances = repository::static_function($typename, 'get_instances', $params);
9449 $courseinstances = array();
9450 $userinstances = array();
9452 foreach ($instances as $instance) {
9453 $repocontext = context::instance_by_id($instance->instance->contextid);
9454 if ($repocontext->contextlevel == CONTEXT_COURSE) {
9455 $courseinstances[] = $instance;
9456 } else if ($repocontext->contextlevel == CONTEXT_USER) {
9457 $userinstances[] = $instance;
9460 // course instances
9461 $instancenumber = count($courseinstances);
9462 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
9464 // user private instances
9465 $instancenumber = count($userinstances);
9466 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
9467 } else {
9468 $admininstancenumbertext = "";
9469 $courseinstancenumbertext = "";
9470 $userinstancenumbertext = "";
9473 $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
9475 $settings .= $OUTPUT->container_start('mdl-left');
9476 $settings .= '<br/>';
9477 $settings .= $admininstancenumbertext;
9478 $settings .= '<br/>';
9479 $settings .= $courseinstancenumbertext;
9480 $settings .= '<br/>';
9481 $settings .= $userinstancenumbertext;
9482 $settings .= $OUTPUT->container_end();
9484 // Get the current visibility
9485 if ($i->get_visible()) {
9486 $currentaction = 'show';
9487 } else {
9488 $currentaction = 'hide';
9491 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
9493 // Display up/down link
9494 $updown = '';
9495 // Should be done with CSS instead.
9496 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
9498 if ($updowncount > 1) {
9499 $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
9500 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
9502 else {
9503 $updown .= $spacer;
9505 if ($updowncount < $totalrepositorytypes) {
9506 $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
9507 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
9509 else {
9510 $updown .= $spacer;
9513 $updowncount++;
9515 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
9517 if (!in_array($typename, $alreadyplugins)) {
9518 $alreadyplugins[] = $typename;
9523 // Get all the plugins that exist on disk
9524 $plugins = core_component::get_plugin_list('repository');
9525 if (!empty($plugins)) {
9526 foreach ($plugins as $plugin => $dir) {
9527 // Check that it has not already been listed
9528 if (!in_array($plugin, $alreadyplugins)) {
9529 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
9530 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
9535 $return .= html_writer::table($table);
9536 $return .= $OUTPUT->box_end();
9537 return highlight($query, $return);
9542 * Special checkbox for enable mobile web service
9543 * If enable then we store the service id of the mobile service into config table
9544 * If disable then we unstore the service id from the config table
9546 class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
9548 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
9549 private $restuse;
9552 * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
9554 * @return boolean
9556 private function is_protocol_cap_allowed() {
9557 global $DB, $CFG;
9559 // If the $this->restuse variable is not set, it needs to be set.
9560 if (empty($this->restuse) and $this->restuse!==false) {
9561 $params = array();
9562 $params['permission'] = CAP_ALLOW;
9563 $params['roleid'] = $CFG->defaultuserroleid;
9564 $params['capability'] = 'webservice/rest:use';
9565 $this->restuse = $DB->record_exists('role_capabilities', $params);
9568 return $this->restuse;
9572 * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
9573 * @param type $status true to allow, false to not set
9575 private function set_protocol_cap($status) {
9576 global $CFG;
9577 if ($status and !$this->is_protocol_cap_allowed()) {
9578 //need to allow the cap
9579 $permission = CAP_ALLOW;
9580 $assign = true;
9581 } else if (!$status and $this->is_protocol_cap_allowed()){
9582 //need to disallow the cap
9583 $permission = CAP_INHERIT;
9584 $assign = true;
9586 if (!empty($assign)) {
9587 $systemcontext = context_system::instance();
9588 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
9593 * Builds XHTML to display the control.
9594 * The main purpose of this overloading is to display a warning when https
9595 * is not supported by the server
9596 * @param string $data Unused
9597 * @param string $query
9598 * @return string XHTML
9600 public function output_html($data, $query='') {
9601 global $OUTPUT;
9602 $html = parent::output_html($data, $query);
9604 if ((string)$data === $this->yes) {
9605 $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
9606 foreach ($notifications as $notification) {
9607 $message = get_string($notification[0], $notification[1]);
9608 $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
9612 return $html;
9616 * Retrieves the current setting using the objects name
9618 * @return string
9620 public function get_setting() {
9621 global $CFG;
9623 // First check if is not set.
9624 $result = $this->config_read($this->name);
9625 if (is_null($result)) {
9626 return null;
9629 // For install cli script, $CFG->defaultuserroleid is not set so return 0
9630 // Or if web services aren't enabled this can't be,
9631 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
9632 return 0;
9635 require_once($CFG->dirroot . '/webservice/lib.php');
9636 $webservicemanager = new webservice();
9637 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9638 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
9639 return $result;
9640 } else {
9641 return 0;
9646 * Save the selected setting
9648 * @param string $data The selected site
9649 * @return string empty string or error message
9651 public function write_setting($data) {
9652 global $DB, $CFG;
9654 //for install cli script, $CFG->defaultuserroleid is not set so do nothing
9655 if (empty($CFG->defaultuserroleid)) {
9656 return '';
9659 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
9661 require_once($CFG->dirroot . '/webservice/lib.php');
9662 $webservicemanager = new webservice();
9664 $updateprotocol = false;
9665 if ((string)$data === $this->yes) {
9666 //code run when enable mobile web service
9667 //enable web service systeme if necessary
9668 set_config('enablewebservices', true);
9670 //enable mobile service
9671 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9672 $mobileservice->enabled = 1;
9673 $webservicemanager->update_external_service($mobileservice);
9675 // Enable REST server.
9676 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9678 if (!in_array('rest', $activeprotocols)) {
9679 $activeprotocols[] = 'rest';
9680 $updateprotocol = true;
9683 if ($updateprotocol) {
9684 set_config('webserviceprotocols', implode(',', $activeprotocols));
9687 // Allow rest:use capability for authenticated user.
9688 $this->set_protocol_cap(true);
9689 } else {
9690 // Disable the mobile service.
9691 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9692 $mobileservice->enabled = 0;
9693 $webservicemanager->update_external_service($mobileservice);
9696 return (parent::write_setting($data));
9701 * Special class for management of external services
9703 * @author Petr Skoda (skodak)
9705 class admin_setting_manageexternalservices extends admin_setting {
9707 * Calls parent::__construct with specific arguments
9709 public function __construct() {
9710 $this->nosave = true;
9711 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
9715 * Always returns true, does nothing
9717 * @return true
9719 public function get_setting() {
9720 return true;
9724 * Always returns true, does nothing
9726 * @return true
9728 public function get_defaultsetting() {
9729 return true;
9733 * Always returns '', does not write anything
9735 * @return string Always returns ''
9737 public function write_setting($data) {
9738 // do not write any setting
9739 return '';
9743 * Checks if $query is one of the available external services
9745 * @param string $query The string to search for
9746 * @return bool Returns true if found, false if not
9748 public function is_related($query) {
9749 global $DB;
9751 if (parent::is_related($query)) {
9752 return true;
9755 $services = $DB->get_records('external_services', array(), 'id, name');
9756 foreach ($services as $service) {
9757 if (strpos(core_text::strtolower($service->name), $query) !== false) {
9758 return true;
9761 return false;
9765 * Builds the XHTML to display the control
9767 * @param string $data Unused
9768 * @param string $query
9769 * @return string
9771 public function output_html($data, $query='') {
9772 global $CFG, $OUTPUT, $DB;
9774 // display strings
9775 $stradministration = get_string('administration');
9776 $stredit = get_string('edit');
9777 $strservice = get_string('externalservice', 'webservice');
9778 $strdelete = get_string('delete');
9779 $strplugin = get_string('plugin', 'admin');
9780 $stradd = get_string('add');
9781 $strfunctions = get_string('functions', 'webservice');
9782 $strusers = get_string('users');
9783 $strserviceusers = get_string('serviceusers', 'webservice');
9785 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
9786 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
9787 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
9789 // built in services
9790 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
9791 $return = "";
9792 if (!empty($services)) {
9793 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
9797 $table = new html_table();
9798 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
9799 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9800 $table->id = 'builtinservices';
9801 $table->attributes['class'] = 'admintable externalservices generaltable';
9802 $table->data = array();
9804 // iterate through auth plugins and add to the display table
9805 foreach ($services as $service) {
9806 $name = $service->name;
9808 // hide/show link
9809 if ($service->enabled) {
9810 $displayname = "<span>$name</span>";
9811 } else {
9812 $displayname = "<span class=\"dimmed_text\">$name</span>";
9815 $plugin = $service->component;
9817 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9819 if ($service->restrictedusers) {
9820 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9821 } else {
9822 $users = get_string('allusers', 'webservice');
9825 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9827 // add a row to the table
9828 $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
9830 $return .= html_writer::table($table);
9833 // Custom services
9834 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
9835 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
9837 $table = new html_table();
9838 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
9839 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9840 $table->id = 'customservices';
9841 $table->attributes['class'] = 'admintable externalservices generaltable';
9842 $table->data = array();
9844 // iterate through auth plugins and add to the display table
9845 foreach ($services as $service) {
9846 $name = $service->name;
9848 // hide/show link
9849 if ($service->enabled) {
9850 $displayname = "<span>$name</span>";
9851 } else {
9852 $displayname = "<span class=\"dimmed_text\">$name</span>";
9855 // delete link
9856 $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
9858 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9860 if ($service->restrictedusers) {
9861 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9862 } else {
9863 $users = get_string('allusers', 'webservice');
9866 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9868 // add a row to the table
9869 $table->data[] = array($displayname, $delete, $functions, $users, $edit);
9871 // add new custom service option
9872 $return .= html_writer::table($table);
9874 $return .= '<br />';
9875 // add a token to the table
9876 $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
9878 return highlight($query, $return);
9883 * Special class for overview of external services
9885 * @author Jerome Mouneyrac
9887 class admin_setting_webservicesoverview extends admin_setting {
9890 * Calls parent::__construct with specific arguments
9892 public function __construct() {
9893 $this->nosave = true;
9894 parent::__construct('webservicesoverviewui',
9895 get_string('webservicesoverview', 'webservice'), '', '');
9899 * Always returns true, does nothing
9901 * @return true
9903 public function get_setting() {
9904 return true;
9908 * Always returns true, does nothing
9910 * @return true
9912 public function get_defaultsetting() {
9913 return true;
9917 * Always returns '', does not write anything
9919 * @return string Always returns ''
9921 public function write_setting($data) {
9922 // do not write any setting
9923 return '';
9927 * Builds the XHTML to display the control
9929 * @param string $data Unused
9930 * @param string $query
9931 * @return string
9933 public function output_html($data, $query='') {
9934 global $CFG, $OUTPUT;
9936 $return = "";
9937 $brtag = html_writer::empty_tag('br');
9939 /// One system controlling Moodle with Token
9940 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
9941 $table = new html_table();
9942 $table->head = array(get_string('step', 'webservice'), get_string('status'),
9943 get_string('description'));
9944 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
9945 $table->id = 'onesystemcontrol';
9946 $table->attributes['class'] = 'admintable wsoverview generaltable';
9947 $table->data = array();
9949 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
9950 . $brtag . $brtag;
9952 /// 1. Enable Web Services
9953 $row = array();
9954 $url = new moodle_url("/admin/search.php?query=enablewebservices");
9955 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
9956 array('href' => $url));
9957 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
9958 if ($CFG->enablewebservices) {
9959 $status = get_string('yes');
9961 $row[1] = $status;
9962 $row[2] = get_string('enablewsdescription', 'webservice');
9963 $table->data[] = $row;
9965 /// 2. Enable protocols
9966 $row = array();
9967 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
9968 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
9969 array('href' => $url));
9970 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
9971 //retrieve activated protocol
9972 $active_protocols = empty($CFG->webserviceprotocols) ?
9973 array() : explode(',', $CFG->webserviceprotocols);
9974 if (!empty($active_protocols)) {
9975 $status = "";
9976 foreach ($active_protocols as $protocol) {
9977 $status .= $protocol . $brtag;
9980 $row[1] = $status;
9981 $row[2] = get_string('enableprotocolsdescription', 'webservice');
9982 $table->data[] = $row;
9984 /// 3. Create user account
9985 $row = array();
9986 $url = new moodle_url("/user/editadvanced.php?id=-1");
9987 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
9988 array('href' => $url));
9989 $row[1] = "";
9990 $row[2] = get_string('createuserdescription', 'webservice');
9991 $table->data[] = $row;
9993 /// 4. Add capability to users
9994 $row = array();
9995 $url = new moodle_url("/admin/roles/check.php?contextid=1");
9996 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
9997 array('href' => $url));
9998 $row[1] = "";
9999 $row[2] = get_string('checkusercapabilitydescription', 'webservice');
10000 $table->data[] = $row;
10002 /// 5. Select a web service
10003 $row = array();
10004 $url = new moodle_url("/admin/settings.php?section=externalservices");
10005 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10006 array('href' => $url));
10007 $row[1] = "";
10008 $row[2] = get_string('createservicedescription', 'webservice');
10009 $table->data[] = $row;
10011 /// 6. Add functions
10012 $row = array();
10013 $url = new moodle_url("/admin/settings.php?section=externalservices");
10014 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10015 array('href' => $url));
10016 $row[1] = "";
10017 $row[2] = get_string('addfunctionsdescription', 'webservice');
10018 $table->data[] = $row;
10020 /// 7. Add the specific user
10021 $row = array();
10022 $url = new moodle_url("/admin/settings.php?section=externalservices");
10023 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
10024 array('href' => $url));
10025 $row[1] = "";
10026 $row[2] = get_string('selectspecificuserdescription', 'webservice');
10027 $table->data[] = $row;
10029 /// 8. Create token for the specific user
10030 $row = array();
10031 $url = new moodle_url('/admin/webservice/tokens.php', ['action' => 'create']);
10032 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
10033 array('href' => $url));
10034 $row[1] = "";
10035 $row[2] = get_string('createtokenforuserdescription', 'webservice');
10036 $table->data[] = $row;
10038 /// 9. Enable the documentation
10039 $row = array();
10040 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
10041 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
10042 array('href' => $url));
10043 $status = '<span class="warning">' . get_string('no') . '</span>';
10044 if ($CFG->enablewsdocumentation) {
10045 $status = get_string('yes');
10047 $row[1] = $status;
10048 $row[2] = get_string('enabledocumentationdescription', 'webservice');
10049 $table->data[] = $row;
10051 /// 10. Test the service
10052 $row = array();
10053 $url = new moodle_url("/admin/webservice/testclient.php");
10054 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10055 array('href' => $url));
10056 $row[1] = "";
10057 $row[2] = get_string('testwithtestclientdescription', 'webservice');
10058 $table->data[] = $row;
10060 $return .= html_writer::table($table);
10062 /// Users as clients with token
10063 $return .= $brtag . $brtag . $brtag;
10064 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
10065 $table = new html_table();
10066 $table->head = array(get_string('step', 'webservice'), get_string('status'),
10067 get_string('description'));
10068 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
10069 $table->id = 'userasclients';
10070 $table->attributes['class'] = 'admintable wsoverview generaltable';
10071 $table->data = array();
10073 $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
10074 $brtag . $brtag;
10076 /// 1. Enable Web Services
10077 $row = array();
10078 $url = new moodle_url("/admin/search.php?query=enablewebservices");
10079 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10080 array('href' => $url));
10081 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
10082 if ($CFG->enablewebservices) {
10083 $status = get_string('yes');
10085 $row[1] = $status;
10086 $row[2] = get_string('enablewsdescription', 'webservice');
10087 $table->data[] = $row;
10089 /// 2. Enable protocols
10090 $row = array();
10091 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10092 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10093 array('href' => $url));
10094 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
10095 //retrieve activated protocol
10096 $active_protocols = empty($CFG->webserviceprotocols) ?
10097 array() : explode(',', $CFG->webserviceprotocols);
10098 if (!empty($active_protocols)) {
10099 $status = "";
10100 foreach ($active_protocols as $protocol) {
10101 $status .= $protocol . $brtag;
10104 $row[1] = $status;
10105 $row[2] = get_string('enableprotocolsdescription', 'webservice');
10106 $table->data[] = $row;
10109 /// 3. Select a web service
10110 $row = array();
10111 $url = new moodle_url("/admin/settings.php?section=externalservices");
10112 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10113 array('href' => $url));
10114 $row[1] = "";
10115 $row[2] = get_string('createserviceforusersdescription', 'webservice');
10116 $table->data[] = $row;
10118 /// 4. Add functions
10119 $row = array();
10120 $url = new moodle_url("/admin/settings.php?section=externalservices");
10121 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10122 array('href' => $url));
10123 $row[1] = "";
10124 $row[2] = get_string('addfunctionsdescription', 'webservice');
10125 $table->data[] = $row;
10127 /// 5. Add capability to users
10128 $row = array();
10129 $url = new moodle_url("/admin/roles/check.php?contextid=1");
10130 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
10131 array('href' => $url));
10132 $row[1] = "";
10133 $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
10134 $table->data[] = $row;
10136 /// 6. Test the service
10137 $row = array();
10138 $url = new moodle_url("/admin/webservice/testclient.php");
10139 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10140 array('href' => $url));
10141 $row[1] = "";
10142 $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
10143 $table->data[] = $row;
10145 $return .= html_writer::table($table);
10147 return highlight($query, $return);
10154 * Special class for web service protocol administration.
10156 * @author Petr Skoda (skodak)
10158 class admin_setting_managewebserviceprotocols extends admin_setting {
10161 * Calls parent::__construct with specific arguments
10163 public function __construct() {
10164 $this->nosave = true;
10165 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
10169 * Always returns true, does nothing
10171 * @return true
10173 public function get_setting() {
10174 return true;
10178 * Always returns true, does nothing
10180 * @return true
10182 public function get_defaultsetting() {
10183 return true;
10187 * Always returns '', does not write anything
10189 * @return string Always returns ''
10191 public function write_setting($data) {
10192 // do not write any setting
10193 return '';
10197 * Checks if $query is one of the available webservices
10199 * @param string $query The string to search for
10200 * @return bool Returns true if found, false if not
10202 public function is_related($query) {
10203 if (parent::is_related($query)) {
10204 return true;
10207 $protocols = core_component::get_plugin_list('webservice');
10208 foreach ($protocols as $protocol=>$location) {
10209 if (strpos($protocol, $query) !== false) {
10210 return true;
10212 $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
10213 if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
10214 return true;
10217 return false;
10221 * Builds the XHTML to display the control
10223 * @param string $data Unused
10224 * @param string $query
10225 * @return string
10227 public function output_html($data, $query='') {
10228 global $CFG, $OUTPUT;
10230 // display strings
10231 $stradministration = get_string('administration');
10232 $strsettings = get_string('settings');
10233 $stredit = get_string('edit');
10234 $strprotocol = get_string('protocol', 'webservice');
10235 $strenable = get_string('enable');
10236 $strdisable = get_string('disable');
10237 $strversion = get_string('version');
10239 $protocols_available = core_component::get_plugin_list('webservice');
10240 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
10241 ksort($protocols_available);
10243 foreach ($activeprotocols as $key => $protocol) {
10244 if (empty($protocols_available[$protocol])) {
10245 unset($activeprotocols[$key]);
10249 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
10250 if (in_array('xmlrpc', $activeprotocols)) {
10251 $notify = new \core\output\notification(get_string('xmlrpcwebserviceenabled', 'admin'),
10252 \core\output\notification::NOTIFY_WARNING);
10253 $return .= $OUTPUT->render($notify);
10255 $return .= $OUTPUT->box_start('generalbox webservicesui');
10257 $table = new html_table();
10258 $table->head = array($strprotocol, $strversion, $strenable, $strsettings);
10259 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
10260 $table->id = 'webserviceprotocols';
10261 $table->attributes['class'] = 'admintable generaltable';
10262 $table->data = array();
10264 // iterate through auth plugins and add to the display table
10265 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
10266 foreach ($protocols_available as $protocol => $location) {
10267 $name = get_string('pluginname', 'webservice_'.$protocol);
10269 $plugin = new stdClass();
10270 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
10271 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
10273 $version = isset($plugin->version) ? $plugin->version : '';
10275 // hide/show link
10276 if (in_array($protocol, $activeprotocols)) {
10277 $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
10278 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
10279 $displayname = "<span>$name</span>";
10280 } else {
10281 $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
10282 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
10283 $displayname = "<span class=\"dimmed_text\">$name</span>";
10286 // settings link
10287 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
10288 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
10289 } else {
10290 $settings = '';
10293 // add a row to the table
10294 $table->data[] = array($displayname, $version, $hideshow, $settings);
10296 $return .= html_writer::table($table);
10297 $return .= get_string('configwebserviceplugins', 'webservice');
10298 $return .= $OUTPUT->box_end();
10300 return highlight($query, $return);
10305 * Colour picker
10307 * @copyright 2010 Sam Hemelryk
10308 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10310 class admin_setting_configcolourpicker extends admin_setting {
10313 * Information for previewing the colour
10315 * @var array|null
10317 protected $previewconfig = null;
10320 * Use default when empty.
10322 protected $usedefaultwhenempty = true;
10326 * @param string $name
10327 * @param string $visiblename
10328 * @param string $description
10329 * @param string $defaultsetting
10330 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
10332 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null,
10333 $usedefaultwhenempty = true) {
10334 $this->previewconfig = $previewconfig;
10335 $this->usedefaultwhenempty = $usedefaultwhenempty;
10336 parent::__construct($name, $visiblename, $description, $defaultsetting);
10337 $this->set_force_ltr(true);
10341 * Return the setting
10343 * @return mixed returns config if successful else null
10345 public function get_setting() {
10346 return $this->config_read($this->name);
10350 * Saves the setting
10352 * @param string $data
10353 * @return bool
10355 public function write_setting($data) {
10356 $data = $this->validate($data);
10357 if ($data === false) {
10358 return get_string('validateerror', 'admin');
10360 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
10364 * Validates the colour that was entered by the user
10366 * @param string $data
10367 * @return string|false
10369 protected function validate($data) {
10371 * List of valid HTML colour names
10373 * @var array
10375 $colornames = array(
10376 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
10377 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
10378 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
10379 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
10380 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
10381 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
10382 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
10383 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
10384 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
10385 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
10386 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
10387 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
10388 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
10389 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
10390 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
10391 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
10392 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
10393 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
10394 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
10395 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
10396 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
10397 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
10398 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
10399 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
10400 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
10401 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
10402 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
10403 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
10404 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
10405 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
10406 'whitesmoke', 'yellow', 'yellowgreen'
10409 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
10410 if (strpos($data, '#')!==0) {
10411 $data = '#'.$data;
10413 return $data;
10414 } else if (in_array(strtolower($data), $colornames)) {
10415 return $data;
10416 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
10417 return $data;
10418 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
10419 return $data;
10420 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
10421 return $data;
10422 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
10423 return $data;
10424 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
10425 return $data;
10426 } else if (empty($data)) {
10427 if ($this->usedefaultwhenempty){
10428 return $this->defaultsetting;
10429 } else {
10430 return '';
10432 } else {
10433 return false;
10438 * Generates the HTML for the setting
10440 * @global moodle_page $PAGE
10441 * @global core_renderer $OUTPUT
10442 * @param string $data
10443 * @param string $query
10445 public function output_html($data, $query = '') {
10446 global $PAGE, $OUTPUT;
10448 $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
10449 $context = (object) [
10450 'id' => $this->get_id(),
10451 'name' => $this->get_full_name(),
10452 'value' => $data,
10453 'icon' => $icon->export_for_template($OUTPUT),
10454 'haspreviewconfig' => !empty($this->previewconfig),
10455 'forceltr' => $this->get_force_ltr(),
10456 'readonly' => $this->is_readonly(),
10459 $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
10460 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
10462 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
10463 $this->get_defaultsetting(), $query);
10470 * Class used for uploading of one file into file storage,
10471 * the file name is stored in config table.
10473 * Please note you need to implement your own '_pluginfile' callback function,
10474 * this setting only stores the file, it does not deal with file serving.
10476 * @copyright 2013 Petr Skoda {@link http://skodak.org}
10477 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10479 class admin_setting_configstoredfile extends admin_setting {
10480 /** @var array file area options - should be one file only */
10481 protected $options;
10482 /** @var string name of the file area */
10483 protected $filearea;
10484 /** @var int intemid */
10485 protected $itemid;
10486 /** @var string used for detection of changes */
10487 protected $oldhashes;
10490 * Create new stored file setting.
10492 * @param string $name low level setting name
10493 * @param string $visiblename human readable setting name
10494 * @param string $description description of setting
10495 * @param mixed $filearea file area for file storage
10496 * @param int $itemid itemid for file storage
10497 * @param array $options file area options
10499 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
10500 parent::__construct($name, $visiblename, $description, '');
10501 $this->filearea = $filearea;
10502 $this->itemid = $itemid;
10503 $this->options = (array)$options;
10504 $this->customcontrol = true;
10508 * Applies defaults and returns all options.
10509 * @return array
10511 protected function get_options() {
10512 global $CFG;
10514 require_once("$CFG->libdir/filelib.php");
10515 require_once("$CFG->dirroot/repository/lib.php");
10516 $defaults = array(
10517 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
10518 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
10519 'context' => context_system::instance());
10520 foreach($this->options as $k => $v) {
10521 $defaults[$k] = $v;
10524 return $defaults;
10527 public function get_setting() {
10528 return $this->config_read($this->name);
10531 public function write_setting($data) {
10532 global $USER;
10534 // Let's not deal with validation here, this is for admins only.
10535 $current = $this->get_setting();
10536 if (empty($data) && $current === null) {
10537 // This will be the case when applying default settings (installation).
10538 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
10539 } else if (!is_number($data)) {
10540 // Draft item id is expected here!
10541 return get_string('errorsetting', 'admin');
10544 $options = $this->get_options();
10545 $fs = get_file_storage();
10546 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10548 $this->oldhashes = null;
10549 if ($current) {
10550 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10551 if ($file = $fs->get_file_by_hash($hash)) {
10552 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
10554 unset($file);
10557 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
10558 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
10559 // But we can safely ignore that if the destination area is empty, so that the user is not prompt
10560 // with an error because the draft area does not exist, as he did not use it.
10561 $usercontext = context_user::instance($USER->id);
10562 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
10563 return get_string('errorsetting', 'admin');
10567 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10568 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
10570 $filepath = '';
10571 if ($files) {
10572 /** @var stored_file $file */
10573 $file = reset($files);
10574 $filepath = $file->get_filepath().$file->get_filename();
10577 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
10580 public function post_write_settings($original) {
10581 $options = $this->get_options();
10582 $fs = get_file_storage();
10583 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10585 $current = $this->get_setting();
10586 $newhashes = null;
10587 if ($current) {
10588 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10589 if ($file = $fs->get_file_by_hash($hash)) {
10590 $newhashes = $file->get_contenthash().$file->get_pathnamehash();
10592 unset($file);
10595 if ($this->oldhashes === $newhashes) {
10596 $this->oldhashes = null;
10597 return false;
10599 $this->oldhashes = null;
10601 $callbackfunction = $this->updatedcallback;
10602 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
10603 $callbackfunction($this->get_full_name());
10605 return true;
10608 public function output_html($data, $query = '') {
10609 global $CFG;
10611 $options = $this->get_options();
10612 $id = $this->get_id();
10613 $elname = $this->get_full_name();
10614 $draftitemid = file_get_submitted_draft_itemid($elname);
10615 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10616 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10618 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
10619 require_once("$CFG->dirroot/lib/form/filemanager.php");
10621 $fmoptions = new stdClass();
10622 $fmoptions->mainfile = $options['mainfile'];
10623 $fmoptions->maxbytes = $options['maxbytes'];
10624 $fmoptions->maxfiles = $options['maxfiles'];
10625 $fmoptions->subdirs = $options['subdirs'];
10626 $fmoptions->accepted_types = $options['accepted_types'];
10627 $fmoptions->return_types = $options['return_types'];
10628 $fmoptions->context = $options['context'];
10629 $fmoptions->areamaxbytes = $options['areamaxbytes'];
10631 $fm = new MoodleQuickForm_filemanager($elname, $this->visiblename, ['id' => $id], $fmoptions);
10632 $fm->setValue($draftitemid);
10634 return format_admin_setting($this, $this->visiblename,
10635 '<div class="form-filemanager" data-fieldtype="filemanager">' . $fm->toHtml() . '</div>',
10636 $this->description, true, '', '', $query);
10642 * Administration interface for user specified regular expressions for device detection.
10644 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10646 class admin_setting_devicedetectregex extends admin_setting {
10649 * Calls parent::__construct with specific args
10651 * @param string $name
10652 * @param string $visiblename
10653 * @param string $description
10654 * @param mixed $defaultsetting
10656 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
10657 global $CFG;
10658 parent::__construct($name, $visiblename, $description, $defaultsetting);
10662 * Return the current setting(s)
10664 * @return array Current settings array
10666 public function get_setting() {
10667 global $CFG;
10669 $config = $this->config_read($this->name);
10670 if (is_null($config)) {
10671 return null;
10674 return $this->prepare_form_data($config);
10678 * Save selected settings
10680 * @param array $data Array of settings to save
10681 * @return bool
10683 public function write_setting($data) {
10684 if (empty($data)) {
10685 $data = array();
10688 if ($this->config_write($this->name, $this->process_form_data($data))) {
10689 return ''; // success
10690 } else {
10691 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
10696 * Return XHTML field(s) for regexes
10698 * @param array $data Array of options to set in HTML
10699 * @return string XHTML string for the fields and wrapping div(s)
10701 public function output_html($data, $query='') {
10702 global $OUTPUT;
10704 $context = (object) [
10705 'expressions' => [],
10706 'name' => $this->get_full_name()
10709 if (empty($data)) {
10710 $looplimit = 1;
10711 } else {
10712 $looplimit = (count($data)/2)+1;
10715 for ($i=0; $i<$looplimit; $i++) {
10717 $expressionname = 'expression'.$i;
10719 if (!empty($data[$expressionname])){
10720 $expression = $data[$expressionname];
10721 } else {
10722 $expression = '';
10725 $valuename = 'value'.$i;
10727 if (!empty($data[$valuename])){
10728 $value = $data[$valuename];
10729 } else {
10730 $value= '';
10733 $context->expressions[] = [
10734 'index' => $i,
10735 'expression' => $expression,
10736 'value' => $value
10740 $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
10742 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
10746 * Converts the string of regexes
10748 * @see self::process_form_data()
10749 * @param $regexes string of regexes
10750 * @return array of form fields and their values
10752 protected function prepare_form_data($regexes) {
10754 $regexes = json_decode($regexes);
10756 $form = array();
10758 $i = 0;
10760 foreach ($regexes as $value => $regex) {
10761 $expressionname = 'expression'.$i;
10762 $valuename = 'value'.$i;
10764 $form[$expressionname] = $regex;
10765 $form[$valuename] = $value;
10766 $i++;
10769 return $form;
10773 * Converts the data from admin settings form into a string of regexes
10775 * @see self::prepare_form_data()
10776 * @param array $data array of admin form fields and values
10777 * @return false|string of regexes
10779 protected function process_form_data(array $form) {
10781 $count = count($form); // number of form field values
10783 if ($count % 2) {
10784 // we must get five fields per expression
10785 return false;
10788 $regexes = array();
10789 for ($i = 0; $i < $count / 2; $i++) {
10790 $expressionname = "expression".$i;
10791 $valuename = "value".$i;
10793 $expression = trim($form['expression'.$i]);
10794 $value = trim($form['value'.$i]);
10796 if (empty($expression)){
10797 continue;
10800 $regexes[$value] = $expression;
10803 $regexes = json_encode($regexes);
10805 return $regexes;
10811 * Multiselect for current modules
10813 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10815 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
10816 private $excludesystem;
10819 * Calls parent::__construct - note array $choices is not required
10821 * @param string $name setting name
10822 * @param string $visiblename localised setting name
10823 * @param string $description setting description
10824 * @param array $defaultsetting a plain array of default module ids
10825 * @param bool $excludesystem If true, excludes modules with 'system' archetype
10827 public function __construct($name, $visiblename, $description, $defaultsetting = array(),
10828 $excludesystem = true) {
10829 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
10830 $this->excludesystem = $excludesystem;
10834 * Loads an array of current module choices
10836 * @return bool always return true
10838 public function load_choices() {
10839 if (is_array($this->choices)) {
10840 return true;
10842 $this->choices = array();
10844 global $CFG, $DB;
10845 $records = $DB->get_records('modules', array('visible'=>1), 'name');
10846 foreach ($records as $record) {
10847 // Exclude modules if the code doesn't exist
10848 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
10849 // Also exclude system modules (if specified)
10850 if (!($this->excludesystem &&
10851 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
10852 MOD_ARCHETYPE_SYSTEM)) {
10853 $this->choices[$record->id] = $record->name;
10857 return true;
10862 * Admin setting to show if a php extension is enabled or not.
10864 * @copyright 2013 Damyon Wiese
10865 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10867 class admin_setting_php_extension_enabled extends admin_setting {
10869 /** @var string The name of the extension to check for */
10870 private $extension;
10873 * Calls parent::__construct with specific arguments
10875 public function __construct($name, $visiblename, $description, $extension) {
10876 $this->extension = $extension;
10877 $this->nosave = true;
10878 parent::__construct($name, $visiblename, $description, '');
10882 * Always returns true, does nothing
10884 * @return true
10886 public function get_setting() {
10887 return true;
10891 * Always returns true, does nothing
10893 * @return true
10895 public function get_defaultsetting() {
10896 return true;
10900 * Always returns '', does not write anything
10902 * @return string Always returns ''
10904 public function write_setting($data) {
10905 // Do not write any setting.
10906 return '';
10910 * Outputs the html for this setting.
10911 * @return string Returns an XHTML string
10913 public function output_html($data, $query='') {
10914 global $OUTPUT;
10916 $o = '';
10917 if (!extension_loaded($this->extension)) {
10918 $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description;
10920 $o .= format_admin_setting($this, $this->visiblename, $warning);
10922 return $o;
10927 * Server timezone setting.
10929 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
10930 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10931 * @author Petr Skoda <petr.skoda@totaralms.com>
10933 class admin_setting_servertimezone extends admin_setting_configselect {
10935 * Constructor.
10937 public function __construct() {
10938 $default = core_date::get_default_php_timezone();
10939 if ($default === 'UTC') {
10940 // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
10941 $default = 'Europe/London';
10944 parent::__construct('timezone',
10945 new lang_string('timezone', 'core_admin'),
10946 new lang_string('configtimezone', 'core_admin'), $default, null);
10950 * Lazy load timezone options.
10951 * @return bool true if loaded, false if error
10953 public function load_choices() {
10954 global $CFG;
10955 if (is_array($this->choices)) {
10956 return true;
10959 $current = isset($CFG->timezone) ? $CFG->timezone : null;
10960 $this->choices = core_date::get_list_of_timezones($current, false);
10961 if ($current == 99) {
10962 // Do not show 99 unless it is current value, we want to get rid of it over time.
10963 $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
10964 core_date::get_default_php_timezone());
10967 return true;
10972 * Forced user timezone setting.
10974 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
10975 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10976 * @author Petr Skoda <petr.skoda@totaralms.com>
10978 class admin_setting_forcetimezone extends admin_setting_configselect {
10980 * Constructor.
10982 public function __construct() {
10983 parent::__construct('forcetimezone',
10984 new lang_string('forcetimezone', 'core_admin'),
10985 new lang_string('helpforcetimezone', 'core_admin'), '99', null);
10989 * Lazy load timezone options.
10990 * @return bool true if loaded, false if error
10992 public function load_choices() {
10993 global $CFG;
10994 if (is_array($this->choices)) {
10995 return true;
10998 $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
10999 $this->choices = core_date::get_list_of_timezones($current, true);
11000 $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
11002 return true;
11008 * Search setup steps info.
11010 * @package core
11011 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
11012 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11014 class admin_setting_searchsetupinfo extends admin_setting {
11017 * Calls parent::__construct with specific arguments
11019 public function __construct() {
11020 $this->nosave = true;
11021 parent::__construct('searchsetupinfo', '', '', '');
11025 * Always returns true, does nothing
11027 * @return true
11029 public function get_setting() {
11030 return true;
11034 * Always returns true, does nothing
11036 * @return true
11038 public function get_defaultsetting() {
11039 return true;
11043 * Always returns '', does not write anything
11045 * @param array $data
11046 * @return string Always returns ''
11048 public function write_setting($data) {
11049 // Do not write any setting.
11050 return '';
11054 * Builds the HTML to display the control
11056 * @param string $data Unused
11057 * @param string $query
11058 * @return string
11060 public function output_html($data, $query='') {
11061 global $CFG, $OUTPUT, $ADMIN;
11063 $return = '';
11064 $brtag = html_writer::empty_tag('br');
11066 $searchareas = \core_search\manager::get_search_areas_list();
11067 $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
11068 $anyindexed = false;
11069 foreach ($searchareas as $areaid => $searcharea) {
11070 list($componentname, $varname) = $searcharea->get_config_var_name();
11071 if (get_config($componentname, $varname . '_indexingstart')) {
11072 $anyindexed = true;
11073 break;
11077 $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
11079 $table = new html_table();
11080 $table->head = array(get_string('step', 'search'), get_string('status'));
11081 $table->colclasses = array('leftalign step', 'leftalign status');
11082 $table->id = 'searchsetup';
11083 $table->attributes['class'] = 'admintable generaltable';
11084 $table->data = array();
11086 $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
11088 // Select a search engine.
11089 $row = array();
11090 $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
11091 $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
11092 array('href' => $url));
11094 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11095 if (!empty($CFG->searchengine)) {
11096 $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
11097 array('class' => 'badge badge-success'));
11100 $row[1] = $status;
11101 $table->data[] = $row;
11103 // Available areas.
11104 $row = array();
11105 $url = new moodle_url('/admin/searchareas.php');
11106 $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
11107 array('href' => $url));
11109 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11110 if ($anyenabled) {
11111 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11114 $row[1] = $status;
11115 $table->data[] = $row;
11117 // Setup search engine.
11118 $row = array();
11119 if (empty($CFG->searchengine)) {
11120 $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
11121 $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11122 } else {
11123 if ($ADMIN->locate('search' . $CFG->searchengine)) {
11124 $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
11125 $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin'));
11126 } else {
11127 $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin');
11130 // Check the engine status.
11131 $searchengine = \core_search\manager::search_engine_instance();
11132 try {
11133 $serverstatus = $searchengine->is_server_ready();
11134 } catch (\moodle_exception $e) {
11135 $serverstatus = $e->getMessage();
11137 if ($serverstatus === true) {
11138 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11139 } else {
11140 $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger'));
11142 $row[1] = $status;
11144 $table->data[] = $row;
11146 // Indexed data.
11147 $row = array();
11148 $url = new moodle_url('/admin/searchareas.php');
11149 $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
11150 if ($anyindexed) {
11151 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11152 } else {
11153 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11155 $row[1] = $status;
11156 $table->data[] = $row;
11158 // Enable global search.
11159 $row = array();
11160 $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
11161 $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
11162 array('href' => $url));
11163 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11164 if (\core_search\manager::is_global_search_enabled()) {
11165 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11167 $row[1] = $status;
11168 $table->data[] = $row;
11170 // Replace front page search.
11171 $row = array();
11172 $url = new moodle_url("/admin/search.php?query=searchincludeallcourses");
11173 $row[0] = '6. ' . html_writer::tag('a', get_string('replacefrontsearch', 'admin'),
11174 array('href' => $url));
11175 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11176 if (\core_search\manager::can_replace_course_search()) {
11177 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11179 $row[1] = $status;
11180 $table->data[] = $row;
11182 $return .= html_writer::table($table);
11184 return highlight($query, $return);
11190 * Used to validate the contents of SCSS code and ensuring they are parsable.
11192 * It does not attempt to detect undefined SCSS variables because it is designed
11193 * to be used without knowledge of other config/scss included.
11195 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11196 * @copyright 2016 Dan Poltawski <dan@moodle.com>
11198 class admin_setting_scsscode extends admin_setting_configtextarea {
11201 * Validate the contents of the SCSS to ensure its parsable. Does not
11202 * attempt to detect undefined scss variables.
11204 * @param string $data The scss code from text field.
11205 * @return mixed bool true for success or string:error on failure.
11207 public function validate($data) {
11208 if (empty($data)) {
11209 return true;
11212 $scss = new core_scss();
11213 try {
11214 $scss->compile($data);
11215 } catch (ScssPhp\ScssPhp\Exception\ParserException $e) {
11216 return get_string('scssinvalid', 'admin', $e->getMessage());
11217 } catch (ScssPhp\ScssPhp\Exception\CompilerException $e) {
11218 // Silently ignore this - it could be a scss variable defined from somewhere
11219 // else which we are not examining here.
11220 return true;
11223 return true;
11229 * Administration setting to define a list of file types.
11231 * @copyright 2016 Jonathon Fowler <fowlerj@usq.edu.au>
11232 * @copyright 2017 David Mudrák <david@moodle.com>
11233 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11235 class admin_setting_filetypes extends admin_setting_configtext {
11237 /** @var array Allow selection from these file types only. */
11238 protected $onlytypes = [];
11240 /** @var bool Allow selection of 'All file types' (will be stored as '*'). */
11241 protected $allowall = true;
11243 /** @var core_form\filetypes_util instance to use as a helper. */
11244 protected $util = null;
11247 * Constructor.
11249 * @param string $name Unique ascii name like 'mycoresetting' or 'myplugin/mysetting'
11250 * @param string $visiblename Localised label of the setting
11251 * @param string $description Localised description of the setting
11252 * @param string $defaultsetting Default setting value.
11253 * @param array $options Setting widget options, an array with optional keys:
11254 * 'onlytypes' => array Allow selection from these file types only; for example ['onlytypes' => ['web_image']].
11255 * 'allowall' => bool Allow to select 'All file types', defaults to true. Does not apply if onlytypes are set.
11257 public function __construct($name, $visiblename, $description, $defaultsetting = '', array $options = []) {
11259 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW);
11261 if (array_key_exists('onlytypes', $options) && is_array($options['onlytypes'])) {
11262 $this->onlytypes = $options['onlytypes'];
11265 if (!$this->onlytypes && array_key_exists('allowall', $options)) {
11266 $this->allowall = (bool)$options['allowall'];
11269 $this->util = new \core_form\filetypes_util();
11273 * Normalize the user's input and write it to the database as comma separated list.
11275 * Comma separated list as a text representation of the array was chosen to
11276 * make this compatible with how the $CFG->courseoverviewfilesext values are stored.
11278 * @param string $data Value submitted by the admin.
11279 * @return string Epty string if all good, error message otherwise.
11281 public function write_setting($data) {
11282 return parent::write_setting(implode(',', $this->util->normalize_file_types($data)));
11286 * Validate data before storage
11288 * @param string $data The setting values provided by the admin
11289 * @return bool|string True if ok, the string if error found
11291 public function validate($data) {
11292 $parentcheck = parent::validate($data);
11293 if ($parentcheck !== true) {
11294 return $parentcheck;
11297 // Check for unknown file types.
11298 if ($unknown = $this->util->get_unknown_file_types($data)) {
11299 return get_string('filetypesunknown', 'core_form', implode(', ', $unknown));
11302 // Check for disallowed file types.
11303 if ($notlisted = $this->util->get_not_listed($data, $this->onlytypes)) {
11304 return get_string('filetypesnotallowed', 'core_form', implode(', ', $notlisted));
11307 return true;
11311 * Return an HTML string for the setting element.
11313 * @param string $data The current setting value
11314 * @param string $query Admin search query to be highlighted
11315 * @return string HTML to be displayed
11317 public function output_html($data, $query='') {
11318 global $OUTPUT, $PAGE;
11320 $default = $this->get_defaultsetting();
11321 $context = (object) [
11322 'id' => $this->get_id(),
11323 'name' => $this->get_full_name(),
11324 'value' => $data,
11325 'descriptions' => $this->util->describe_file_types($data),
11327 $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
11329 $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
11330 $this->get_id(),
11331 $this->visiblename->out(),
11332 $this->onlytypes,
11333 $this->allowall,
11336 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
11340 * Should the values be always displayed in LTR mode?
11342 * We always return true here because these values are not RTL compatible.
11344 * @return bool True because these values are not RTL compatible.
11346 public function get_force_ltr() {
11347 return true;
11352 * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
11354 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11355 * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
11357 class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
11360 * Constructor.
11362 * @param string $name
11363 * @param string $visiblename
11364 * @param string $description
11365 * @param mixed $defaultsetting string or array
11366 * @param mixed $paramtype
11367 * @param string $cols
11368 * @param string $rows
11370 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
11371 $cols = '60', $rows = '8') {
11372 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
11373 // Pre-set force LTR to false.
11374 $this->set_force_ltr(false);
11378 * Validate the content and format of the age of digital consent map to ensure it is parsable.
11380 * @param string $data The age of digital consent map from text field.
11381 * @return mixed bool true for success or string:error on failure.
11383 public function validate($data) {
11384 if (empty($data)) {
11385 return true;
11388 try {
11389 \core_auth\digital_consent::parse_age_digital_consent_map($data);
11390 } catch (\moodle_exception $e) {
11391 return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
11394 return true;
11399 * Selection of plugins that can work as site policy handlers
11401 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11402 * @copyright 2018 Marina Glancy
11404 class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
11407 * Constructor
11408 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11409 * for ones in config_plugins.
11410 * @param string $visiblename localised
11411 * @param string $description long localised info
11412 * @param string $defaultsetting
11414 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11415 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11419 * Lazy-load the available choices for the select box
11421 public function load_choices() {
11422 if (during_initial_install()) {
11423 return false;
11425 if (is_array($this->choices)) {
11426 return true;
11429 $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
11430 $manager = new \core_privacy\local\sitepolicy\manager();
11431 $plugins = $manager->get_all_handlers();
11432 foreach ($plugins as $pname => $unused) {
11433 $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11434 ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
11437 return true;
11442 * Used to validate theme presets code and ensuring they compile well.
11444 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11445 * @copyright 2019 Bas Brands <bas@moodle.com>
11447 class admin_setting_configthemepreset extends admin_setting_configselect {
11449 /** @var string The name of the theme to check for */
11450 private $themename;
11453 * Constructor
11454 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
11455 * or 'myplugin/mysetting' for ones in config_plugins.
11456 * @param string $visiblename localised
11457 * @param string $description long localised info
11458 * @param string|int $defaultsetting
11459 * @param array $choices array of $value=>$label for each selection
11460 * @param string $themename name of theme to check presets for.
11462 public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
11463 $this->themename = $themename;
11464 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
11468 * Write settings if validated
11470 * @param string $data
11471 * @return string
11473 public function write_setting($data) {
11474 $validated = $this->validate($data);
11475 if ($validated !== true) {
11476 return $validated;
11478 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
11482 * Validate the preset file to ensure its parsable.
11484 * @param string $data The preset file chosen.
11485 * @return mixed bool true for success or string:error on failure.
11487 public function validate($data) {
11489 if (in_array($data, ['default.scss', 'plain.scss'])) {
11490 return true;
11493 $fs = get_file_storage();
11494 $theme = theme_config::load($this->themename);
11495 $context = context_system::instance();
11497 // If the preset has not changed there is no need to validate it.
11498 if ($theme->settings->preset == $data) {
11499 return true;
11502 if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
11503 // This operation uses a lot of resources.
11504 raise_memory_limit(MEMORY_EXTRA);
11505 core_php_time_limit::raise(300);
11507 // TODO: MDL-62757 When changing anything in this method please do not forget to check
11508 // if the get_css_content_from_scss() method in class theme_config needs updating too.
11510 $compiler = new core_scss();
11511 $compiler->prepend_raw_scss($theme->get_pre_scss_code());
11512 $compiler->append_raw_scss($presetfile->get_content());
11513 if ($scssproperties = $theme->get_scss_property()) {
11514 $compiler->setImportPaths($scssproperties[0]);
11516 $compiler->append_raw_scss($theme->get_extra_scss_code());
11518 try {
11519 $compiler->to_css();
11520 } catch (Exception $e) {
11521 return get_string('invalidthemepreset', 'admin', $e->getMessage());
11524 // Try to save memory.
11525 $compiler = null;
11526 unset($compiler);
11529 return true;
11534 * Selection of plugins that can work as H5P libraries handlers
11536 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11537 * @copyright 2020 Sara Arjona <sara@moodle.com>
11539 class admin_settings_h5plib_handler_select extends admin_setting_configselect {
11542 * Constructor
11543 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11544 * for ones in config_plugins.
11545 * @param string $visiblename localised
11546 * @param string $description long localised info
11547 * @param string $defaultsetting
11549 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11550 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11554 * Lazy-load the available choices for the select box
11556 public function load_choices() {
11557 if (during_initial_install()) {
11558 return false;
11560 if (is_array($this->choices)) {
11561 return true;
11564 $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
11565 foreach ($this->choices as $name => $class) {
11566 $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11567 ['name' => new lang_string('pluginname', $name), 'component' => $name]);
11570 return true;