Merge branch 'MDL-76680/39' of https://github.com/skodak/moodle into MOODLE_39_STABLE
[moodle.git] / lib / adminlib.php
blob5ab6cab84336422bd8def420e34bc9326d11f48a
1 <?php
2 // This file is part of Moodle - http://moodle.org/
3 //
4 // Moodle is free software: you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation, either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // Moodle is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 // GNU General Public License for more details.
14 // You should have received a copy of the GNU General Public License
15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17 /**
18 * Functions and classes used during installation, upgrades and for admin settings.
20 * ADMIN SETTINGS TREE INTRODUCTION
22 * This file performs the following tasks:
23 * -it defines the necessary objects and interfaces to build the Moodle
24 * admin hierarchy
25 * -it defines the admin_externalpage_setup()
27 * ADMIN_SETTING OBJECTS
29 * Moodle settings are represented by objects that inherit from the admin_setting
30 * class. These objects encapsulate how to read a setting, how to write a new value
31 * to a setting, and how to appropriately display the HTML to modify the setting.
33 * ADMIN_SETTINGPAGE OBJECTS
35 * The admin_setting objects are then grouped into admin_settingpages. The latter
36 * appear in the Moodle admin tree block. All interaction with admin_settingpage
37 * objects is handled by the admin/settings.php file.
39 * ADMIN_EXTERNALPAGE OBJECTS
41 * There are some settings in Moodle that are too complex to (efficiently) handle
42 * with admin_settingpages. (Consider, for example, user management and displaying
43 * lists of users.) In this case, we use the admin_externalpage object. This object
44 * places a link to an external PHP file in the admin tree block.
46 * If you're using an admin_externalpage object for some settings, you can take
47 * advantage of the admin_externalpage_* functions. For example, suppose you wanted
48 * to add a foo.php file into admin. First off, you add the following line to
49 * admin/settings/first.php (at the end of the file) or to some other file in
50 * admin/settings:
51 * <code>
52 * $ADMIN->add('userinterface', new admin_externalpage('foo', get_string('foo'),
53 * $CFG->wwwdir . '/' . '$CFG->admin . '/foo.php', 'some_role_permission'));
54 * </code>
56 * Next, in foo.php, your file structure would resemble the following:
57 * <code>
58 * require(__DIR__.'/../../config.php');
59 * require_once($CFG->libdir.'/adminlib.php');
60 * admin_externalpage_setup('foo');
61 * // functionality like processing form submissions goes here
62 * echo $OUTPUT->header();
63 * // your HTML goes here
64 * echo $OUTPUT->footer();
65 * </code>
67 * The admin_externalpage_setup() function call ensures the user is logged in,
68 * and makes sure that they have the proper role permission to access the page.
69 * It also configures all $PAGE properties needed for navigation.
71 * ADMIN_CATEGORY OBJECTS
73 * Above and beyond all this, we have admin_category objects. These objects
74 * appear as folders in the admin tree block. They contain admin_settingpage's,
75 * admin_externalpage's, and other admin_category's.
77 * OTHER NOTES
79 * admin_settingpage's, admin_externalpage's, and admin_category's all inherit
80 * from part_of_admin_tree (a pseudointerface). This interface insists that
81 * a class has a check_access method for access permissions, a locate method
82 * used to find a specific node in the admin tree and find parent path.
84 * admin_category's inherit from parentable_part_of_admin_tree. This pseudo-
85 * interface ensures that the class implements a recursive add function which
86 * accepts a part_of_admin_tree object and searches for the proper place to
87 * put it. parentable_part_of_admin_tree implies part_of_admin_tree.
89 * Please note that the $this->name field of any part_of_admin_tree must be
90 * UNIQUE throughout the ENTIRE admin tree.
92 * The $this->name field of an admin_setting object (which is *not* part_of_
93 * admin_tree) must be unique on the respective admin_settingpage where it is
94 * used.
96 * Original author: Vincenzo K. Marcovecchio
97 * Maintainer: Petr Skoda
99 * @package core
100 * @subpackage admin
101 * @copyright 1999 onwards Martin Dougiamas http://dougiamas.com
102 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
105 defined('MOODLE_INTERNAL') || die();
107 /// Add libraries
108 require_once($CFG->libdir.'/ddllib.php');
109 require_once($CFG->libdir.'/xmlize.php');
110 require_once($CFG->libdir.'/messagelib.php');
112 define('INSECURE_DATAROOT_WARNING', 1);
113 define('INSECURE_DATAROOT_ERROR', 2);
116 * Automatically clean-up all plugin data and remove the plugin DB tables
118 * NOTE: do not call directly, use new /admin/plugins.php?uninstall=component instead!
120 * @param string $type The plugin type, eg. 'mod', 'qtype', 'workshopgrading' etc.
121 * @param string $name The plugin name, eg. 'forum', 'multichoice', 'accumulative' etc.
122 * @uses global $OUTPUT to produce notices and other messages
123 * @return void
125 function uninstall_plugin($type, $name) {
126 global $CFG, $DB, $OUTPUT;
128 // This may take a long time.
129 core_php_time_limit::raise();
131 // Recursively uninstall all subplugins first.
132 $subplugintypes = core_component::get_plugin_types_with_subplugins();
133 if (isset($subplugintypes[$type])) {
134 $base = core_component::get_plugin_directory($type, $name);
136 $subpluginsfile = "{$base}/db/subplugins.json";
137 if (file_exists($subpluginsfile)) {
138 $subplugins = (array) json_decode(file_get_contents($subpluginsfile))->plugintypes;
139 } else if (file_exists("{$base}/db/subplugins.php")) {
140 debugging('Use of subplugins.php has been deprecated. ' .
141 'Please update your plugin to provide a subplugins.json file instead.',
142 DEBUG_DEVELOPER);
143 $subplugins = [];
144 include("{$base}/db/subplugins.php");
147 if (!empty($subplugins)) {
148 foreach (array_keys($subplugins) as $subplugintype) {
149 $instances = core_component::get_plugin_list($subplugintype);
150 foreach ($instances as $subpluginname => $notusedpluginpath) {
151 uninstall_plugin($subplugintype, $subpluginname);
157 $component = $type . '_' . $name; // eg. 'qtype_multichoice' or 'workshopgrading_accumulative' or 'mod_forum'
159 if ($type === 'mod') {
160 $pluginname = $name; // eg. 'forum'
161 if (get_string_manager()->string_exists('modulename', $component)) {
162 $strpluginname = get_string('modulename', $component);
163 } else {
164 $strpluginname = $component;
167 } else {
168 $pluginname = $component;
169 if (get_string_manager()->string_exists('pluginname', $component)) {
170 $strpluginname = get_string('pluginname', $component);
171 } else {
172 $strpluginname = $component;
176 echo $OUTPUT->heading($pluginname);
178 // Delete all tag areas, collections and instances associated with this plugin.
179 core_tag_area::uninstall($component);
181 // Custom plugin uninstall.
182 $plugindirectory = core_component::get_plugin_directory($type, $name);
183 $uninstalllib = $plugindirectory . '/db/uninstall.php';
184 if (file_exists($uninstalllib)) {
185 require_once($uninstalllib);
186 $uninstallfunction = 'xmldb_' . $pluginname . '_uninstall'; // eg. 'xmldb_workshop_uninstall()'
187 if (function_exists($uninstallfunction)) {
188 // Do not verify result, let plugin complain if necessary.
189 $uninstallfunction();
193 // Specific plugin type cleanup.
194 $plugininfo = core_plugin_manager::instance()->get_plugin_info($component);
195 if ($plugininfo) {
196 $plugininfo->uninstall_cleanup();
197 core_plugin_manager::reset_caches();
199 $plugininfo = null;
201 // perform clean-up task common for all the plugin/subplugin types
203 //delete the web service functions and pre-built services
204 require_once($CFG->dirroot.'/lib/externallib.php');
205 external_delete_descriptions($component);
207 // delete calendar events
208 $DB->delete_records('event', array('modulename' => $pluginname));
209 $DB->delete_records('event', ['component' => $component]);
211 // Delete scheduled tasks.
212 $DB->delete_records('task_scheduled', array('component' => $component));
214 // Delete Inbound Message datakeys.
215 $DB->delete_records_select('messageinbound_datakeys',
216 'handler IN (SELECT id FROM {messageinbound_handlers} WHERE component = ?)', array($component));
218 // Delete Inbound Message handlers.
219 $DB->delete_records('messageinbound_handlers', array('component' => $component));
221 // delete all the logs
222 $DB->delete_records('log', array('module' => $pluginname));
224 // delete log_display information
225 $DB->delete_records('log_display', array('component' => $component));
227 // delete the module configuration records
228 unset_all_config_for_plugin($component);
229 if ($type === 'mod') {
230 unset_all_config_for_plugin($pluginname);
233 // delete message provider
234 message_provider_uninstall($component);
236 // delete the plugin tables
237 $xmldbfilepath = $plugindirectory . '/db/install.xml';
238 drop_plugin_tables($component, $xmldbfilepath, false);
239 if ($type === 'mod' or $type === 'block') {
240 // non-frankenstyle table prefixes
241 drop_plugin_tables($name, $xmldbfilepath, false);
244 // delete the capabilities that were defined by this module
245 capabilities_cleanup($component);
247 // Delete all remaining files in the filepool owned by the component.
248 $fs = get_file_storage();
249 $fs->delete_component_files($component);
251 // Finally purge all caches.
252 purge_all_caches();
254 // Invalidate the hash used for upgrade detections.
255 set_config('allversionshash', '');
257 echo $OUTPUT->notification(get_string('success'), 'notifysuccess');
261 * Returns the version of installed component
263 * @param string $component component name
264 * @param string $source either 'disk' or 'installed' - where to get the version information from
265 * @return string|bool version number or false if the component is not found
267 function get_component_version($component, $source='installed') {
268 global $CFG, $DB;
270 list($type, $name) = core_component::normalize_component($component);
272 // moodle core or a core subsystem
273 if ($type === 'core') {
274 if ($source === 'installed') {
275 if (empty($CFG->version)) {
276 return false;
277 } else {
278 return $CFG->version;
280 } else {
281 if (!is_readable($CFG->dirroot.'/version.php')) {
282 return false;
283 } else {
284 $version = null; //initialize variable for IDEs
285 include($CFG->dirroot.'/version.php');
286 return $version;
291 // activity module
292 if ($type === 'mod') {
293 if ($source === 'installed') {
294 if ($CFG->version < 2013092001.02) {
295 return $DB->get_field('modules', 'version', array('name'=>$name));
296 } else {
297 return get_config('mod_'.$name, 'version');
300 } else {
301 $mods = core_component::get_plugin_list('mod');
302 if (empty($mods[$name]) or !is_readable($mods[$name].'/version.php')) {
303 return false;
304 } else {
305 $plugin = new stdClass();
306 $plugin->version = null;
307 $module = $plugin;
308 include($mods[$name].'/version.php');
309 return $plugin->version;
314 // block
315 if ($type === 'block') {
316 if ($source === 'installed') {
317 if ($CFG->version < 2013092001.02) {
318 return $DB->get_field('block', 'version', array('name'=>$name));
319 } else {
320 return get_config('block_'.$name, 'version');
322 } else {
323 $blocks = core_component::get_plugin_list('block');
324 if (empty($blocks[$name]) or !is_readable($blocks[$name].'/version.php')) {
325 return false;
326 } else {
327 $plugin = new stdclass();
328 include($blocks[$name].'/version.php');
329 return $plugin->version;
334 // all other plugin types
335 if ($source === 'installed') {
336 return get_config($type.'_'.$name, 'version');
337 } else {
338 $plugins = core_component::get_plugin_list($type);
339 if (empty($plugins[$name])) {
340 return false;
341 } else {
342 $plugin = new stdclass();
343 include($plugins[$name].'/version.php');
344 return $plugin->version;
350 * Delete all plugin tables
352 * @param string $name Name of plugin, used as table prefix
353 * @param string $file Path to install.xml file
354 * @param bool $feedback defaults to true
355 * @return bool Always returns true
357 function drop_plugin_tables($name, $file, $feedback=true) {
358 global $CFG, $DB;
360 // first try normal delete
361 if (file_exists($file) and $DB->get_manager()->delete_tables_from_xmldb_file($file)) {
362 return true;
365 // then try to find all tables that start with name and are not in any xml file
366 $used_tables = get_used_table_names();
368 $tables = $DB->get_tables();
370 /// Iterate over, fixing id fields as necessary
371 foreach ($tables as $table) {
372 if (in_array($table, $used_tables)) {
373 continue;
376 if (strpos($table, $name) !== 0) {
377 continue;
380 // found orphan table --> delete it
381 if ($DB->get_manager()->table_exists($table)) {
382 $xmldb_table = new xmldb_table($table);
383 $DB->get_manager()->drop_table($xmldb_table);
387 return true;
391 * Returns names of all known tables == tables that moodle knows about.
393 * @return array Array of lowercase table names
395 function get_used_table_names() {
396 $table_names = array();
397 $dbdirs = get_db_directories();
399 foreach ($dbdirs as $dbdir) {
400 $file = $dbdir.'/install.xml';
402 $xmldb_file = new xmldb_file($file);
404 if (!$xmldb_file->fileExists()) {
405 continue;
408 $loaded = $xmldb_file->loadXMLStructure();
409 $structure = $xmldb_file->getStructure();
411 if ($loaded and $tables = $structure->getTables()) {
412 foreach($tables as $table) {
413 $table_names[] = strtolower($table->getName());
418 return $table_names;
422 * Returns list of all directories where we expect install.xml files
423 * @return array Array of paths
425 function get_db_directories() {
426 global $CFG;
428 $dbdirs = array();
430 /// First, the main one (lib/db)
431 $dbdirs[] = $CFG->libdir.'/db';
433 /// Then, all the ones defined by core_component::get_plugin_types()
434 $plugintypes = core_component::get_plugin_types();
435 foreach ($plugintypes as $plugintype => $pluginbasedir) {
436 if ($plugins = core_component::get_plugin_list($plugintype)) {
437 foreach ($plugins as $plugin => $plugindir) {
438 $dbdirs[] = $plugindir.'/db';
443 return $dbdirs;
447 * Try to obtain or release the cron lock.
448 * @param string $name name of lock
449 * @param int $until timestamp when this lock considered stale, null means remove lock unconditionally
450 * @param bool $ignorecurrent ignore current lock state, usually extend previous lock, defaults to false
451 * @return bool true if lock obtained
453 function set_cron_lock($name, $until, $ignorecurrent=false) {
454 global $DB;
455 if (empty($name)) {
456 debugging("Tried to get a cron lock for a null fieldname");
457 return false;
460 // remove lock by force == remove from config table
461 if (is_null($until)) {
462 set_config($name, null);
463 return true;
466 if (!$ignorecurrent) {
467 // read value from db - other processes might have changed it
468 $value = $DB->get_field('config', 'value', array('name'=>$name));
470 if ($value and $value > time()) {
471 //lock active
472 return false;
476 set_config($name, $until);
477 return true;
481 * Test if and critical warnings are present
482 * @return bool
484 function admin_critical_warnings_present() {
485 global $SESSION;
487 if (!has_capability('moodle/site:config', context_system::instance())) {
488 return 0;
491 if (!isset($SESSION->admin_critical_warning)) {
492 $SESSION->admin_critical_warning = 0;
493 if (is_dataroot_insecure(true) === INSECURE_DATAROOT_ERROR) {
494 $SESSION->admin_critical_warning = 1;
498 return $SESSION->admin_critical_warning;
502 * Detects if float supports at least 10 decimal digits
504 * Detects if float supports at least 10 decimal digits
505 * and also if float-->string conversion works as expected.
507 * @return bool true if problem found
509 function is_float_problem() {
510 $num1 = 2009010200.01;
511 $num2 = 2009010200.02;
513 return ((string)$num1 === (string)$num2 or $num1 === $num2 or $num2 <= (string)$num1);
517 * Try to verify that dataroot is not accessible from web.
519 * Try to verify that dataroot is not accessible from web.
520 * It is not 100% correct but might help to reduce number of vulnerable sites.
521 * Protection from httpd.conf and .htaccess is not detected properly.
523 * @uses INSECURE_DATAROOT_WARNING
524 * @uses INSECURE_DATAROOT_ERROR
525 * @param bool $fetchtest try to test public access by fetching file, default false
526 * @return mixed empty means secure, INSECURE_DATAROOT_ERROR found a critical problem, INSECURE_DATAROOT_WARNING might be problematic
528 function is_dataroot_insecure($fetchtest=false) {
529 global $CFG;
531 $siteroot = str_replace('\\', '/', strrev($CFG->dirroot.'/')); // win32 backslash workaround
533 $rp = preg_replace('|https?://[^/]+|i', '', $CFG->wwwroot, 1);
534 $rp = strrev(trim($rp, '/'));
535 $rp = explode('/', $rp);
536 foreach($rp as $r) {
537 if (strpos($siteroot, '/'.$r.'/') === 0) {
538 $siteroot = substr($siteroot, strlen($r)+1); // moodle web in subdirectory
539 } else {
540 break; // probably alias root
544 $siteroot = strrev($siteroot);
545 $dataroot = str_replace('\\', '/', $CFG->dataroot.'/');
547 if (strpos($dataroot, $siteroot) !== 0) {
548 return false;
551 if (!$fetchtest) {
552 return INSECURE_DATAROOT_WARNING;
555 // now try all methods to fetch a test file using http protocol
557 $httpdocroot = str_replace('\\', '/', strrev($CFG->dirroot.'/'));
558 preg_match('|(https?://[^/]+)|i', $CFG->wwwroot, $matches);
559 $httpdocroot = $matches[1];
560 $datarooturl = $httpdocroot.'/'. substr($dataroot, strlen($siteroot));
561 make_upload_directory('diag');
562 $testfile = $CFG->dataroot.'/diag/public.txt';
563 if (!file_exists($testfile)) {
564 file_put_contents($testfile, 'test file, do not delete');
565 @chmod($testfile, $CFG->filepermissions);
567 $teststr = trim(file_get_contents($testfile));
568 if (empty($teststr)) {
569 // hmm, strange
570 return INSECURE_DATAROOT_WARNING;
573 $testurl = $datarooturl.'/diag/public.txt';
574 if (extension_loaded('curl') and
575 !(stripos(ini_get('disable_functions'), 'curl_init') !== FALSE) and
576 !(stripos(ini_get('disable_functions'), 'curl_setop') !== FALSE) and
577 ($ch = @curl_init($testurl)) !== false) {
578 curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
579 curl_setopt($ch, CURLOPT_HEADER, false);
580 $data = curl_exec($ch);
581 if (!curl_errno($ch)) {
582 $data = trim($data);
583 if ($data === $teststr) {
584 curl_close($ch);
585 return INSECURE_DATAROOT_ERROR;
588 curl_close($ch);
591 if ($data = @file_get_contents($testurl)) {
592 $data = trim($data);
593 if ($data === $teststr) {
594 return INSECURE_DATAROOT_ERROR;
598 preg_match('|https?://([^/]+)|i', $testurl, $matches);
599 $sitename = $matches[1];
600 $error = 0;
601 if ($fp = @fsockopen($sitename, 80, $error)) {
602 preg_match('|https?://[^/]+(.*)|i', $testurl, $matches);
603 $localurl = $matches[1];
604 $out = "GET $localurl HTTP/1.1\r\n";
605 $out .= "Host: $sitename\r\n";
606 $out .= "Connection: Close\r\n\r\n";
607 fwrite($fp, $out);
608 $data = '';
609 $incoming = false;
610 while (!feof($fp)) {
611 if ($incoming) {
612 $data .= fgets($fp, 1024);
613 } else if (@fgets($fp, 1024) === "\r\n") {
614 $incoming = true;
617 fclose($fp);
618 $data = trim($data);
619 if ($data === $teststr) {
620 return INSECURE_DATAROOT_ERROR;
624 return INSECURE_DATAROOT_WARNING;
628 * Enables CLI maintenance mode by creating new dataroot/climaintenance.html file.
630 function enable_cli_maintenance_mode() {
631 global $CFG;
633 if (file_exists("$CFG->dataroot/climaintenance.html")) {
634 unlink("$CFG->dataroot/climaintenance.html");
637 if (isset($CFG->maintenance_message) and !html_is_blank($CFG->maintenance_message)) {
638 $data = $CFG->maintenance_message;
639 $data = bootstrap_renderer::early_error_content($data, null, null, null);
640 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
642 } else if (file_exists("$CFG->dataroot/climaintenance.template.html")) {
643 $data = file_get_contents("$CFG->dataroot/climaintenance.template.html");
645 } else {
646 $data = get_string('sitemaintenance', 'admin');
647 $data = bootstrap_renderer::early_error_content($data, null, null, null);
648 $data = bootstrap_renderer::plain_page(get_string('sitemaintenance', 'admin'), $data);
651 file_put_contents("$CFG->dataroot/climaintenance.html", $data);
652 chmod("$CFG->dataroot/climaintenance.html", $CFG->filepermissions);
655 /// CLASS DEFINITIONS /////////////////////////////////////////////////////////
659 * Interface for anything appearing in the admin tree
661 * The interface that is implemented by anything that appears in the admin tree
662 * block. It forces inheriting classes to define a method for checking user permissions
663 * and methods for finding something in the admin tree.
665 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
667 interface part_of_admin_tree {
670 * Finds a named part_of_admin_tree.
672 * Used to find a part_of_admin_tree. If a class only inherits part_of_admin_tree
673 * and not parentable_part_of_admin_tree, then this function should only check if
674 * $this->name matches $name. If it does, it should return a reference to $this,
675 * otherwise, it should return a reference to NULL.
677 * If a class inherits parentable_part_of_admin_tree, this method should be called
678 * recursively on all child objects (assuming, of course, the parent object's name
679 * doesn't match the search criterion).
681 * @param string $name The internal name of the part_of_admin_tree we're searching for.
682 * @return mixed An object reference or a NULL reference.
684 public function locate($name);
687 * Removes named part_of_admin_tree.
689 * @param string $name The internal name of the part_of_admin_tree we want to remove.
690 * @return bool success.
692 public function prune($name);
695 * Search using query
696 * @param string $query
697 * @return mixed array-object structure of found settings and pages
699 public function search($query);
702 * Verifies current user's access to this part_of_admin_tree.
704 * Used to check if the current user has access to this part of the admin tree or
705 * not. If a class only inherits part_of_admin_tree and not parentable_part_of_admin_tree,
706 * then this method is usually just a call to has_capability() in the site context.
708 * If a class inherits parentable_part_of_admin_tree, this method should return the
709 * logical OR of the return of check_access() on all child objects.
711 * @return bool True if the user has access, false if she doesn't.
713 public function check_access();
716 * Mostly useful for removing of some parts of the tree in admin tree block.
718 * @return True is hidden from normal list view
720 public function is_hidden();
723 * Show we display Save button at the page bottom?
724 * @return bool
726 public function show_save();
731 * Interface implemented by any part_of_admin_tree that has children.
733 * The interface implemented by any part_of_admin_tree that can be a parent
734 * to other part_of_admin_tree's. (For now, this only includes admin_category.) Apart
735 * from ensuring part_of_admin_tree compliancy, it also ensures inheriting methods
736 * include an add method for adding other part_of_admin_tree objects as children.
738 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
740 interface parentable_part_of_admin_tree extends part_of_admin_tree {
743 * Adds a part_of_admin_tree object to the admin tree.
745 * Used to add a part_of_admin_tree object to this object or a child of this
746 * object. $something should only be added if $destinationname matches
747 * $this->name. If it doesn't, add should be called on child objects that are
748 * also parentable_part_of_admin_tree's.
750 * $something should be appended as the last child in the $destinationname. If the
751 * $beforesibling is specified, $something should be prepended to it. If the given
752 * sibling is not found, $something should be appended to the end of $destinationname
753 * and a developer debugging message should be displayed.
755 * @param string $destinationname The internal name of the new parent for $something.
756 * @param part_of_admin_tree $something The object to be added.
757 * @return bool True on success, false on failure.
759 public function add($destinationname, $something, $beforesibling = null);
765 * The object used to represent folders (a.k.a. categories) in the admin tree block.
767 * Each admin_category object contains a number of part_of_admin_tree objects.
769 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
771 class admin_category implements parentable_part_of_admin_tree {
773 /** @var part_of_admin_tree[] An array of part_of_admin_tree objects that are this object's children */
774 protected $children;
775 /** @var string An internal name for this category. Must be unique amongst ALL part_of_admin_tree objects */
776 public $name;
777 /** @var string The displayed name for this category. Usually obtained through get_string() */
778 public $visiblename;
779 /** @var bool Should this category be hidden in admin tree block? */
780 public $hidden;
781 /** @var mixed Either a string or an array or strings */
782 public $path;
783 /** @var mixed Either a string or an array or strings */
784 public $visiblepath;
786 /** @var array fast lookup category cache, all categories of one tree point to one cache */
787 protected $category_cache;
789 /** @var bool If set to true children will be sorted when calling {@link admin_category::get_children()} */
790 protected $sort = false;
791 /** @var bool If set to true children will be sorted in ascending order. */
792 protected $sortasc = true;
793 /** @var bool If set to true sub categories and pages will be split and then sorted.. */
794 protected $sortsplit = true;
795 /** @var bool $sorted True if the children have been sorted and don't need resorting */
796 protected $sorted = false;
799 * Constructor for an empty admin category
801 * @param string $name The internal name for this category. Must be unique amongst ALL part_of_admin_tree objects
802 * @param string $visiblename The displayed named for this category. Usually obtained through get_string()
803 * @param bool $hidden hide category in admin tree block, defaults to false
805 public function __construct($name, $visiblename, $hidden=false) {
806 $this->children = array();
807 $this->name = $name;
808 $this->visiblename = $visiblename;
809 $this->hidden = $hidden;
813 * Returns a reference to the part_of_admin_tree object with internal name $name.
815 * @param string $name The internal name of the object we want.
816 * @param bool $findpath initialize path and visiblepath arrays
817 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
818 * defaults to false
820 public function locate($name, $findpath=false) {
821 if (!isset($this->category_cache[$this->name])) {
822 // somebody much have purged the cache
823 $this->category_cache[$this->name] = $this;
826 if ($this->name == $name) {
827 if ($findpath) {
828 $this->visiblepath[] = $this->visiblename;
829 $this->path[] = $this->name;
831 return $this;
834 // quick category lookup
835 if (!$findpath and isset($this->category_cache[$name])) {
836 return $this->category_cache[$name];
839 $return = NULL;
840 foreach($this->children as $childid=>$unused) {
841 if ($return = $this->children[$childid]->locate($name, $findpath)) {
842 break;
846 if (!is_null($return) and $findpath) {
847 $return->visiblepath[] = $this->visiblename;
848 $return->path[] = $this->name;
851 return $return;
855 * Search using query
857 * @param string query
858 * @return mixed array-object structure of found settings and pages
860 public function search($query) {
861 $result = array();
862 foreach ($this->get_children() as $child) {
863 $subsearch = $child->search($query);
864 if (!is_array($subsearch)) {
865 debugging('Incorrect search result from '.$child->name);
866 continue;
868 $result = array_merge($result, $subsearch);
870 return $result;
874 * Removes part_of_admin_tree object with internal name $name.
876 * @param string $name The internal name of the object we want to remove.
877 * @return bool success
879 public function prune($name) {
881 if ($this->name == $name) {
882 return false; //can not remove itself
885 foreach($this->children as $precedence => $child) {
886 if ($child->name == $name) {
887 // clear cache and delete self
888 while($this->category_cache) {
889 // delete the cache, but keep the original array address
890 array_pop($this->category_cache);
892 unset($this->children[$precedence]);
893 return true;
894 } else if ($this->children[$precedence]->prune($name)) {
895 return true;
898 return false;
902 * Adds a part_of_admin_tree to a child or grandchild (or great-grandchild, and so forth) of this object.
904 * By default the new part of the tree is appended as the last child of the parent. You
905 * can specify a sibling node that the new part should be prepended to. If the given
906 * sibling is not found, the part is appended to the end (as it would be by default) and
907 * a developer debugging message is displayed.
909 * @throws coding_exception if the $beforesibling is empty string or is not string at all.
910 * @param string $destinationame The internal name of the immediate parent that we want for $something.
911 * @param mixed $something A part_of_admin_tree or setting instance to be added.
912 * @param string $beforesibling The name of the parent's child the $something should be prepended to.
913 * @return bool True if successfully added, false if $something can not be added.
915 public function add($parentname, $something, $beforesibling = null) {
916 global $CFG;
918 $parent = $this->locate($parentname);
919 if (is_null($parent)) {
920 debugging('parent does not exist!');
921 return false;
924 if ($something instanceof part_of_admin_tree) {
925 if (!($parent instanceof parentable_part_of_admin_tree)) {
926 debugging('error - parts of tree can be inserted only into parentable parts');
927 return false;
929 if ($CFG->debugdeveloper && !is_null($this->locate($something->name))) {
930 // The name of the node is already used, simply warn the developer that this should not happen.
931 // It is intentional to check for the debug level before performing the check.
932 debugging('Duplicate admin page name: ' . $something->name, DEBUG_DEVELOPER);
934 if (is_null($beforesibling)) {
935 // Append $something as the parent's last child.
936 $parent->children[] = $something;
937 } else {
938 if (!is_string($beforesibling) or trim($beforesibling) === '') {
939 throw new coding_exception('Unexpected value of the beforesibling parameter');
941 // Try to find the position of the sibling.
942 $siblingposition = null;
943 foreach ($parent->children as $childposition => $child) {
944 if ($child->name === $beforesibling) {
945 $siblingposition = $childposition;
946 break;
949 if (is_null($siblingposition)) {
950 debugging('Sibling '.$beforesibling.' not found', DEBUG_DEVELOPER);
951 $parent->children[] = $something;
952 } else {
953 $parent->children = array_merge(
954 array_slice($parent->children, 0, $siblingposition),
955 array($something),
956 array_slice($parent->children, $siblingposition)
960 if ($something instanceof admin_category) {
961 if (isset($this->category_cache[$something->name])) {
962 debugging('Duplicate admin category name: '.$something->name);
963 } else {
964 $this->category_cache[$something->name] = $something;
965 $something->category_cache =& $this->category_cache;
966 foreach ($something->children as $child) {
967 // just in case somebody already added subcategories
968 if ($child instanceof admin_category) {
969 if (isset($this->category_cache[$child->name])) {
970 debugging('Duplicate admin category name: '.$child->name);
971 } else {
972 $this->category_cache[$child->name] = $child;
973 $child->category_cache =& $this->category_cache;
979 return true;
981 } else {
982 debugging('error - can not add this element');
983 return false;
989 * Checks if the user has access to anything in this category.
991 * @return bool True if the user has access to at least one child in this category, false otherwise.
993 public function check_access() {
994 foreach ($this->children as $child) {
995 if ($child->check_access()) {
996 return true;
999 return false;
1003 * Is this category hidden in admin tree block?
1005 * @return bool True if hidden
1007 public function is_hidden() {
1008 return $this->hidden;
1012 * Show we display Save button at the page bottom?
1013 * @return bool
1015 public function show_save() {
1016 foreach ($this->children as $child) {
1017 if ($child->show_save()) {
1018 return true;
1021 return false;
1025 * Sets sorting on this category.
1027 * Please note this function doesn't actually do the sorting.
1028 * It can be called anytime.
1029 * Sorting occurs when the user calls get_children.
1030 * Code using the children array directly won't see the sorted results.
1032 * @param bool $sort If set to true children will be sorted, if false they won't be.
1033 * @param bool $asc If true sorting will be ascending, otherwise descending.
1034 * @param bool $split If true we sort pages and sub categories separately.
1036 public function set_sorting($sort, $asc = true, $split = true) {
1037 $this->sort = (bool)$sort;
1038 $this->sortasc = (bool)$asc;
1039 $this->sortsplit = (bool)$split;
1043 * Returns the children associated with this category.
1045 * @return part_of_admin_tree[]
1047 public function get_children() {
1048 // If we should sort and it hasn't already been sorted.
1049 if ($this->sort && !$this->sorted) {
1050 if ($this->sortsplit) {
1051 $categories = array();
1052 $pages = array();
1053 foreach ($this->children as $child) {
1054 if ($child instanceof admin_category) {
1055 $categories[] = $child;
1056 } else {
1057 $pages[] = $child;
1060 core_collator::asort_objects_by_property($categories, 'visiblename');
1061 core_collator::asort_objects_by_property($pages, 'visiblename');
1062 if (!$this->sortasc) {
1063 $categories = array_reverse($categories);
1064 $pages = array_reverse($pages);
1066 $this->children = array_merge($pages, $categories);
1067 } else {
1068 core_collator::asort_objects_by_property($this->children, 'visiblename');
1069 if (!$this->sortasc) {
1070 $this->children = array_reverse($this->children);
1073 $this->sorted = true;
1075 return $this->children;
1079 * Magically gets a property from this object.
1081 * @param $property
1082 * @return part_of_admin_tree[]
1083 * @throws coding_exception
1085 public function __get($property) {
1086 if ($property === 'children') {
1087 return $this->get_children();
1089 throw new coding_exception('Invalid property requested.');
1093 * Magically sets a property against this object.
1095 * @param string $property
1096 * @param mixed $value
1097 * @throws coding_exception
1099 public function __set($property, $value) {
1100 if ($property === 'children') {
1101 $this->sorted = false;
1102 $this->children = $value;
1103 } else {
1104 throw new coding_exception('Invalid property requested.');
1109 * Checks if an inaccessible property is set.
1111 * @param string $property
1112 * @return bool
1113 * @throws coding_exception
1115 public function __isset($property) {
1116 if ($property === 'children') {
1117 return isset($this->children);
1119 throw new coding_exception('Invalid property requested.');
1125 * Root of admin settings tree, does not have any parent.
1127 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1129 class admin_root extends admin_category {
1130 /** @var array List of errors */
1131 public $errors;
1132 /** @var string search query */
1133 public $search;
1134 /** @var bool full tree flag - true means all settings required, false only pages required */
1135 public $fulltree;
1136 /** @var bool flag indicating loaded tree */
1137 public $loaded;
1138 /** @var mixed site custom defaults overriding defaults in settings files*/
1139 public $custom_defaults;
1142 * @param bool $fulltree true means all settings required,
1143 * false only pages required
1145 public function __construct($fulltree) {
1146 global $CFG;
1148 parent::__construct('root', get_string('administration'), false);
1149 $this->errors = array();
1150 $this->search = '';
1151 $this->fulltree = $fulltree;
1152 $this->loaded = false;
1154 $this->category_cache = array();
1156 // load custom defaults if found
1157 $this->custom_defaults = null;
1158 $defaultsfile = "$CFG->dirroot/local/defaults.php";
1159 if (is_readable($defaultsfile)) {
1160 $defaults = array();
1161 include($defaultsfile);
1162 if (is_array($defaults) and count($defaults)) {
1163 $this->custom_defaults = $defaults;
1169 * Empties children array, and sets loaded to false
1171 * @param bool $requirefulltree
1173 public function purge_children($requirefulltree) {
1174 $this->children = array();
1175 $this->fulltree = ($requirefulltree || $this->fulltree);
1176 $this->loaded = false;
1177 //break circular dependencies - this helps PHP 5.2
1178 while($this->category_cache) {
1179 array_pop($this->category_cache);
1181 $this->category_cache = array();
1187 * Links external PHP pages into the admin tree.
1189 * See detailed usage example at the top of this document (adminlib.php)
1191 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1193 class admin_externalpage implements part_of_admin_tree {
1195 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1196 public $name;
1198 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1199 public $visiblename;
1201 /** @var string The external URL that we should link to when someone requests this external page. */
1202 public $url;
1204 /** @var array The role capability/permission a user must have to access this external page. */
1205 public $req_capability;
1207 /** @var object The context in which capability/permission should be checked, default is site context. */
1208 public $context;
1210 /** @var bool hidden in admin tree block. */
1211 public $hidden;
1213 /** @var mixed either string or array of string */
1214 public $path;
1216 /** @var array list of visible names of page parents */
1217 public $visiblepath;
1220 * Constructor for adding an external page into the admin tree.
1222 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1223 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1224 * @param string $url The external URL that we should link to when someone requests this external page.
1225 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1226 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1227 * @param stdClass $context The context the page relates to. Not sure what happens
1228 * if you specify something other than system or front page. Defaults to system.
1230 public function __construct($name, $visiblename, $url, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1231 $this->name = $name;
1232 $this->visiblename = $visiblename;
1233 $this->url = $url;
1234 if (is_array($req_capability)) {
1235 $this->req_capability = $req_capability;
1236 } else {
1237 $this->req_capability = array($req_capability);
1239 $this->hidden = $hidden;
1240 $this->context = $context;
1244 * Returns a reference to the part_of_admin_tree object with internal name $name.
1246 * @param string $name The internal name of the object we want.
1247 * @param bool $findpath defaults to false
1248 * @return mixed A reference to the object with internal name $name if found, otherwise a reference to NULL.
1250 public function locate($name, $findpath=false) {
1251 if ($this->name == $name) {
1252 if ($findpath) {
1253 $this->visiblepath = array($this->visiblename);
1254 $this->path = array($this->name);
1256 return $this;
1257 } else {
1258 $return = NULL;
1259 return $return;
1264 * This function always returns false, required function by interface
1266 * @param string $name
1267 * @return false
1269 public function prune($name) {
1270 return false;
1274 * Search using query
1276 * @param string $query
1277 * @return mixed array-object structure of found settings and pages
1279 public function search($query) {
1280 $found = false;
1281 if (strpos(strtolower($this->name), $query) !== false) {
1282 $found = true;
1283 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1284 $found = true;
1286 if ($found) {
1287 $result = new stdClass();
1288 $result->page = $this;
1289 $result->settings = array();
1290 return array($this->name => $result);
1291 } else {
1292 return array();
1297 * Determines if the current user has access to this external page based on $this->req_capability.
1299 * @return bool True if user has access, false otherwise.
1301 public function check_access() {
1302 global $CFG;
1303 $context = empty($this->context) ? context_system::instance() : $this->context;
1304 foreach($this->req_capability as $cap) {
1305 if (has_capability($cap, $context)) {
1306 return true;
1309 return false;
1313 * Is this external page hidden in admin tree block?
1315 * @return bool True if hidden
1317 public function is_hidden() {
1318 return $this->hidden;
1322 * Show we display Save button at the page bottom?
1323 * @return bool
1325 public function show_save() {
1326 return false;
1331 * Used to store details of the dependency between two settings elements.
1333 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1334 * @copyright 2017 Davo Smith, Synergy Learning
1336 class admin_settingdependency {
1337 /** @var string the name of the setting to be shown/hidden */
1338 public $settingname;
1339 /** @var string the setting this is dependent on */
1340 public $dependenton;
1341 /** @var string the condition to show/hide the element */
1342 public $condition;
1343 /** @var string the value to compare against */
1344 public $value;
1346 /** @var string[] list of valid conditions */
1347 private static $validconditions = ['checked', 'notchecked', 'noitemselected', 'eq', 'neq', 'in'];
1350 * admin_settingdependency constructor.
1351 * @param string $settingname
1352 * @param string $dependenton
1353 * @param string $condition
1354 * @param string $value
1355 * @throws \coding_exception
1357 public function __construct($settingname, $dependenton, $condition, $value) {
1358 $this->settingname = $this->parse_name($settingname);
1359 $this->dependenton = $this->parse_name($dependenton);
1360 $this->condition = $condition;
1361 $this->value = $value;
1363 if (!in_array($this->condition, self::$validconditions)) {
1364 throw new coding_exception("Invalid condition '$condition'");
1369 * Convert the setting name into the form field name.
1370 * @param string $name
1371 * @return string
1373 private function parse_name($name) {
1374 $bits = explode('/', $name);
1375 $name = array_pop($bits);
1376 $plugin = '';
1377 if ($bits) {
1378 $plugin = array_pop($bits);
1379 if ($plugin === 'moodle') {
1380 $plugin = '';
1383 return 's_'.$plugin.'_'.$name;
1387 * Gather together all the dependencies in a format suitable for initialising javascript
1388 * @param admin_settingdependency[] $dependencies
1389 * @return array
1391 public static function prepare_for_javascript($dependencies) {
1392 $result = [];
1393 foreach ($dependencies as $d) {
1394 if (!isset($result[$d->dependenton])) {
1395 $result[$d->dependenton] = [];
1397 if (!isset($result[$d->dependenton][$d->condition])) {
1398 $result[$d->dependenton][$d->condition] = [];
1400 if (!isset($result[$d->dependenton][$d->condition][$d->value])) {
1401 $result[$d->dependenton][$d->condition][$d->value] = [];
1403 $result[$d->dependenton][$d->condition][$d->value][] = $d->settingname;
1405 return $result;
1410 * Used to group a number of admin_setting objects into a page and add them to the admin tree.
1412 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1414 class admin_settingpage implements part_of_admin_tree {
1416 /** @var string An internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects */
1417 public $name;
1419 /** @var string The displayed name for this external page. Usually obtained through get_string(). */
1420 public $visiblename;
1422 /** @var mixed An array of admin_setting objects that are part of this setting page. */
1423 public $settings;
1425 /** @var admin_settingdependency[] list of settings to hide when certain conditions are met */
1426 protected $dependencies = [];
1428 /** @var array The role capability/permission a user must have to access this external page. */
1429 public $req_capability;
1431 /** @var object The context in which capability/permission should be checked, default is site context. */
1432 public $context;
1434 /** @var bool hidden in admin tree block. */
1435 public $hidden;
1437 /** @var mixed string of paths or array of strings of paths */
1438 public $path;
1440 /** @var array list of visible names of page parents */
1441 public $visiblepath;
1444 * see admin_settingpage for details of this function
1446 * @param string $name The internal name for this external page. Must be unique amongst ALL part_of_admin_tree objects.
1447 * @param string $visiblename The displayed name for this external page. Usually obtained through get_string().
1448 * @param mixed $req_capability The role capability/permission a user must have to access this external page. Defaults to 'moodle/site:config'.
1449 * @param boolean $hidden Is this external page hidden in admin tree block? Default false.
1450 * @param stdClass $context The context the page relates to. Not sure what happens
1451 * if you specify something other than system or front page. Defaults to system.
1453 public function __construct($name, $visiblename, $req_capability='moodle/site:config', $hidden=false, $context=NULL) {
1454 $this->settings = new stdClass();
1455 $this->name = $name;
1456 $this->visiblename = $visiblename;
1457 if (is_array($req_capability)) {
1458 $this->req_capability = $req_capability;
1459 } else {
1460 $this->req_capability = array($req_capability);
1462 $this->hidden = $hidden;
1463 $this->context = $context;
1467 * see admin_category
1469 * @param string $name
1470 * @param bool $findpath
1471 * @return mixed Object (this) if name == this->name, else returns null
1473 public function locate($name, $findpath=false) {
1474 if ($this->name == $name) {
1475 if ($findpath) {
1476 $this->visiblepath = array($this->visiblename);
1477 $this->path = array($this->name);
1479 return $this;
1480 } else {
1481 $return = NULL;
1482 return $return;
1487 * Search string in settings page.
1489 * @param string $query
1490 * @return array
1492 public function search($query) {
1493 $found = array();
1495 foreach ($this->settings as $setting) {
1496 if ($setting->is_related($query)) {
1497 $found[] = $setting;
1501 if ($found) {
1502 $result = new stdClass();
1503 $result->page = $this;
1504 $result->settings = $found;
1505 return array($this->name => $result);
1508 $found = false;
1509 if (strpos(strtolower($this->name), $query) !== false) {
1510 $found = true;
1511 } else if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
1512 $found = true;
1514 if ($found) {
1515 $result = new stdClass();
1516 $result->page = $this;
1517 $result->settings = array();
1518 return array($this->name => $result);
1519 } else {
1520 return array();
1525 * This function always returns false, required by interface
1527 * @param string $name
1528 * @return bool Always false
1530 public function prune($name) {
1531 return false;
1535 * adds an admin_setting to this admin_settingpage
1537 * not the same as add for admin_category. adds an admin_setting to this admin_settingpage. settings appear (on the settingpage) in the order in which they're added
1538 * n.b. each admin_setting in an admin_settingpage must have a unique internal name
1540 * @param object $setting is the admin_setting object you want to add
1541 * @return bool true if successful, false if not
1543 public function add($setting) {
1544 if (!($setting instanceof admin_setting)) {
1545 debugging('error - not a setting instance');
1546 return false;
1549 $name = $setting->name;
1550 if ($setting->plugin) {
1551 $name = $setting->plugin . $name;
1553 $this->settings->{$name} = $setting;
1554 return true;
1558 * Hide the named setting if the specified condition is matched.
1560 * @param string $settingname
1561 * @param string $dependenton
1562 * @param string $condition
1563 * @param string $value
1565 public function hide_if($settingname, $dependenton, $condition = 'notchecked', $value = '1') {
1566 $this->dependencies[] = new admin_settingdependency($settingname, $dependenton, $condition, $value);
1568 // Reformat the dependency name to the plugin | name format used in the display.
1569 $dependenton = str_replace('/', ' | ', $dependenton);
1571 // Let the setting know, so it can be displayed underneath.
1572 $findname = str_replace('/', '', $settingname);
1573 foreach ($this->settings as $name => $setting) {
1574 if ($name === $findname) {
1575 $setting->add_dependent_on($dependenton);
1581 * see admin_externalpage
1583 * @return bool Returns true for yes false for no
1585 public function check_access() {
1586 global $CFG;
1587 $context = empty($this->context) ? context_system::instance() : $this->context;
1588 foreach($this->req_capability as $cap) {
1589 if (has_capability($cap, $context)) {
1590 return true;
1593 return false;
1597 * outputs this page as html in a table (suitable for inclusion in an admin pagetype)
1598 * @return string Returns an XHTML string
1600 public function output_html() {
1601 $adminroot = admin_get_root();
1602 $return = '<fieldset>'."\n".'<div class="clearer"><!-- --></div>'."\n";
1603 foreach($this->settings as $setting) {
1604 $fullname = $setting->get_full_name();
1605 if (array_key_exists($fullname, $adminroot->errors)) {
1606 $data = $adminroot->errors[$fullname]->data;
1607 } else {
1608 $data = $setting->get_setting();
1609 // do not use defaults if settings not available - upgrade settings handles the defaults!
1611 $return .= $setting->output_html($data);
1613 $return .= '</fieldset>';
1614 return $return;
1618 * Is this settings page hidden in admin tree block?
1620 * @return bool True if hidden
1622 public function is_hidden() {
1623 return $this->hidden;
1627 * Show we display Save button at the page bottom?
1628 * @return bool
1630 public function show_save() {
1631 foreach($this->settings as $setting) {
1632 if (empty($setting->nosave)) {
1633 return true;
1636 return false;
1640 * Should any of the settings on this page be shown / hidden based on conditions?
1641 * @return bool
1643 public function has_dependencies() {
1644 return (bool)$this->dependencies;
1648 * Format the setting show/hide conditions ready to initialise the page javascript
1649 * @return array
1651 public function get_dependencies_for_javascript() {
1652 if (!$this->has_dependencies()) {
1653 return [];
1655 return admin_settingdependency::prepare_for_javascript($this->dependencies);
1661 * Admin settings class. Only exists on setting pages.
1662 * Read & write happens at this level; no authentication.
1664 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
1666 abstract class admin_setting {
1667 /** @var string unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins. */
1668 public $name;
1669 /** @var string localised name */
1670 public $visiblename;
1671 /** @var string localised long description in Markdown format */
1672 public $description;
1673 /** @var mixed Can be string or array of string */
1674 public $defaultsetting;
1675 /** @var string */
1676 public $updatedcallback;
1677 /** @var mixed can be String or Null. Null means main config table */
1678 public $plugin; // null means main config table
1679 /** @var bool true indicates this setting does not actually save anything, just information */
1680 public $nosave = false;
1681 /** @var bool if set, indicates that a change to this setting requires rebuild course cache */
1682 public $affectsmodinfo = false;
1683 /** @var array of admin_setting_flag - These are extra checkboxes attached to a setting. */
1684 private $flags = array();
1685 /** @var bool Whether this field must be forced LTR. */
1686 private $forceltr = null;
1687 /** @var array list of other settings that may cause this setting to be hidden */
1688 private $dependenton = [];
1689 /** @var bool Whether this setting uses a custom form control */
1690 protected $customcontrol = false;
1693 * Constructor
1694 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
1695 * or 'myplugin/mysetting' for ones in config_plugins.
1696 * @param string $visiblename localised name
1697 * @param string $description localised long description
1698 * @param mixed $defaultsetting string or array depending on implementation
1700 public function __construct($name, $visiblename, $description, $defaultsetting) {
1701 $this->parse_setting_name($name);
1702 $this->visiblename = $visiblename;
1703 $this->description = $description;
1704 $this->defaultsetting = $defaultsetting;
1708 * Generic function to add a flag to this admin setting.
1710 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1711 * @param bool $default - The default for the flag
1712 * @param string $shortname - The shortname for this flag. Used as a suffix for the setting name.
1713 * @param string $displayname - The display name for this flag. Used as a label next to the checkbox.
1715 protected function set_flag_options($enabled, $default, $shortname, $displayname) {
1716 if (empty($this->flags[$shortname])) {
1717 $this->flags[$shortname] = new admin_setting_flag($enabled, $default, $shortname, $displayname);
1718 } else {
1719 $this->flags[$shortname]->set_options($enabled, $default);
1724 * Set the enabled options flag on this admin setting.
1726 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1727 * @param bool $default - The default for the flag
1729 public function set_enabled_flag_options($enabled, $default) {
1730 $this->set_flag_options($enabled, $default, 'enabled', new lang_string('enabled', 'core_admin'));
1734 * Set the advanced options flag on this admin setting.
1736 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1737 * @param bool $default - The default for the flag
1739 public function set_advanced_flag_options($enabled, $default) {
1740 $this->set_flag_options($enabled, $default, 'adv', new lang_string('advanced'));
1745 * Set the locked options flag on this admin setting.
1747 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED
1748 * @param bool $default - The default for the flag
1750 public function set_locked_flag_options($enabled, $default) {
1751 $this->set_flag_options($enabled, $default, 'locked', new lang_string('locked', 'core_admin'));
1755 * Set the required options flag on this admin setting.
1757 * @param bool $enabled - One of self::OPTION_ENABLED or self::OPTION_DISABLED.
1758 * @param bool $default - The default for the flag.
1760 public function set_required_flag_options($enabled, $default) {
1761 $this->set_flag_options($enabled, $default, 'required', new lang_string('required', 'core_admin'));
1765 * Is this option forced in config.php?
1767 * @return bool
1769 public function is_readonly(): bool {
1770 global $CFG;
1772 if (empty($this->plugin)) {
1773 if (array_key_exists($this->name, $CFG->config_php_settings)) {
1774 return true;
1776 } else {
1777 if (array_key_exists($this->plugin, $CFG->forced_plugin_settings)
1778 and array_key_exists($this->name, $CFG->forced_plugin_settings[$this->plugin])) {
1779 return true;
1782 return false;
1786 * Get the currently saved value for a setting flag
1788 * @param admin_setting_flag $flag - One of the admin_setting_flag for this admin_setting.
1789 * @return bool
1791 public function get_setting_flag_value(admin_setting_flag $flag) {
1792 $value = $this->config_read($this->name . '_' . $flag->get_shortname());
1793 if (!isset($value)) {
1794 $value = $flag->get_default();
1797 return !empty($value);
1801 * Get the list of defaults for the flags on this setting.
1803 * @param array of strings describing the defaults for this setting. This is appended to by this function.
1805 public function get_setting_flag_defaults(& $defaults) {
1806 foreach ($this->flags as $flag) {
1807 if ($flag->is_enabled() && $flag->get_default()) {
1808 $defaults[] = $flag->get_displayname();
1814 * Output the input fields for the advanced and locked flags on this setting.
1816 * @param bool $adv - The current value of the advanced flag.
1817 * @param bool $locked - The current value of the locked flag.
1818 * @return string $output - The html for the flags.
1820 public function output_setting_flags() {
1821 $output = '';
1823 foreach ($this->flags as $flag) {
1824 if ($flag->is_enabled()) {
1825 $output .= $flag->output_setting_flag($this);
1829 if (!empty($output)) {
1830 return html_writer::tag('span', $output, array('class' => 'adminsettingsflags'));
1832 return $output;
1836 * Write the values of the flags for this admin setting.
1838 * @param array $data - The data submitted from the form or null to set the default value for new installs.
1839 * @return bool - true if successful.
1841 public function write_setting_flags($data) {
1842 $result = true;
1843 foreach ($this->flags as $flag) {
1844 $result = $result && $flag->write_setting_flag($this, $data);
1846 return $result;
1850 * Set up $this->name and potentially $this->plugin
1852 * Set up $this->name and possibly $this->plugin based on whether $name looks
1853 * like 'settingname' or 'plugin/settingname'. Also, do some sanity checking
1854 * on the names, that is, output a developer debug warning if the name
1855 * contains anything other than [a-zA-Z0-9_]+.
1857 * @param string $name the setting name passed in to the constructor.
1859 private function parse_setting_name($name) {
1860 $bits = explode('/', $name);
1861 if (count($bits) > 2) {
1862 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1864 $this->name = array_pop($bits);
1865 if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->name)) {
1866 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1868 if (!empty($bits)) {
1869 $this->plugin = array_pop($bits);
1870 if ($this->plugin === 'moodle') {
1871 $this->plugin = null;
1872 } else if (!preg_match('/^[a-zA-Z0-9_]+$/', $this->plugin)) {
1873 throw new moodle_exception('invalidadminsettingname', '', '', $name);
1879 * Returns the fullname prefixed by the plugin
1880 * @return string
1882 public function get_full_name() {
1883 return 's_'.$this->plugin.'_'.$this->name;
1887 * Returns the ID string based on plugin and name
1888 * @return string
1890 public function get_id() {
1891 return 'id_s_'.$this->plugin.'_'.$this->name;
1895 * @param bool $affectsmodinfo If true, changes to this setting will
1896 * cause the course cache to be rebuilt
1898 public function set_affects_modinfo($affectsmodinfo) {
1899 $this->affectsmodinfo = $affectsmodinfo;
1903 * Returns the config if possible
1905 * @return mixed returns config if successful else null
1907 public function config_read($name) {
1908 global $CFG;
1909 if (!empty($this->plugin)) {
1910 $value = get_config($this->plugin, $name);
1911 return $value === false ? NULL : $value;
1913 } else {
1914 if (isset($CFG->$name)) {
1915 return $CFG->$name;
1916 } else {
1917 return NULL;
1923 * Used to set a config pair and log change
1925 * @param string $name
1926 * @param mixed $value Gets converted to string if not null
1927 * @return bool Write setting to config table
1929 public function config_write($name, $value) {
1930 global $DB, $USER, $CFG;
1932 if ($this->nosave) {
1933 return true;
1936 // make sure it is a real change
1937 $oldvalue = get_config($this->plugin, $name);
1938 $oldvalue = ($oldvalue === false) ? null : $oldvalue; // normalise
1939 $value = is_null($value) ? null : (string)$value;
1941 if ($oldvalue === $value) {
1942 return true;
1945 // store change
1946 set_config($name, $value, $this->plugin);
1948 // Some admin settings affect course modinfo
1949 if ($this->affectsmodinfo) {
1950 // Clear course cache for all courses
1951 rebuild_course_cache(0, true);
1954 $this->add_to_config_log($name, $oldvalue, $value);
1956 return true; // BC only
1960 * Log config changes if necessary.
1961 * @param string $name
1962 * @param string $oldvalue
1963 * @param string $value
1965 protected function add_to_config_log($name, $oldvalue, $value) {
1966 add_to_config_log($name, $oldvalue, $value, $this->plugin);
1970 * Returns current value of this setting
1971 * @return mixed array or string depending on instance, NULL means not set yet
1973 public abstract function get_setting();
1976 * Returns default setting if exists
1977 * @return mixed array or string depending on instance; NULL means no default, user must supply
1979 public function get_defaultsetting() {
1980 $adminroot = admin_get_root(false, false);
1981 if (!empty($adminroot->custom_defaults)) {
1982 $plugin = is_null($this->plugin) ? 'moodle' : $this->plugin;
1983 if (isset($adminroot->custom_defaults[$plugin])) {
1984 if (array_key_exists($this->name, $adminroot->custom_defaults[$plugin])) { // null is valid value here ;-)
1985 return $adminroot->custom_defaults[$plugin][$this->name];
1989 return $this->defaultsetting;
1993 * Store new setting
1995 * @param mixed $data string or array, must not be NULL
1996 * @return string empty string if ok, string error message otherwise
1998 public abstract function write_setting($data);
2001 * Return part of form with setting
2002 * This function should always be overwritten
2004 * @param mixed $data array or string depending on setting
2005 * @param string $query
2006 * @return string
2008 public function output_html($data, $query='') {
2009 // should be overridden
2010 return;
2014 * Function called if setting updated - cleanup, cache reset, etc.
2015 * @param string $functionname Sets the function name
2016 * @return void
2018 public function set_updatedcallback($functionname) {
2019 $this->updatedcallback = $functionname;
2023 * Execute postupdatecallback if necessary.
2024 * @param mixed $original original value before write_setting()
2025 * @return bool true if changed, false if not.
2027 public function post_write_settings($original) {
2028 // Comparison must work for arrays too.
2029 if (serialize($original) === serialize($this->get_setting())) {
2030 return false;
2033 $callbackfunction = $this->updatedcallback;
2034 if (!empty($callbackfunction) and is_callable($callbackfunction)) {
2035 $callbackfunction($this->get_full_name());
2037 return true;
2041 * Is setting related to query text - used when searching
2042 * @param string $query
2043 * @return bool
2045 public function is_related($query) {
2046 if (strpos(strtolower($this->name), $query) !== false) {
2047 return true;
2049 if (strpos(core_text::strtolower($this->visiblename), $query) !== false) {
2050 return true;
2052 if (strpos(core_text::strtolower($this->description), $query) !== false) {
2053 return true;
2055 $current = $this->get_setting();
2056 if (!is_null($current)) {
2057 if (is_string($current)) {
2058 if (strpos(core_text::strtolower($current), $query) !== false) {
2059 return true;
2063 $default = $this->get_defaultsetting();
2064 if (!is_null($default)) {
2065 if (is_string($default)) {
2066 if (strpos(core_text::strtolower($default), $query) !== false) {
2067 return true;
2071 return false;
2075 * Get whether this should be displayed in LTR mode.
2077 * @return bool|null
2079 public function get_force_ltr() {
2080 return $this->forceltr;
2084 * Set whether to force LTR or not.
2086 * @param bool $value True when forced, false when not force, null when unknown.
2088 public function set_force_ltr($value) {
2089 $this->forceltr = $value;
2093 * Add a setting to the list of those that could cause this one to be hidden
2094 * @param string $dependenton
2096 public function add_dependent_on($dependenton) {
2097 $this->dependenton[] = $dependenton;
2101 * Get a list of the settings that could cause this one to be hidden.
2102 * @return array
2104 public function get_dependent_on() {
2105 return $this->dependenton;
2109 * Whether this setting uses a custom form control.
2110 * This function is especially useful to decide if we should render a label element for this setting or not.
2112 * @return bool
2114 public function has_custom_form_control(): bool {
2115 return $this->customcontrol;
2120 * An additional option that can be applied to an admin setting.
2121 * The currently supported options are 'ADVANCED', 'LOCKED' and 'REQUIRED'.
2123 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2125 class admin_setting_flag {
2126 /** @var bool Flag to indicate if this option can be toggled for this setting */
2127 private $enabled = false;
2128 /** @var bool Flag to indicate if this option defaults to true or false */
2129 private $default = false;
2130 /** @var string Short string used to create setting name - e.g. 'adv' */
2131 private $shortname = '';
2132 /** @var string String used as the label for this flag */
2133 private $displayname = '';
2134 /** @const Checkbox for this flag is displayed in admin page */
2135 const ENABLED = true;
2136 /** @const Checkbox for this flag is not displayed in admin page */
2137 const DISABLED = false;
2140 * Constructor
2142 * @param bool $enabled Can this option can be toggled.
2143 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2144 * @param bool $default The default checked state for this setting option.
2145 * @param string $shortname The shortname of this flag. Currently supported flags are 'locked' and 'adv'
2146 * @param string $displayname The displayname of this flag. Used as a label for the flag.
2148 public function __construct($enabled, $default, $shortname, $displayname) {
2149 $this->shortname = $shortname;
2150 $this->displayname = $displayname;
2151 $this->set_options($enabled, $default);
2155 * Update the values of this setting options class
2157 * @param bool $enabled Can this option can be toggled.
2158 * Should be one of admin_setting_flag::ENABLED or admin_setting_flag::DISABLED.
2159 * @param bool $default The default checked state for this setting option.
2161 public function set_options($enabled, $default) {
2162 $this->enabled = $enabled;
2163 $this->default = $default;
2167 * Should this option appear in the interface and be toggleable?
2169 * @return bool Is it enabled?
2171 public function is_enabled() {
2172 return $this->enabled;
2176 * Should this option be checked by default?
2178 * @return bool Is it on by default?
2180 public function get_default() {
2181 return $this->default;
2185 * Return the short name for this flag. e.g. 'adv' or 'locked'
2187 * @return string
2189 public function get_shortname() {
2190 return $this->shortname;
2194 * Return the display name for this flag. e.g. 'Advanced' or 'Locked'
2196 * @return string
2198 public function get_displayname() {
2199 return $this->displayname;
2203 * Save the submitted data for this flag - or set it to the default if $data is null.
2205 * @param admin_setting $setting - The admin setting for this flag
2206 * @param array $data - The data submitted from the form or null to set the default value for new installs.
2207 * @return bool
2209 public function write_setting_flag(admin_setting $setting, $data) {
2210 $result = true;
2211 if ($this->is_enabled()) {
2212 if (!isset($data)) {
2213 $value = $this->get_default();
2214 } else {
2215 $value = !empty($data[$setting->get_full_name() . '_' . $this->get_shortname()]);
2217 $result = $setting->config_write($setting->name . '_' . $this->get_shortname(), $value);
2220 return $result;
2225 * Output the checkbox for this setting flag. Should only be called if the flag is enabled.
2227 * @param admin_setting $setting - The admin setting for this flag
2228 * @return string - The html for the checkbox.
2230 public function output_setting_flag(admin_setting $setting) {
2231 global $OUTPUT;
2233 $value = $setting->get_setting_flag_value($this);
2235 $context = new stdClass();
2236 $context->id = $setting->get_id() . '_' . $this->get_shortname();
2237 $context->name = $setting->get_full_name() . '_' . $this->get_shortname();
2238 $context->value = 1;
2239 $context->checked = $value ? true : false;
2240 $context->label = $this->get_displayname();
2242 return $OUTPUT->render_from_template('core_admin/setting_flag', $context);
2248 * No setting - just heading and text.
2250 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2252 class admin_setting_heading extends admin_setting {
2255 * not a setting, just text
2256 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2257 * @param string $heading heading
2258 * @param string $information text in box
2260 public function __construct($name, $heading, $information) {
2261 $this->nosave = true;
2262 parent::__construct($name, $heading, $information, '');
2266 * Always returns true
2267 * @return bool Always returns true
2269 public function get_setting() {
2270 return true;
2274 * Always returns true
2275 * @return bool Always returns true
2277 public function get_defaultsetting() {
2278 return true;
2282 * Never write settings
2283 * @return string Always returns an empty string
2285 public function write_setting($data) {
2286 // do not write any setting
2287 return '';
2291 * Returns an HTML string
2292 * @return string Returns an HTML string
2294 public function output_html($data, $query='') {
2295 global $OUTPUT;
2296 $context = new stdClass();
2297 $context->title = $this->visiblename;
2298 $context->description = $this->description;
2299 $context->descriptionformatted = highlight($query, markdown_to_html($this->description));
2300 return $OUTPUT->render_from_template('core_admin/setting_heading', $context);
2305 * No setting - just name and description in same row.
2307 * @copyright 2018 onwards Amaia Anabitarte
2308 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2310 class admin_setting_description extends admin_setting {
2313 * Not a setting, just text
2315 * @param string $name
2316 * @param string $visiblename
2317 * @param string $description
2319 public function __construct($name, $visiblename, $description) {
2320 $this->nosave = true;
2321 parent::__construct($name, $visiblename, $description, '');
2325 * Always returns true
2327 * @return bool Always returns true
2329 public function get_setting() {
2330 return true;
2334 * Always returns true
2336 * @return bool Always returns true
2338 public function get_defaultsetting() {
2339 return true;
2343 * Never write settings
2345 * @param mixed $data Gets converted to str for comparison against yes value
2346 * @return string Always returns an empty string
2348 public function write_setting($data) {
2349 // Do not write any setting.
2350 return '';
2354 * Returns an HTML string
2356 * @param string $data
2357 * @param string $query
2358 * @return string Returns an HTML string
2360 public function output_html($data, $query='') {
2361 global $OUTPUT;
2363 $context = new stdClass();
2364 $context->title = $this->visiblename;
2365 $context->description = $this->description;
2367 return $OUTPUT->render_from_template('core_admin/setting_description', $context);
2374 * The most flexible setting, the user enters text.
2376 * This type of field should be used for config settings which are using
2377 * English words and are not localised (passwords, database name, list of values, ...).
2379 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2381 class admin_setting_configtext extends admin_setting {
2383 /** @var mixed int means PARAM_XXX type, string is a allowed format in regex */
2384 public $paramtype;
2385 /** @var int default field size */
2386 public $size;
2389 * Config text constructor
2391 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2392 * @param string $visiblename localised
2393 * @param string $description long localised info
2394 * @param string $defaultsetting
2395 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2396 * @param int $size default field size
2398 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
2399 $this->paramtype = $paramtype;
2400 if (!is_null($size)) {
2401 $this->size = $size;
2402 } else {
2403 $this->size = ($paramtype === PARAM_INT) ? 5 : 30;
2405 parent::__construct($name, $visiblename, $description, $defaultsetting);
2409 * Get whether this should be displayed in LTR mode.
2411 * Try to guess from the PARAM type unless specifically set.
2413 public function get_force_ltr() {
2414 $forceltr = parent::get_force_ltr();
2415 if ($forceltr === null) {
2416 return !is_rtl_compatible($this->paramtype);
2418 return $forceltr;
2422 * Return the setting
2424 * @return mixed returns config if successful else null
2426 public function get_setting() {
2427 return $this->config_read($this->name);
2430 public function write_setting($data) {
2431 if ($this->paramtype === PARAM_INT and $data === '') {
2432 // do not complain if '' used instead of 0
2433 $data = 0;
2435 // $data is a string
2436 $validated = $this->validate($data);
2437 if ($validated !== true) {
2438 return $validated;
2440 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2444 * Validate data before storage
2445 * @param string data
2446 * @return mixed true if ok string if error found
2448 public function validate($data) {
2449 // allow paramtype to be a custom regex if it is the form of /pattern/
2450 if (preg_match('#^/.*/$#', $this->paramtype)) {
2451 if (preg_match($this->paramtype, $data)) {
2452 return true;
2453 } else {
2454 return get_string('validateerror', 'admin');
2457 } else if ($this->paramtype === PARAM_RAW) {
2458 return true;
2460 } else {
2461 $cleaned = clean_param($data, $this->paramtype);
2462 if ("$data" === "$cleaned") { // implicit conversion to string is needed to do exact comparison
2463 return true;
2464 } else {
2465 return get_string('validateerror', 'admin');
2471 * Return an XHTML string for the setting
2472 * @return string Returns an XHTML string
2474 public function output_html($data, $query='') {
2475 global $OUTPUT;
2477 $default = $this->get_defaultsetting();
2478 $context = (object) [
2479 'size' => $this->size,
2480 'id' => $this->get_id(),
2481 'name' => $this->get_full_name(),
2482 'value' => $data,
2483 'forceltr' => $this->get_force_ltr(),
2484 'readonly' => $this->is_readonly(),
2486 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
2488 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2493 * Text input with a maximum length constraint.
2495 * @copyright 2015 onwards Ankit Agarwal
2496 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2498 class admin_setting_configtext_with_maxlength extends admin_setting_configtext {
2500 /** @var int maximum number of chars allowed. */
2501 protected $maxlength;
2504 * Config text constructor
2506 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
2507 * or 'myplugin/mysetting' for ones in config_plugins.
2508 * @param string $visiblename localised
2509 * @param string $description long localised info
2510 * @param string $defaultsetting
2511 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
2512 * @param int $size default field size
2513 * @param mixed $maxlength int maxlength allowed, 0 for infinite.
2515 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW,
2516 $size=null, $maxlength = 0) {
2517 $this->maxlength = $maxlength;
2518 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
2522 * Validate data before storage
2524 * @param string $data data
2525 * @return mixed true if ok string if error found
2527 public function validate($data) {
2528 $parentvalidation = parent::validate($data);
2529 if ($parentvalidation === true) {
2530 if ($this->maxlength > 0) {
2531 // Max length check.
2532 $length = core_text::strlen($data);
2533 if ($length > $this->maxlength) {
2534 return get_string('maximumchars', 'moodle', $this->maxlength);
2536 return true;
2537 } else {
2538 return true; // No max length check needed.
2540 } else {
2541 return $parentvalidation;
2547 * General text area without html editor.
2549 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2551 class admin_setting_configtextarea extends admin_setting_configtext {
2552 private $rows;
2553 private $cols;
2556 * @param string $name
2557 * @param string $visiblename
2558 * @param string $description
2559 * @param mixed $defaultsetting string or array
2560 * @param mixed $paramtype
2561 * @param string $cols The number of columns to make the editor
2562 * @param string $rows The number of rows to make the editor
2564 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2565 $this->rows = $rows;
2566 $this->cols = $cols;
2567 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype);
2571 * Returns an XHTML string for the editor
2573 * @param string $data
2574 * @param string $query
2575 * @return string XHTML string for the editor
2577 public function output_html($data, $query='') {
2578 global $OUTPUT;
2580 $default = $this->get_defaultsetting();
2581 $defaultinfo = $default;
2582 if (!is_null($default) and $default !== '') {
2583 $defaultinfo = "\n".$default;
2586 $context = (object) [
2587 'cols' => $this->cols,
2588 'rows' => $this->rows,
2589 'id' => $this->get_id(),
2590 'name' => $this->get_full_name(),
2591 'value' => $data,
2592 'forceltr' => $this->get_force_ltr(),
2593 'readonly' => $this->is_readonly(),
2595 $element = $OUTPUT->render_from_template('core_admin/setting_configtextarea', $context);
2597 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
2602 * General text area with html editor.
2604 class admin_setting_confightmleditor extends admin_setting_configtextarea {
2607 * @param string $name
2608 * @param string $visiblename
2609 * @param string $description
2610 * @param mixed $defaultsetting string or array
2611 * @param mixed $paramtype
2613 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $cols='60', $rows='8') {
2614 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
2615 $this->set_force_ltr(false);
2616 editors_head_setup();
2620 * Returns an XHTML string for the editor
2622 * @param string $data
2623 * @param string $query
2624 * @return string XHTML string for the editor
2626 public function output_html($data, $query='') {
2627 $editor = editors_get_preferred_editor(FORMAT_HTML);
2628 $editor->set_text($data);
2629 $editor->use_editor($this->get_id(), array('noclean'=>true));
2630 return parent::output_html($data, $query);
2634 * Checks if data has empty html.
2636 * @param string $data
2637 * @return string Empty when no errors.
2639 public function write_setting($data) {
2640 if (trim(html_to_text($data)) === '') {
2641 $data = '';
2643 return parent::write_setting($data);
2649 * Password field, allows unmasking of password
2651 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2653 class admin_setting_configpasswordunmask extends admin_setting_configtext {
2656 * Constructor
2657 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2658 * @param string $visiblename localised
2659 * @param string $description long localised info
2660 * @param string $defaultsetting default password
2662 public function __construct($name, $visiblename, $description, $defaultsetting) {
2663 parent::__construct($name, $visiblename, $description, $defaultsetting, PARAM_RAW, 30);
2667 * Log config changes if necessary.
2668 * @param string $name
2669 * @param string $oldvalue
2670 * @param string $value
2672 protected function add_to_config_log($name, $oldvalue, $value) {
2673 if ($value !== '') {
2674 $value = '********';
2676 if ($oldvalue !== '' and $oldvalue !== null) {
2677 $oldvalue = '********';
2679 parent::add_to_config_log($name, $oldvalue, $value);
2683 * Returns HTML for the field.
2685 * @param string $data Value for the field
2686 * @param string $query Passed as final argument for format_admin_setting
2687 * @return string Rendered HTML
2689 public function output_html($data, $query='') {
2690 global $OUTPUT;
2692 $context = (object) [
2693 'id' => $this->get_id(),
2694 'name' => $this->get_full_name(),
2695 'size' => $this->size,
2696 'value' => $this->is_readonly() ? null : $data,
2697 'forceltr' => $this->get_force_ltr(),
2698 'readonly' => $this->is_readonly(),
2700 $element = $OUTPUT->render_from_template('core_admin/setting_configpasswordunmask', $context);
2701 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', null, $query);
2706 * Password field, allows unmasking of password, with an advanced checkbox that controls an additional $name.'_adv' setting.
2708 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2709 * @copyright 2018 Paul Holden (pholden@greenhead.ac.uk)
2711 class admin_setting_configpasswordunmask_with_advanced extends admin_setting_configpasswordunmask {
2714 * Constructor
2716 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2717 * @param string $visiblename localised
2718 * @param string $description long localised info
2719 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
2721 public function __construct($name, $visiblename, $description, $defaultsetting) {
2722 parent::__construct($name, $visiblename, $description, $defaultsetting['value']);
2723 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
2728 * Empty setting used to allow flags (advanced) on settings that can have no sensible default.
2729 * Note: Only advanced makes sense right now - locked does not.
2731 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2733 class admin_setting_configempty extends admin_setting_configtext {
2736 * @param string $name
2737 * @param string $visiblename
2738 * @param string $description
2740 public function __construct($name, $visiblename, $description) {
2741 parent::__construct($name, $visiblename, $description, '', PARAM_RAW);
2745 * Returns an XHTML string for the hidden field
2747 * @param string $data
2748 * @param string $query
2749 * @return string XHTML string for the editor
2751 public function output_html($data, $query='') {
2752 global $OUTPUT;
2754 $context = (object) [
2755 'id' => $this->get_id(),
2756 'name' => $this->get_full_name()
2758 $element = $OUTPUT->render_from_template('core_admin/setting_configempty', $context);
2760 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', get_string('none'), $query);
2766 * Path to directory
2768 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2770 class admin_setting_configfile extends admin_setting_configtext {
2772 * Constructor
2773 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2774 * @param string $visiblename localised
2775 * @param string $description long localised info
2776 * @param string $defaultdirectory default directory location
2778 public function __construct($name, $visiblename, $description, $defaultdirectory) {
2779 parent::__construct($name, $visiblename, $description, $defaultdirectory, PARAM_RAW, 50);
2783 * Returns XHTML for the field
2785 * Returns XHTML for the field and also checks whether the file
2786 * specified in $data exists using file_exists()
2788 * @param string $data File name and path to use in value attr
2789 * @param string $query
2790 * @return string XHTML field
2792 public function output_html($data, $query='') {
2793 global $CFG, $OUTPUT;
2795 $default = $this->get_defaultsetting();
2796 $context = (object) [
2797 'id' => $this->get_id(),
2798 'name' => $this->get_full_name(),
2799 'size' => $this->size,
2800 'value' => $data,
2801 'showvalidity' => !empty($data),
2802 'valid' => $data && file_exists($data),
2803 'readonly' => !empty($CFG->preventexecpath) || $this->is_readonly(),
2804 'forceltr' => $this->get_force_ltr(),
2807 if ($context->readonly) {
2808 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2811 $element = $OUTPUT->render_from_template('core_admin/setting_configfile', $context);
2813 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2817 * Checks if execpatch has been disabled in config.php
2819 public function write_setting($data) {
2820 global $CFG;
2821 if (!empty($CFG->preventexecpath)) {
2822 if ($this->get_setting() === null) {
2823 // Use default during installation.
2824 $data = $this->get_defaultsetting();
2825 if ($data === null) {
2826 $data = '';
2828 } else {
2829 return '';
2832 return parent::write_setting($data);
2839 * Path to executable file
2841 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2843 class admin_setting_configexecutable extends admin_setting_configfile {
2846 * Returns an XHTML field
2848 * @param string $data This is the value for the field
2849 * @param string $query
2850 * @return string XHTML field
2852 public function output_html($data, $query='') {
2853 global $CFG, $OUTPUT;
2854 $default = $this->get_defaultsetting();
2855 require_once("$CFG->libdir/filelib.php");
2857 $context = (object) [
2858 'id' => $this->get_id(),
2859 'name' => $this->get_full_name(),
2860 'size' => $this->size,
2861 'value' => $data,
2862 'showvalidity' => !empty($data),
2863 'valid' => $data && file_exists($data) && !is_dir($data) && file_is_executable($data),
2864 'readonly' => !empty($CFG->preventexecpath),
2865 'forceltr' => $this->get_force_ltr()
2868 if (!empty($CFG->preventexecpath)) {
2869 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2872 $element = $OUTPUT->render_from_template('core_admin/setting_configexecutable', $context);
2874 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2880 * Path to directory
2882 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2884 class admin_setting_configdirectory extends admin_setting_configfile {
2887 * Returns an XHTML field
2889 * @param string $data This is the value for the field
2890 * @param string $query
2891 * @return string XHTML
2893 public function output_html($data, $query='') {
2894 global $CFG, $OUTPUT;
2895 $default = $this->get_defaultsetting();
2897 $context = (object) [
2898 'id' => $this->get_id(),
2899 'name' => $this->get_full_name(),
2900 'size' => $this->size,
2901 'value' => $data,
2902 'showvalidity' => !empty($data),
2903 'valid' => $data && file_exists($data) && is_dir($data),
2904 'readonly' => !empty($CFG->preventexecpath),
2905 'forceltr' => $this->get_force_ltr()
2908 if (!empty($CFG->preventexecpath)) {
2909 $this->visiblename .= '<div class="alert alert-info">'.get_string('execpathnotallowed', 'admin').'</div>';
2912 $element = $OUTPUT->render_from_template('core_admin/setting_configdirectory', $context);
2914 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
2920 * Checkbox
2922 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
2924 class admin_setting_configcheckbox extends admin_setting {
2925 /** @var string Value used when checked */
2926 public $yes;
2927 /** @var string Value used when not checked */
2928 public $no;
2931 * Constructor
2932 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
2933 * @param string $visiblename localised
2934 * @param string $description long localised info
2935 * @param string $defaultsetting
2936 * @param string $yes value used when checked
2937 * @param string $no value used when not checked
2939 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
2940 parent::__construct($name, $visiblename, $description, $defaultsetting);
2941 $this->yes = (string)$yes;
2942 $this->no = (string)$no;
2946 * Retrieves the current setting using the objects name
2948 * @return string
2950 public function get_setting() {
2951 return $this->config_read($this->name);
2955 * Sets the value for the setting
2957 * Sets the value for the setting to either the yes or no values
2958 * of the object by comparing $data to yes
2960 * @param mixed $data Gets converted to str for comparison against yes value
2961 * @return string empty string or error
2963 public function write_setting($data) {
2964 if ((string)$data === $this->yes) { // convert to strings before comparison
2965 $data = $this->yes;
2966 } else {
2967 $data = $this->no;
2969 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
2973 * Returns an XHTML checkbox field
2975 * @param string $data If $data matches yes then checkbox is checked
2976 * @param string $query
2977 * @return string XHTML field
2979 public function output_html($data, $query='') {
2980 global $OUTPUT;
2982 $context = (object) [
2983 'id' => $this->get_id(),
2984 'name' => $this->get_full_name(),
2985 'no' => $this->no,
2986 'value' => $this->yes,
2987 'checked' => (string) $data === $this->yes,
2988 'readonly' => $this->is_readonly(),
2991 $default = $this->get_defaultsetting();
2992 if (!is_null($default)) {
2993 if ((string)$default === $this->yes) {
2994 $defaultinfo = get_string('checkboxyes', 'admin');
2995 } else {
2996 $defaultinfo = get_string('checkboxno', 'admin');
2998 } else {
2999 $defaultinfo = NULL;
3002 $element = $OUTPUT->render_from_template('core_admin/setting_configcheckbox', $context);
3004 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3010 * Multiple checkboxes, each represents different value, stored in csv format
3012 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3014 class admin_setting_configmulticheckbox extends admin_setting {
3015 /** @var array Array of choices value=>label */
3016 public $choices;
3019 * Constructor: uses parent::__construct
3021 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3022 * @param string $visiblename localised
3023 * @param string $description long localised info
3024 * @param array $defaultsetting array of selected
3025 * @param array $choices array of $value=>$label for each checkbox
3027 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3028 $this->choices = $choices;
3029 parent::__construct($name, $visiblename, $description, $defaultsetting);
3033 * This public function may be used in ancestors for lazy loading of choices
3035 * @todo Check if this function is still required content commented out only returns true
3036 * @return bool true if loaded, false if error
3038 public function load_choices() {
3040 if (is_array($this->choices)) {
3041 return true;
3043 .... load choices here
3045 return true;
3049 * Is setting related to query text - used when searching
3051 * @param string $query
3052 * @return bool true on related, false on not or failure
3054 public function is_related($query) {
3055 if (!$this->load_choices() or empty($this->choices)) {
3056 return false;
3058 if (parent::is_related($query)) {
3059 return true;
3062 foreach ($this->choices as $desc) {
3063 if (strpos(core_text::strtolower($desc), $query) !== false) {
3064 return true;
3067 return false;
3071 * Returns the current setting if it is set
3073 * @return mixed null if null, else an array
3075 public function get_setting() {
3076 $result = $this->config_read($this->name);
3078 if (is_null($result)) {
3079 return NULL;
3081 if ($result === '') {
3082 return array();
3084 $enabled = explode(',', $result);
3085 $setting = array();
3086 foreach ($enabled as $option) {
3087 $setting[$option] = 1;
3089 return $setting;
3093 * Saves the setting(s) provided in $data
3095 * @param array $data An array of data, if not array returns empty str
3096 * @return mixed empty string on useless data or bool true=success, false=failed
3098 public function write_setting($data) {
3099 if (!is_array($data)) {
3100 return ''; // ignore it
3102 if (!$this->load_choices() or empty($this->choices)) {
3103 return '';
3105 unset($data['xxxxx']);
3106 $result = array();
3107 foreach ($data as $key => $value) {
3108 if ($value and array_key_exists($key, $this->choices)) {
3109 $result[] = $key;
3112 return $this->config_write($this->name, implode(',', $result)) ? '' : get_string('errorsetting', 'admin');
3116 * Returns XHTML field(s) as required by choices
3118 * Relies on data being an array should data ever be another valid vartype with
3119 * acceptable value this may cause a warning/error
3120 * if (!is_array($data)) would fix the problem
3122 * @todo Add vartype handling to ensure $data is an array
3124 * @param array $data An array of checked values
3125 * @param string $query
3126 * @return string XHTML field
3128 public function output_html($data, $query='') {
3129 global $OUTPUT;
3131 if (!$this->load_choices() or empty($this->choices)) {
3132 return '';
3135 $default = $this->get_defaultsetting();
3136 if (is_null($default)) {
3137 $default = array();
3139 if (is_null($data)) {
3140 $data = array();
3143 $context = (object) [
3144 'id' => $this->get_id(),
3145 'name' => $this->get_full_name(),
3148 $options = array();
3149 $defaults = array();
3150 foreach ($this->choices as $key => $description) {
3151 if (!empty($default[$key])) {
3152 $defaults[] = $description;
3155 $options[] = [
3156 'key' => $key,
3157 'checked' => !empty($data[$key]),
3158 'label' => highlightfast($query, $description)
3162 if (is_null($default)) {
3163 $defaultinfo = null;
3164 } else if (!empty($defaults)) {
3165 $defaultinfo = implode(', ', $defaults);
3166 } else {
3167 $defaultinfo = get_string('none');
3170 $context->options = $options;
3171 $context->hasoptions = !empty($options);
3173 $element = $OUTPUT->render_from_template('core_admin/setting_configmulticheckbox', $context);
3175 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', $defaultinfo, $query);
3182 * Multiple checkboxes 2, value stored as string 00101011
3184 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3186 class admin_setting_configmulticheckbox2 extends admin_setting_configmulticheckbox {
3189 * Returns the setting if set
3191 * @return mixed null if not set, else an array of set settings
3193 public function get_setting() {
3194 $result = $this->config_read($this->name);
3195 if (is_null($result)) {
3196 return NULL;
3198 if (!$this->load_choices()) {
3199 return NULL;
3201 $result = str_pad($result, count($this->choices), '0');
3202 $result = preg_split('//', $result, -1, PREG_SPLIT_NO_EMPTY);
3203 $setting = array();
3204 foreach ($this->choices as $key=>$unused) {
3205 $value = array_shift($result);
3206 if ($value) {
3207 $setting[$key] = 1;
3210 return $setting;
3214 * Save setting(s) provided in $data param
3216 * @param array $data An array of settings to save
3217 * @return mixed empty string for bad data or bool true=>success, false=>error
3219 public function write_setting($data) {
3220 if (!is_array($data)) {
3221 return ''; // ignore it
3223 if (!$this->load_choices() or empty($this->choices)) {
3224 return '';
3226 $result = '';
3227 foreach ($this->choices as $key=>$unused) {
3228 if (!empty($data[$key])) {
3229 $result .= '1';
3230 } else {
3231 $result .= '0';
3234 return $this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin');
3240 * Select one value from list
3242 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3244 class admin_setting_configselect extends admin_setting {
3245 /** @var array Array of choices value=>label */
3246 public $choices;
3247 /** @var array Array of choices grouped using optgroups */
3248 public $optgroups;
3251 * Constructor
3252 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3253 * @param string $visiblename localised
3254 * @param string $description long localised info
3255 * @param string|int $defaultsetting
3256 * @param array $choices array of $value=>$label for each selection
3258 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3259 // Look for optgroup and single options.
3260 if (is_array($choices)) {
3261 $this->choices = [];
3262 foreach ($choices as $key => $val) {
3263 if (is_array($val)) {
3264 $this->optgroups[$key] = $val;
3265 $this->choices = array_merge($this->choices, $val);
3266 } else {
3267 $this->choices[$key] = $val;
3272 parent::__construct($name, $visiblename, $description, $defaultsetting);
3276 * This function may be used in ancestors for lazy loading of choices
3278 * Override this method if loading of choices is expensive, such
3279 * as when it requires multiple db requests.
3281 * @return bool true if loaded, false if error
3283 public function load_choices() {
3285 if (is_array($this->choices)) {
3286 return true;
3288 .... load choices here
3290 return true;
3294 * Check if this is $query is related to a choice
3296 * @param string $query
3297 * @return bool true if related, false if not
3299 public function is_related($query) {
3300 if (parent::is_related($query)) {
3301 return true;
3303 if (!$this->load_choices()) {
3304 return false;
3306 foreach ($this->choices as $key=>$value) {
3307 if (strpos(core_text::strtolower($key), $query) !== false) {
3308 return true;
3310 if (strpos(core_text::strtolower($value), $query) !== false) {
3311 return true;
3314 return false;
3318 * Return the setting
3320 * @return mixed returns config if successful else null
3322 public function get_setting() {
3323 return $this->config_read($this->name);
3327 * Save a setting
3329 * @param string $data
3330 * @return string empty of error string
3332 public function write_setting($data) {
3333 if (!$this->load_choices() or empty($this->choices)) {
3334 return '';
3336 if (!array_key_exists($data, $this->choices)) {
3337 return ''; // ignore it
3340 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
3344 * Returns XHTML select field
3346 * Ensure the options are loaded, and generate the XHTML for the select
3347 * element and any warning message. Separating this out from output_html
3348 * makes it easier to subclass this class.
3350 * @param string $data the option to show as selected.
3351 * @param string $current the currently selected option in the database, null if none.
3352 * @param string $default the default selected option.
3353 * @return array the HTML for the select element, and a warning message.
3354 * @deprecated since Moodle 3.2
3356 public function output_select_html($data, $current, $default, $extraname = '') {
3357 debugging('The method admin_setting_configselect::output_select_html is depreacted, do not use any more.', DEBUG_DEVELOPER);
3361 * Returns XHTML select field and wrapping div(s)
3363 * @see output_select_html()
3365 * @param string $data the option to show as selected
3366 * @param string $query
3367 * @return string XHTML field and wrapping div
3369 public function output_html($data, $query='') {
3370 global $OUTPUT;
3372 $default = $this->get_defaultsetting();
3373 $current = $this->get_setting();
3375 if (!$this->load_choices() || empty($this->choices)) {
3376 return '';
3379 $context = (object) [
3380 'id' => $this->get_id(),
3381 'name' => $this->get_full_name(),
3384 if (!is_null($default) && array_key_exists($default, $this->choices)) {
3385 $defaultinfo = $this->choices[$default];
3386 } else {
3387 $defaultinfo = NULL;
3390 // Warnings.
3391 $warning = '';
3392 if ($current === null) {
3393 // First run.
3394 } else if (empty($current) && (array_key_exists('', $this->choices) || array_key_exists(0, $this->choices))) {
3395 // No warning.
3396 } else if (!array_key_exists($current, $this->choices)) {
3397 $warning = get_string('warningcurrentsetting', 'admin', $current);
3398 if (!is_null($default) && $data == $current) {
3399 $data = $default; // Use default instead of first value when showing the form.
3403 $options = [];
3404 $template = 'core_admin/setting_configselect';
3406 if (!empty($this->optgroups)) {
3407 $optgroups = [];
3408 foreach ($this->optgroups as $label => $choices) {
3409 $optgroup = array('label' => $label, 'options' => []);
3410 foreach ($choices as $value => $name) {
3411 $optgroup['options'][] = [
3412 'value' => $value,
3413 'name' => $name,
3414 'selected' => (string) $value == $data
3416 unset($this->choices[$value]);
3418 $optgroups[] = $optgroup;
3420 $context->options = $options;
3421 $context->optgroups = $optgroups;
3422 $template = 'core_admin/setting_configselect_optgroup';
3425 foreach ($this->choices as $value => $name) {
3426 $options[] = [
3427 'value' => $value,
3428 'name' => $name,
3429 'selected' => (string) $value == $data
3432 $context->options = $options;
3433 $context->readonly = $this->is_readonly();
3435 $element = $OUTPUT->render_from_template($template, $context);
3437 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, $warning, $defaultinfo, $query);
3443 * Select multiple items from list
3445 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3447 class admin_setting_configmultiselect extends admin_setting_configselect {
3449 * Constructor
3450 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
3451 * @param string $visiblename localised
3452 * @param string $description long localised info
3453 * @param array $defaultsetting array of selected items
3454 * @param array $choices array of $value=>$label for each list item
3456 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
3457 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
3461 * Returns the select setting(s)
3463 * @return mixed null or array. Null if no settings else array of setting(s)
3465 public function get_setting() {
3466 $result = $this->config_read($this->name);
3467 if (is_null($result)) {
3468 return NULL;
3470 if ($result === '') {
3471 return array();
3473 return explode(',', $result);
3477 * Saves setting(s) provided through $data
3479 * Potential bug in the works should anyone call with this function
3480 * using a vartype that is not an array
3482 * @param array $data
3484 public function write_setting($data) {
3485 if (!is_array($data)) {
3486 return ''; //ignore it
3488 if (!$this->load_choices() or empty($this->choices)) {
3489 return '';
3492 unset($data['xxxxx']);
3494 $save = array();
3495 foreach ($data as $value) {
3496 if (!array_key_exists($value, $this->choices)) {
3497 continue; // ignore it
3499 $save[] = $value;
3502 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
3506 * Is setting related to query text - used when searching
3508 * @param string $query
3509 * @return bool true if related, false if not
3511 public function is_related($query) {
3512 if (!$this->load_choices() or empty($this->choices)) {
3513 return false;
3515 if (parent::is_related($query)) {
3516 return true;
3519 foreach ($this->choices as $desc) {
3520 if (strpos(core_text::strtolower($desc), $query) !== false) {
3521 return true;
3524 return false;
3528 * Returns XHTML multi-select field
3530 * @todo Add vartype handling to ensure $data is an array
3531 * @param array $data Array of values to select by default
3532 * @param string $query
3533 * @return string XHTML multi-select field
3535 public function output_html($data, $query='') {
3536 global $OUTPUT;
3538 if (!$this->load_choices() or empty($this->choices)) {
3539 return '';
3542 $default = $this->get_defaultsetting();
3543 if (is_null($default)) {
3544 $default = array();
3546 if (is_null($data)) {
3547 $data = array();
3550 $context = (object) [
3551 'id' => $this->get_id(),
3552 'name' => $this->get_full_name(),
3553 'size' => min(10, count($this->choices))
3556 $defaults = [];
3557 $options = [];
3558 $template = 'core_admin/setting_configmultiselect';
3560 if (!empty($this->optgroups)) {
3561 $optgroups = [];
3562 foreach ($this->optgroups as $label => $choices) {
3563 $optgroup = array('label' => $label, 'options' => []);
3564 foreach ($choices as $value => $name) {
3565 if (in_array($value, $default)) {
3566 $defaults[] = $name;
3568 $optgroup['options'][] = [
3569 'value' => $value,
3570 'name' => $name,
3571 'selected' => in_array($value, $data)
3573 unset($this->choices[$value]);
3575 $optgroups[] = $optgroup;
3577 $context->optgroups = $optgroups;
3578 $template = 'core_admin/setting_configmultiselect_optgroup';
3581 foreach ($this->choices as $value => $name) {
3582 if (in_array($value, $default)) {
3583 $defaults[] = $name;
3585 $options[] = [
3586 'value' => $value,
3587 'name' => $name,
3588 'selected' => in_array($value, $data)
3591 $context->options = $options;
3592 $context->readonly = $this->is_readonly();
3594 if (is_null($default)) {
3595 $defaultinfo = NULL;
3596 } if (!empty($defaults)) {
3597 $defaultinfo = implode(', ', $defaults);
3598 } else {
3599 $defaultinfo = get_string('none');
3602 $element = $OUTPUT->render_from_template($template, $context);
3604 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
3609 * Time selector
3611 * This is a liiitle bit messy. we're using two selects, but we're returning
3612 * them as an array named after $name (so we only use $name2 internally for the setting)
3614 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3616 class admin_setting_configtime extends admin_setting {
3617 /** @var string Used for setting second select (minutes) */
3618 public $name2;
3621 * Constructor
3622 * @param string $hoursname setting for hours
3623 * @param string $minutesname setting for hours
3624 * @param string $visiblename localised
3625 * @param string $description long localised info
3626 * @param array $defaultsetting array representing default time 'h'=>hours, 'm'=>minutes
3628 public function __construct($hoursname, $minutesname, $visiblename, $description, $defaultsetting) {
3629 $this->name2 = $minutesname;
3630 parent::__construct($hoursname, $visiblename, $description, $defaultsetting);
3634 * Get the selected time
3636 * @return mixed An array containing 'h'=>xx, 'm'=>xx, or null if not set
3638 public function get_setting() {
3639 $result1 = $this->config_read($this->name);
3640 $result2 = $this->config_read($this->name2);
3641 if (is_null($result1) or is_null($result2)) {
3642 return NULL;
3645 return array('h' => $result1, 'm' => $result2);
3649 * Store the time (hours and minutes)
3651 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3652 * @return bool true if success, false if not
3654 public function write_setting($data) {
3655 if (!is_array($data)) {
3656 return '';
3659 $result = $this->config_write($this->name, (int)$data['h']) && $this->config_write($this->name2, (int)$data['m']);
3660 return ($result ? '' : get_string('errorsetting', 'admin'));
3664 * Returns XHTML time select fields
3666 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3667 * @param string $query
3668 * @return string XHTML time select fields and wrapping div(s)
3670 public function output_html($data, $query='') {
3671 global $OUTPUT;
3673 $default = $this->get_defaultsetting();
3674 if (is_array($default)) {
3675 $defaultinfo = $default['h'].':'.$default['m'];
3676 } else {
3677 $defaultinfo = NULL;
3680 $context = (object) [
3681 'id' => $this->get_id(),
3682 'name' => $this->get_full_name(),
3683 'readonly' => $this->is_readonly(),
3684 'hours' => array_map(function($i) use ($data) {
3685 return [
3686 'value' => $i,
3687 'name' => $i,
3688 'selected' => $i == $data['h']
3690 }, range(0, 23)),
3691 'minutes' => array_map(function($i) use ($data) {
3692 return [
3693 'value' => $i,
3694 'name' => $i,
3695 'selected' => $i == $data['m']
3697 }, range(0, 59, 5))
3700 $element = $OUTPUT->render_from_template('core_admin/setting_configtime', $context);
3702 return format_admin_setting($this, $this->visiblename, $element, $this->description,
3703 $this->get_id() . 'h', '', $defaultinfo, $query);
3710 * Seconds duration setting.
3712 * @copyright 2012 Petr Skoda (http://skodak.org)
3713 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3715 class admin_setting_configduration extends admin_setting {
3717 /** @var int default duration unit */
3718 protected $defaultunit;
3721 * Constructor
3722 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3723 * or 'myplugin/mysetting' for ones in config_plugins.
3724 * @param string $visiblename localised name
3725 * @param string $description localised long description
3726 * @param mixed $defaultsetting string or array depending on implementation
3727 * @param int $defaultunit - day, week, etc. (in seconds)
3729 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3730 if (is_number($defaultsetting)) {
3731 $defaultsetting = self::parse_seconds($defaultsetting);
3733 $units = self::get_units();
3734 if (isset($units[$defaultunit])) {
3735 $this->defaultunit = $defaultunit;
3736 } else {
3737 $this->defaultunit = 86400;
3739 parent::__construct($name, $visiblename, $description, $defaultsetting);
3743 * Returns selectable units.
3744 * @static
3745 * @return array
3747 protected static function get_units() {
3748 return array(
3749 604800 => get_string('weeks'),
3750 86400 => get_string('days'),
3751 3600 => get_string('hours'),
3752 60 => get_string('minutes'),
3753 1 => get_string('seconds'),
3758 * Converts seconds to some more user friendly string.
3759 * @static
3760 * @param int $seconds
3761 * @return string
3763 protected static function get_duration_text($seconds) {
3764 if (empty($seconds)) {
3765 return get_string('none');
3767 $data = self::parse_seconds($seconds);
3768 switch ($data['u']) {
3769 case (60*60*24*7):
3770 return get_string('numweeks', '', $data['v']);
3771 case (60*60*24):
3772 return get_string('numdays', '', $data['v']);
3773 case (60*60):
3774 return get_string('numhours', '', $data['v']);
3775 case (60):
3776 return get_string('numminutes', '', $data['v']);
3777 default:
3778 return get_string('numseconds', '', $data['v']*$data['u']);
3783 * Finds suitable units for given duration.
3784 * @static
3785 * @param int $seconds
3786 * @return array
3788 protected static function parse_seconds($seconds) {
3789 foreach (self::get_units() as $unit => $unused) {
3790 if ($seconds % $unit === 0) {
3791 return array('v'=>(int)($seconds/$unit), 'u'=>$unit);
3794 return array('v'=>(int)$seconds, 'u'=>1);
3798 * Get the selected duration as array.
3800 * @return mixed An array containing 'v'=>xx, 'u'=>xx, or null if not set
3802 public function get_setting() {
3803 $seconds = $this->config_read($this->name);
3804 if (is_null($seconds)) {
3805 return null;
3808 return self::parse_seconds($seconds);
3812 * Store the duration as seconds.
3814 * @param array $data Must be form 'h'=>xx, 'm'=>xx
3815 * @return bool true if success, false if not
3817 public function write_setting($data) {
3818 if (!is_array($data)) {
3819 return '';
3822 $seconds = (int)($data['v']*$data['u']);
3823 if ($seconds < 0) {
3824 return get_string('errorsetting', 'admin');
3827 $result = $this->config_write($this->name, $seconds);
3828 return ($result ? '' : get_string('errorsetting', 'admin'));
3832 * Returns duration text+select fields.
3834 * @param array $data Must be form 'v'=>xx, 'u'=>xx
3835 * @param string $query
3836 * @return string duration text+select fields and wrapping div(s)
3838 public function output_html($data, $query='') {
3839 global $OUTPUT;
3841 $default = $this->get_defaultsetting();
3842 if (is_number($default)) {
3843 $defaultinfo = self::get_duration_text($default);
3844 } else if (is_array($default)) {
3845 $defaultinfo = self::get_duration_text($default['v']*$default['u']);
3846 } else {
3847 $defaultinfo = null;
3850 $inputid = $this->get_id() . 'v';
3851 $units = self::get_units();
3852 $defaultunit = $this->defaultunit;
3854 $context = (object) [
3855 'id' => $this->get_id(),
3856 'name' => $this->get_full_name(),
3857 'value' => $data['v'],
3858 'readonly' => $this->is_readonly(),
3859 'options' => array_map(function($unit) use ($units, $data, $defaultunit) {
3860 return [
3861 'value' => $unit,
3862 'name' => $units[$unit],
3863 'selected' => ($data['v'] == 0 && $unit == $defaultunit) || $unit == $data['u']
3865 }, array_keys($units))
3868 $element = $OUTPUT->render_from_template('core_admin/setting_configduration', $context);
3870 return format_admin_setting($this, $this->visiblename, $element, $this->description, $inputid, '', $defaultinfo, $query);
3876 * Seconds duration setting with an advanced checkbox, that controls a additional
3877 * $name.'_adv' setting.
3879 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3880 * @copyright 2014 The Open University
3882 class admin_setting_configduration_with_advanced extends admin_setting_configduration {
3884 * Constructor
3885 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
3886 * or 'myplugin/mysetting' for ones in config_plugins.
3887 * @param string $visiblename localised name
3888 * @param string $description localised long description
3889 * @param array $defaultsetting array of int value, and bool whether it is
3890 * is advanced by default.
3891 * @param int $defaultunit - day, week, etc. (in seconds)
3893 public function __construct($name, $visiblename, $description, $defaultsetting, $defaultunit = 86400) {
3894 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $defaultunit);
3895 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
3901 * Used to validate a textarea used for ip addresses
3903 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3904 * @copyright 2011 Petr Skoda (http://skodak.org)
3906 class admin_setting_configiplist extends admin_setting_configtextarea {
3909 * Validate the contents of the textarea as IP addresses
3911 * Used to validate a new line separated list of IP addresses collected from
3912 * a textarea control
3914 * @param string $data A list of IP Addresses separated by new lines
3915 * @return mixed bool true for success or string:error on failure
3917 public function validate($data) {
3918 if(!empty($data)) {
3919 $lines = explode("\n", $data);
3920 } else {
3921 return true;
3923 $result = true;
3924 $badips = array();
3925 foreach ($lines as $line) {
3926 $tokens = explode('#', $line);
3927 $ip = trim($tokens[0]);
3928 if (empty($ip)) {
3929 continue;
3931 if (preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}$#', $ip, $match) ||
3932 preg_match('#^(\d{1,3})(\.\d{1,3}){0,3}(\/\d{1,2})$#', $ip, $match) ||
3933 preg_match('#^(\d{1,3})(\.\d{1,3}){3}(-\d{1,3})$#', $ip, $match)) {
3934 } else {
3935 $result = false;
3936 $badips[] = $ip;
3939 if($result) {
3940 return true;
3941 } else {
3942 return get_string('validateiperror', 'admin', join(', ', $badips));
3948 * Used to validate a textarea used for domain names, wildcard domain names and IP addresses/ranges (both IPv4 and IPv6 format).
3950 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
3951 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
3953 class admin_setting_configmixedhostiplist extends admin_setting_configtextarea {
3956 * Validate the contents of the textarea as either IP addresses, domain name or wildcard domain name (RFC 4592).
3957 * Used to validate a new line separated list of entries collected from a textarea control.
3959 * This setting provides support for internationalised domain names (IDNs), however, such UTF-8 names will be converted to
3960 * their ascii-compatible encoding (punycode) on save, and converted back to their UTF-8 representation when fetched
3961 * via the get_setting() method, which has been overriden.
3963 * @param string $data A list of FQDNs, DNS wildcard format domains, and IP addresses, separated by new lines.
3964 * @return mixed bool true for success or string:error on failure
3966 public function validate($data) {
3967 if (empty($data)) {
3968 return true;
3970 $entries = explode("\n", $data);
3971 $badentries = [];
3973 foreach ($entries as $key => $entry) {
3974 $entry = trim($entry);
3975 if (empty($entry)) {
3976 return get_string('validateemptylineerror', 'admin');
3979 // Validate each string entry against the supported formats.
3980 if (\core\ip_utils::is_ip_address($entry) || \core\ip_utils::is_ipv6_range($entry)
3981 || \core\ip_utils::is_ipv4_range($entry) || \core\ip_utils::is_domain_name($entry)
3982 || \core\ip_utils::is_domain_matching_pattern($entry)) {
3983 continue;
3986 // Otherwise, the entry is invalid.
3987 $badentries[] = $entry;
3990 if ($badentries) {
3991 return get_string('validateerrorlist', 'admin', join(', ', $badentries));
3993 return true;
3997 * Convert any lines containing international domain names (IDNs) to their ascii-compatible encoding (ACE).
3999 * @param string $data the setting data, as sent from the web form.
4000 * @return string $data the setting data, with all IDNs converted (using punycode) to their ascii encoded version.
4002 protected function ace_encode($data) {
4003 if (empty($data)) {
4004 return $data;
4006 $entries = explode("\n", $data);
4007 foreach ($entries as $key => $entry) {
4008 $entry = trim($entry);
4009 // This regex matches any string that has non-ascii character.
4010 if (preg_match('/[^\x00-\x7f]/', $entry)) {
4011 // If we can convert the unicode string to an idn, do so.
4012 // Otherwise, leave the original unicode string alone and let the validation function handle it (it will fail).
4013 $val = idn_to_ascii($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4014 $entries[$key] = $val ? $val : $entry;
4017 return implode("\n", $entries);
4021 * Decode any ascii-encoded domain names back to their utf-8 representation for display.
4023 * @param string $data the setting data, as found in the database.
4024 * @return string $data the setting data, with all ascii-encoded IDNs decoded back to their utf-8 representation.
4026 protected function ace_decode($data) {
4027 $entries = explode("\n", $data);
4028 foreach ($entries as $key => $entry) {
4029 $entry = trim($entry);
4030 if (strpos($entry, 'xn--') !== false) {
4031 $entries[$key] = idn_to_utf8($entry, IDNA_NONTRANSITIONAL_TO_ASCII, INTL_IDNA_VARIANT_UTS46);
4034 return implode("\n", $entries);
4038 * Override, providing utf8-decoding for ascii-encoded IDN strings.
4040 * @return mixed returns punycode-converted setting string if successful, else null.
4042 public function get_setting() {
4043 // Here, we need to decode any ascii-encoded IDNs back to their native, utf-8 representation.
4044 $data = $this->config_read($this->name);
4045 if (function_exists('idn_to_utf8') && !is_null($data)) {
4046 $data = $this->ace_decode($data);
4048 return $data;
4052 * Override, providing ascii-encoding for utf8 (native) IDN strings.
4054 * @param string $data
4055 * @return string
4057 public function write_setting($data) {
4058 if ($this->paramtype === PARAM_INT and $data === '') {
4059 // Do not complain if '' used instead of 0.
4060 $data = 0;
4063 // Try to convert any non-ascii domains to ACE prior to validation - we can't modify anything in validate!
4064 if (function_exists('idn_to_ascii')) {
4065 $data = $this->ace_encode($data);
4068 $validated = $this->validate($data);
4069 if ($validated !== true) {
4070 return $validated;
4072 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
4077 * Used to validate a textarea used for port numbers.
4079 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4080 * @copyright 2016 Jake Dallimore (jrhdallimore@gmail.com)
4082 class admin_setting_configportlist extends admin_setting_configtextarea {
4085 * Validate the contents of the textarea as port numbers.
4086 * Used to validate a new line separated list of ports collected from a textarea control.
4088 * @param string $data A list of ports separated by new lines
4089 * @return mixed bool true for success or string:error on failure
4091 public function validate($data) {
4092 if (empty($data)) {
4093 return true;
4095 $ports = explode("\n", $data);
4096 $badentries = [];
4097 foreach ($ports as $port) {
4098 $port = trim($port);
4099 if (empty($port)) {
4100 return get_string('validateemptylineerror', 'admin');
4103 // Is the string a valid integer number?
4104 if (strval(intval($port)) !== $port || intval($port) <= 0) {
4105 $badentries[] = $port;
4108 if ($badentries) {
4109 return get_string('validateerrorlist', 'admin', $badentries);
4111 return true;
4117 * An admin setting for selecting one or more users who have a capability
4118 * in the system context
4120 * An admin setting for selecting one or more users, who have a particular capability
4121 * in the system context. Warning, make sure the list will never be too long. There is
4122 * no paging or searching of this list.
4124 * To correctly get a list of users from this config setting, you need to call the
4125 * get_users_from_config($CFG->mysetting, $capability); function in moodlelib.php.
4127 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4129 class admin_setting_users_with_capability extends admin_setting_configmultiselect {
4130 /** @var string The capabilities name */
4131 protected $capability;
4132 /** @var int include admin users too */
4133 protected $includeadmins;
4136 * Constructor.
4138 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
4139 * @param string $visiblename localised name
4140 * @param string $description localised long description
4141 * @param array $defaultsetting array of usernames
4142 * @param string $capability string capability name.
4143 * @param bool $includeadmins include administrators
4145 function __construct($name, $visiblename, $description, $defaultsetting, $capability, $includeadmins = true) {
4146 $this->capability = $capability;
4147 $this->includeadmins = $includeadmins;
4148 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
4152 * Load all of the uses who have the capability into choice array
4154 * @return bool Always returns true
4156 function load_choices() {
4157 if (is_array($this->choices)) {
4158 return true;
4160 list($sort, $sortparams) = users_order_by_sql('u');
4161 if (!empty($sortparams)) {
4162 throw new coding_exception('users_order_by_sql returned some query parameters. ' .
4163 'This is unexpected, and a problem because there is no way to pass these ' .
4164 'parameters to get_users_by_capability. See MDL-34657.');
4166 $userfields = 'u.id, u.username, ' . get_all_user_name_fields(true, 'u');
4167 $users = get_users_by_capability(context_system::instance(), $this->capability, $userfields, $sort);
4168 $this->choices = array(
4169 '$@NONE@$' => get_string('nobody'),
4170 '$@ALL@$' => get_string('everyonewhocan', 'admin', get_capability_string($this->capability)),
4172 if ($this->includeadmins) {
4173 $admins = get_admins();
4174 foreach ($admins as $user) {
4175 $this->choices[$user->id] = fullname($user);
4178 if (is_array($users)) {
4179 foreach ($users as $user) {
4180 $this->choices[$user->id] = fullname($user);
4183 return true;
4187 * Returns the default setting for class
4189 * @return mixed Array, or string. Empty string if no default
4191 public function get_defaultsetting() {
4192 $this->load_choices();
4193 $defaultsetting = parent::get_defaultsetting();
4194 if (empty($defaultsetting)) {
4195 return array('$@NONE@$');
4196 } else if (array_key_exists($defaultsetting, $this->choices)) {
4197 return $defaultsetting;
4198 } else {
4199 return '';
4204 * Returns the current setting
4206 * @return mixed array or string
4208 public function get_setting() {
4209 $result = parent::get_setting();
4210 if ($result === null) {
4211 // this is necessary for settings upgrade
4212 return null;
4214 if (empty($result)) {
4215 $result = array('$@NONE@$');
4217 return $result;
4221 * Save the chosen setting provided as $data
4223 * @param array $data
4224 * @return mixed string or array
4226 public function write_setting($data) {
4227 // If all is selected, remove any explicit options.
4228 if (in_array('$@ALL@$', $data)) {
4229 $data = array('$@ALL@$');
4231 // None never needs to be written to the DB.
4232 if (in_array('$@NONE@$', $data)) {
4233 unset($data[array_search('$@NONE@$', $data)]);
4235 return parent::write_setting($data);
4241 * Special checkbox for calendar - resets SESSION vars.
4243 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4245 class admin_setting_special_adminseesall extends admin_setting_configcheckbox {
4247 * Calls the parent::__construct with default values
4249 * name => calendar_adminseesall
4250 * visiblename => get_string('adminseesall', 'admin')
4251 * description => get_string('helpadminseesall', 'admin')
4252 * defaultsetting => 0
4254 public function __construct() {
4255 parent::__construct('calendar_adminseesall', get_string('adminseesall', 'admin'),
4256 get_string('helpadminseesall', 'admin'), '0');
4260 * Stores the setting passed in $data
4262 * @param mixed gets converted to string for comparison
4263 * @return string empty string or error message
4265 public function write_setting($data) {
4266 global $SESSION;
4267 return parent::write_setting($data);
4272 * Special select for settings that are altered in setup.php and can not be altered on the fly
4274 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4276 class admin_setting_special_selectsetup extends admin_setting_configselect {
4278 * Reads the setting directly from the database
4280 * @return mixed
4282 public function get_setting() {
4283 // read directly from db!
4284 return get_config(NULL, $this->name);
4288 * Save the setting passed in $data
4290 * @param string $data The setting to save
4291 * @return string empty or error message
4293 public function write_setting($data) {
4294 global $CFG;
4295 // do not change active CFG setting!
4296 $current = $CFG->{$this->name};
4297 $result = parent::write_setting($data);
4298 $CFG->{$this->name} = $current;
4299 return $result;
4305 * Special select for frontpage - stores data in course table
4307 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4309 class admin_setting_sitesetselect extends admin_setting_configselect {
4311 * Returns the site name for the selected site
4313 * @see get_site()
4314 * @return string The site name of the selected site
4316 public function get_setting() {
4317 $site = course_get_format(get_site())->get_course();
4318 return $site->{$this->name};
4322 * Updates the database and save the setting
4324 * @param string data
4325 * @return string empty or error message
4327 public function write_setting($data) {
4328 global $DB, $SITE, $COURSE;
4329 if (!in_array($data, array_keys($this->choices))) {
4330 return get_string('errorsetting', 'admin');
4332 $record = new stdClass();
4333 $record->id = SITEID;
4334 $temp = $this->name;
4335 $record->$temp = $data;
4336 $record->timemodified = time();
4338 course_get_format($SITE)->update_course_format_options($record);
4339 $DB->update_record('course', $record);
4341 // Reset caches.
4342 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4343 if ($SITE->id == $COURSE->id) {
4344 $COURSE = $SITE;
4346 format_base::reset_course_cache($SITE->id);
4348 return '';
4355 * Select for blog's bloglevel setting: if set to 0, will set blog_menu
4356 * block to hidden.
4358 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4360 class admin_setting_bloglevel extends admin_setting_configselect {
4362 * Updates the database and save the setting
4364 * @param string data
4365 * @return string empty or error message
4367 public function write_setting($data) {
4368 global $DB, $CFG;
4369 if ($data == 0) {
4370 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 1");
4371 foreach ($blogblocks as $block) {
4372 $DB->set_field('block', 'visible', 0, array('id' => $block->id));
4374 } else {
4375 // reenable all blocks only when switching from disabled blogs
4376 if (isset($CFG->bloglevel) and $CFG->bloglevel == 0) {
4377 $blogblocks = $DB->get_records_select('block', "name LIKE 'blog_%' AND visible = 0");
4378 foreach ($blogblocks as $block) {
4379 $DB->set_field('block', 'visible', 1, array('id' => $block->id));
4383 return parent::write_setting($data);
4389 * Special select - lists on the frontpage - hacky
4391 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4393 class admin_setting_courselist_frontpage extends admin_setting {
4394 /** @var array Array of choices value=>label */
4395 public $choices;
4398 * Construct override, requires one param
4400 * @param bool $loggedin Is the user logged in
4402 public function __construct($loggedin) {
4403 global $CFG;
4404 require_once($CFG->dirroot.'/course/lib.php');
4405 $name = 'frontpage'.($loggedin ? 'loggedin' : '');
4406 $visiblename = get_string('frontpage'.($loggedin ? 'loggedin' : ''),'admin');
4407 $description = get_string('configfrontpage'.($loggedin ? 'loggedin' : ''),'admin');
4408 $defaults = array(FRONTPAGEALLCOURSELIST);
4409 parent::__construct($name, $visiblename, $description, $defaults);
4413 * Loads the choices available
4415 * @return bool always returns true
4417 public function load_choices() {
4418 if (is_array($this->choices)) {
4419 return true;
4421 $this->choices = array(FRONTPAGENEWS => get_string('frontpagenews'),
4422 FRONTPAGEALLCOURSELIST => get_string('frontpagecourselist'),
4423 FRONTPAGEENROLLEDCOURSELIST => get_string('frontpageenrolledcourselist'),
4424 FRONTPAGECATEGORYNAMES => get_string('frontpagecategorynames'),
4425 FRONTPAGECATEGORYCOMBO => get_string('frontpagecategorycombo'),
4426 FRONTPAGECOURSESEARCH => get_string('frontpagecoursesearch'),
4427 'none' => get_string('none'));
4428 if ($this->name === 'frontpage') {
4429 unset($this->choices[FRONTPAGEENROLLEDCOURSELIST]);
4431 return true;
4435 * Returns the selected settings
4437 * @param mixed array or setting or null
4439 public function get_setting() {
4440 $result = $this->config_read($this->name);
4441 if (is_null($result)) {
4442 return NULL;
4444 if ($result === '') {
4445 return array();
4447 return explode(',', $result);
4451 * Save the selected options
4453 * @param array $data
4454 * @return mixed empty string (data is not an array) or bool true=success false=failure
4456 public function write_setting($data) {
4457 if (!is_array($data)) {
4458 return '';
4460 $this->load_choices();
4461 $save = array();
4462 foreach($data as $datum) {
4463 if ($datum == 'none' or !array_key_exists($datum, $this->choices)) {
4464 continue;
4466 $save[$datum] = $datum; // no duplicates
4468 return ($this->config_write($this->name, implode(',', $save)) ? '' : get_string('errorsetting', 'admin'));
4472 * Return XHTML select field and wrapping div
4474 * @todo Add vartype handling to make sure $data is an array
4475 * @param array $data Array of elements to select by default
4476 * @return string XHTML select field and wrapping div
4478 public function output_html($data, $query='') {
4479 global $OUTPUT;
4481 $this->load_choices();
4482 $currentsetting = array();
4483 foreach ($data as $key) {
4484 if ($key != 'none' and array_key_exists($key, $this->choices)) {
4485 $currentsetting[] = $key; // already selected first
4489 $context = (object) [
4490 'id' => $this->get_id(),
4491 'name' => $this->get_full_name(),
4494 $options = $this->choices;
4495 $selects = [];
4496 for ($i = 0; $i < count($this->choices) - 1; $i++) {
4497 if (!array_key_exists($i, $currentsetting)) {
4498 $currentsetting[$i] = 'none';
4500 $selects[] = [
4501 'key' => $i,
4502 'options' => array_map(function($option) use ($options, $currentsetting, $i) {
4503 return [
4504 'name' => $options[$option],
4505 'value' => $option,
4506 'selected' => $currentsetting[$i] == $option
4508 }, array_keys($options))
4511 $context->selects = $selects;
4513 $element = $OUTPUT->render_from_template('core_admin/setting_courselist_frontpage', $context);
4515 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
4521 * Special checkbox for frontpage - stores data in course table
4523 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4525 class admin_setting_sitesetcheckbox extends admin_setting_configcheckbox {
4527 * Returns the current sites name
4529 * @return string
4531 public function get_setting() {
4532 $site = course_get_format(get_site())->get_course();
4533 return $site->{$this->name};
4537 * Save the selected setting
4539 * @param string $data The selected site
4540 * @return string empty string or error message
4542 public function write_setting($data) {
4543 global $DB, $SITE, $COURSE;
4544 $record = new stdClass();
4545 $record->id = $SITE->id;
4546 $record->{$this->name} = ($data == '1' ? 1 : 0);
4547 $record->timemodified = time();
4549 course_get_format($SITE)->update_course_format_options($record);
4550 $DB->update_record('course', $record);
4552 // Reset caches.
4553 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4554 if ($SITE->id == $COURSE->id) {
4555 $COURSE = $SITE;
4557 format_base::reset_course_cache($SITE->id);
4559 return '';
4564 * Special text for frontpage - stores data in course table.
4565 * Empty string means not set here. Manual setting is required.
4567 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4569 class admin_setting_sitesettext extends admin_setting_configtext {
4572 * Constructor.
4574 public function __construct() {
4575 call_user_func_array(['parent', '__construct'], func_get_args());
4576 $this->set_force_ltr(false);
4580 * Return the current setting
4582 * @return mixed string or null
4584 public function get_setting() {
4585 $site = course_get_format(get_site())->get_course();
4586 return $site->{$this->name} != '' ? $site->{$this->name} : NULL;
4590 * Validate the selected data
4592 * @param string $data The selected value to validate
4593 * @return mixed true or message string
4595 public function validate($data) {
4596 global $DB, $SITE;
4597 $cleaned = clean_param($data, PARAM_TEXT);
4598 if ($cleaned === '') {
4599 return get_string('required');
4601 if ($this->name ==='shortname' &&
4602 $DB->record_exists_sql('SELECT id from {course} WHERE shortname = ? AND id <> ?', array($data, $SITE->id))) {
4603 return get_string('shortnametaken', 'error', $data);
4605 if ("$data" == "$cleaned") { // implicit conversion to string is needed to do exact comparison
4606 return true;
4607 } else {
4608 return get_string('validateerror', 'admin');
4613 * Save the selected setting
4615 * @param string $data The selected value
4616 * @return string empty or error message
4618 public function write_setting($data) {
4619 global $DB, $SITE, $COURSE;
4620 $data = trim($data);
4621 $validated = $this->validate($data);
4622 if ($validated !== true) {
4623 return $validated;
4626 $record = new stdClass();
4627 $record->id = $SITE->id;
4628 $record->{$this->name} = $data;
4629 $record->timemodified = time();
4631 course_get_format($SITE)->update_course_format_options($record);
4632 $DB->update_record('course', $record);
4634 // Reset caches.
4635 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4636 if ($SITE->id == $COURSE->id) {
4637 $COURSE = $SITE;
4639 format_base::reset_course_cache($SITE->id);
4641 return '';
4647 * Special text editor for site description.
4649 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4651 class admin_setting_special_frontpagedesc extends admin_setting_confightmleditor {
4654 * Calls parent::__construct with specific arguments
4656 public function __construct() {
4657 parent::__construct('summary', get_string('frontpagedescription'), get_string('frontpagedescriptionhelp'), null,
4658 PARAM_RAW, 60, 15);
4662 * Return the current setting
4663 * @return string The current setting
4665 public function get_setting() {
4666 $site = course_get_format(get_site())->get_course();
4667 return $site->{$this->name};
4671 * Save the new setting
4673 * @param string $data The new value to save
4674 * @return string empty or error message
4676 public function write_setting($data) {
4677 global $DB, $SITE, $COURSE;
4678 $record = new stdClass();
4679 $record->id = $SITE->id;
4680 $record->{$this->name} = $data;
4681 $record->timemodified = time();
4683 course_get_format($SITE)->update_course_format_options($record);
4684 $DB->update_record('course', $record);
4686 // Reset caches.
4687 $SITE = $DB->get_record('course', array('id'=>$SITE->id), '*', MUST_EXIST);
4688 if ($SITE->id == $COURSE->id) {
4689 $COURSE = $SITE;
4691 format_base::reset_course_cache($SITE->id);
4693 return '';
4699 * Administration interface for emoticon_manager settings.
4701 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4703 class admin_setting_emoticons extends admin_setting {
4706 * Calls parent::__construct with specific args
4708 public function __construct() {
4709 global $CFG;
4711 $manager = get_emoticon_manager();
4712 $defaults = $this->prepare_form_data($manager->default_emoticons());
4713 parent::__construct('emoticons', get_string('emoticons', 'admin'), get_string('emoticons_desc', 'admin'), $defaults);
4717 * Return the current setting(s)
4719 * @return array Current settings array
4721 public function get_setting() {
4722 global $CFG;
4724 $manager = get_emoticon_manager();
4726 $config = $this->config_read($this->name);
4727 if (is_null($config)) {
4728 return null;
4731 $config = $manager->decode_stored_config($config);
4732 if (is_null($config)) {
4733 return null;
4736 return $this->prepare_form_data($config);
4740 * Save selected settings
4742 * @param array $data Array of settings to save
4743 * @return bool
4745 public function write_setting($data) {
4747 $manager = get_emoticon_manager();
4748 $emoticons = $this->process_form_data($data);
4750 if ($emoticons === false) {
4751 return false;
4754 if ($this->config_write($this->name, $manager->encode_stored_config($emoticons))) {
4755 return ''; // success
4756 } else {
4757 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
4762 * Return XHTML field(s) for options
4764 * @param array $data Array of options to set in HTML
4765 * @return string XHTML string for the fields and wrapping div(s)
4767 public function output_html($data, $query='') {
4768 global $OUTPUT;
4770 $context = (object) [
4771 'name' => $this->get_full_name(),
4772 'emoticons' => [],
4773 'forceltr' => true,
4776 $i = 0;
4777 foreach ($data as $field => $value) {
4779 // When $i == 0: text.
4780 // When $i == 1: imagename.
4781 // When $i == 2: imagecomponent.
4782 // When $i == 3: altidentifier.
4783 // When $i == 4: altcomponent.
4784 $fields[$i] = (object) [
4785 'field' => $field,
4786 'value' => $value,
4787 'index' => $i
4789 $i++;
4791 if ($i > 4) {
4792 $icon = null;
4793 if (!empty($fields[1]->value)) {
4794 if (get_string_manager()->string_exists($fields[3]->value, $fields[4]->value)) {
4795 $alt = get_string($fields[3]->value, $fields[4]->value);
4796 } else {
4797 $alt = $fields[0]->value;
4799 $icon = new pix_emoticon($fields[1]->value, $alt, $fields[2]->value);
4801 $context->emoticons[] = [
4802 'fields' => $fields,
4803 'icon' => $icon ? $icon->export_for_template($OUTPUT) : null
4805 $fields = [];
4806 $i = 0;
4810 $context->reseturl = new moodle_url('/admin/resetemoticons.php');
4811 $element = $OUTPUT->render_from_template('core_admin/setting_emoticons', $context);
4812 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
4816 * Converts the array of emoticon objects provided by {@see emoticon_manager} into admin settings form data
4818 * @see self::process_form_data()
4819 * @param array $emoticons array of emoticon objects as returned by {@see emoticon_manager}
4820 * @return array of form fields and their values
4822 protected function prepare_form_data(array $emoticons) {
4824 $form = array();
4825 $i = 0;
4826 foreach ($emoticons as $emoticon) {
4827 $form['text'.$i] = $emoticon->text;
4828 $form['imagename'.$i] = $emoticon->imagename;
4829 $form['imagecomponent'.$i] = $emoticon->imagecomponent;
4830 $form['altidentifier'.$i] = $emoticon->altidentifier;
4831 $form['altcomponent'.$i] = $emoticon->altcomponent;
4832 $i++;
4834 // add one more blank field set for new object
4835 $form['text'.$i] = '';
4836 $form['imagename'.$i] = '';
4837 $form['imagecomponent'.$i] = '';
4838 $form['altidentifier'.$i] = '';
4839 $form['altcomponent'.$i] = '';
4841 return $form;
4845 * Converts the data from admin settings form into an array of emoticon objects
4847 * @see self::prepare_form_data()
4848 * @param array $data array of admin form fields and values
4849 * @return false|array of emoticon objects
4851 protected function process_form_data(array $form) {
4853 $count = count($form); // number of form field values
4855 if ($count % 5) {
4856 // we must get five fields per emoticon object
4857 return false;
4860 $emoticons = array();
4861 for ($i = 0; $i < $count / 5; $i++) {
4862 $emoticon = new stdClass();
4863 $emoticon->text = clean_param(trim($form['text'.$i]), PARAM_NOTAGS);
4864 $emoticon->imagename = clean_param(trim($form['imagename'.$i]), PARAM_PATH);
4865 $emoticon->imagecomponent = clean_param(trim($form['imagecomponent'.$i]), PARAM_COMPONENT);
4866 $emoticon->altidentifier = clean_param(trim($form['altidentifier'.$i]), PARAM_STRINGID);
4867 $emoticon->altcomponent = clean_param(trim($form['altcomponent'.$i]), PARAM_COMPONENT);
4869 if (strpos($emoticon->text, ':/') !== false or strpos($emoticon->text, '//') !== false) {
4870 // prevent from breaking http://url.addresses by accident
4871 $emoticon->text = '';
4874 if (strlen($emoticon->text) < 2) {
4875 // do not allow single character emoticons
4876 $emoticon->text = '';
4879 if (preg_match('/^[a-zA-Z]+[a-zA-Z0-9]*$/', $emoticon->text)) {
4880 // emoticon text must contain some non-alphanumeric character to prevent
4881 // breaking HTML tags
4882 $emoticon->text = '';
4885 if ($emoticon->text !== '' and $emoticon->imagename !== '' and $emoticon->imagecomponent !== '') {
4886 $emoticons[] = $emoticon;
4889 return $emoticons;
4896 * Special setting for limiting of the list of available languages.
4898 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4900 class admin_setting_langlist extends admin_setting_configtext {
4902 * Calls parent::__construct with specific arguments
4904 public function __construct() {
4905 parent::__construct('langlist', get_string('langlist', 'admin'), get_string('configlanglist', 'admin'), '', PARAM_NOTAGS);
4909 * Validate that each language identifier exists on the site
4911 * @param string $data
4912 * @return bool|string True if validation successful, otherwise error string
4914 public function validate($data) {
4915 $parentcheck = parent::validate($data);
4916 if ($parentcheck !== true) {
4917 return $parentcheck;
4920 if ($data === '') {
4921 return true;
4924 // Normalize language identifiers.
4925 $langcodes = array_map('trim', explode(',', $data));
4926 foreach ($langcodes as $langcode) {
4927 // If the langcode contains optional alias, split it out.
4928 [$langcode, ] = preg_split('/\s*\|\s*/', $langcode, 2);
4930 if (!get_string_manager()->translation_exists($langcode)) {
4931 return get_string('invalidlanguagecode', 'error', $langcode);
4935 return true;
4939 * Save the new setting
4941 * @param string $data The new setting
4942 * @return bool
4944 public function write_setting($data) {
4945 $return = parent::write_setting($data);
4946 get_string_manager()->reset_caches();
4947 return $return;
4953 * Allows to specify comma separated list of known country codes.
4955 * This is a simple subclass of the plain input text field with added validation so that all the codes are actually
4956 * known codes.
4958 * @package core
4959 * @category admin
4960 * @copyright 2020 David Mudrák <david@moodle.com>
4961 * @license https://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
4963 class admin_setting_countrycodes extends admin_setting_configtext {
4966 * Construct the instance of the setting.
4968 * @param string $name Name of the admin setting such as 'allcountrycodes' or 'myplugin/countries'.
4969 * @param lang_string|string $visiblename Language string with the field label text.
4970 * @param lang_string|string $description Language string with the field description text.
4971 * @param string $defaultsetting Default value of the setting.
4972 * @param int $size Input text field size.
4974 public function __construct($name, $visiblename, $description, $defaultsetting = '', $size = null) {
4975 parent::__construct($name, $visiblename, $description, $defaultsetting, '/^(?:\w+(?:,\w+)*)?$/', $size);
4979 * Validate the setting value before storing it.
4981 * The value is first validated through custom regex so that it is a word consisting of letters, numbers or underscore; or
4982 * a comma separated list of such words.
4984 * @param string $data Value inserted into the setting field.
4985 * @return bool|string True if the value is OK, error string otherwise.
4987 public function validate($data) {
4989 $parentcheck = parent::validate($data);
4991 if ($parentcheck !== true) {
4992 return $parentcheck;
4995 if ($data === '') {
4996 return true;
4999 $allcountries = get_string_manager()->get_list_of_countries(true);
5001 foreach (explode(',', $data) as $code) {
5002 if (!isset($allcountries[$code])) {
5003 return get_string('invalidcountrycode', 'core_error', $code);
5007 return true;
5013 * Selection of one of the recognised countries using the list
5014 * returned by {@link get_list_of_countries()}.
5016 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5018 class admin_settings_country_select extends admin_setting_configselect {
5019 protected $includeall;
5020 public function __construct($name, $visiblename, $description, $defaultsetting, $includeall=false) {
5021 $this->includeall = $includeall;
5022 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
5026 * Lazy-load the available choices for the select box
5028 public function load_choices() {
5029 global $CFG;
5030 if (is_array($this->choices)) {
5031 return true;
5033 $this->choices = array_merge(
5034 array('0' => get_string('choosedots')),
5035 get_string_manager()->get_list_of_countries($this->includeall));
5036 return true;
5042 * admin_setting_configselect for the default number of sections in a course,
5043 * simply so we can lazy-load the choices.
5045 * @copyright 2011 The Open University
5046 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5048 class admin_settings_num_course_sections extends admin_setting_configselect {
5049 public function __construct($name, $visiblename, $description, $defaultsetting) {
5050 parent::__construct($name, $visiblename, $description, $defaultsetting, array());
5053 /** Lazy-load the available choices for the select box */
5054 public function load_choices() {
5055 $max = get_config('moodlecourse', 'maxsections');
5056 if (!isset($max) || !is_numeric($max)) {
5057 $max = 52;
5059 for ($i = 0; $i <= $max; $i++) {
5060 $this->choices[$i] = "$i";
5062 return true;
5068 * Course category selection
5070 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5072 class admin_settings_coursecat_select extends admin_setting_configselect {
5074 * Calls parent::__construct with specific arguments
5076 public function __construct($name, $visiblename, $description, $defaultsetting) {
5077 parent::__construct($name, $visiblename, $description, $defaultsetting, NULL);
5081 * Load the available choices for the select box
5083 * @return bool
5085 public function load_choices() {
5086 global $CFG;
5087 require_once($CFG->dirroot.'/course/lib.php');
5088 if (is_array($this->choices)) {
5089 return true;
5091 $this->choices = make_categories_options();
5092 return true;
5098 * Special control for selecting days to backup
5100 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5102 class admin_setting_special_backupdays extends admin_setting_configmulticheckbox2 {
5104 * Calls parent::__construct with specific arguments
5106 public function __construct() {
5107 parent::__construct('backup_auto_weekdays', get_string('automatedbackupschedule','backup'), get_string('automatedbackupschedulehelp','backup'), array(), NULL);
5108 $this->plugin = 'backup';
5112 * Load the available choices for the select box
5114 * @return bool Always returns true
5116 public function load_choices() {
5117 if (is_array($this->choices)) {
5118 return true;
5120 $this->choices = array();
5121 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5122 foreach ($days as $day) {
5123 $this->choices[$day] = get_string($day, 'calendar');
5125 return true;
5130 * Special setting for backup auto destination.
5132 * @package core
5133 * @subpackage admin
5134 * @copyright 2014 Frédéric Massart - FMCorz.net
5135 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5137 class admin_setting_special_backup_auto_destination extends admin_setting_configdirectory {
5140 * Calls parent::__construct with specific arguments.
5142 public function __construct() {
5143 parent::__construct('backup/backup_auto_destination', new lang_string('saveto'), new lang_string('backupsavetohelp'), '');
5147 * Check if the directory must be set, depending on backup/backup_auto_storage.
5149 * Note: backup/backup_auto_storage must be specified BEFORE this setting otherwise
5150 * there will be conflicts if this validation happens before the other one.
5152 * @param string $data Form data.
5153 * @return string Empty when no errors.
5155 public function write_setting($data) {
5156 $storage = (int) get_config('backup', 'backup_auto_storage');
5157 if ($storage !== 0) {
5158 if (empty($data) || !file_exists($data) || !is_dir($data) || !is_writable($data) ) {
5159 // The directory must exist and be writable.
5160 return get_string('backuperrorinvaliddestination');
5163 return parent::write_setting($data);
5169 * Special debug setting
5171 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5173 class admin_setting_special_debug extends admin_setting_configselect {
5175 * Calls parent::__construct with specific arguments
5177 public function __construct() {
5178 parent::__construct('debug', get_string('debug', 'admin'), get_string('configdebug', 'admin'), DEBUG_NONE, NULL);
5182 * Load the available choices for the select box
5184 * @return bool
5186 public function load_choices() {
5187 if (is_array($this->choices)) {
5188 return true;
5190 $this->choices = array(DEBUG_NONE => get_string('debugnone', 'admin'),
5191 DEBUG_MINIMAL => get_string('debugminimal', 'admin'),
5192 DEBUG_NORMAL => get_string('debugnormal', 'admin'),
5193 DEBUG_ALL => get_string('debugall', 'admin'),
5194 DEBUG_DEVELOPER => get_string('debugdeveloper', 'admin'));
5195 return true;
5201 * Special admin control
5203 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5205 class admin_setting_special_calendar_weekend extends admin_setting {
5207 * Calls parent::__construct with specific arguments
5209 public function __construct() {
5210 $name = 'calendar_weekend';
5211 $visiblename = get_string('calendar_weekend', 'admin');
5212 $description = get_string('helpweekenddays', 'admin');
5213 $default = array ('0', '6'); // Saturdays and Sundays
5214 parent::__construct($name, $visiblename, $description, $default);
5218 * Gets the current settings as an array
5220 * @return mixed Null if none, else array of settings
5222 public function get_setting() {
5223 $result = $this->config_read($this->name);
5224 if (is_null($result)) {
5225 return NULL;
5227 if ($result === '') {
5228 return array();
5230 $settings = array();
5231 for ($i=0; $i<7; $i++) {
5232 if ($result & (1 << $i)) {
5233 $settings[] = $i;
5236 return $settings;
5240 * Save the new settings
5242 * @param array $data Array of new settings
5243 * @return bool
5245 public function write_setting($data) {
5246 if (!is_array($data)) {
5247 return '';
5249 unset($data['xxxxx']);
5250 $result = 0;
5251 foreach($data as $index) {
5252 $result |= 1 << $index;
5254 return ($this->config_write($this->name, $result) ? '' : get_string('errorsetting', 'admin'));
5258 * Return XHTML to display the control
5260 * @param array $data array of selected days
5261 * @param string $query
5262 * @return string XHTML for display (field + wrapping div(s)
5264 public function output_html($data, $query='') {
5265 global $OUTPUT;
5267 // The order matters very much because of the implied numeric keys.
5268 $days = array('sunday', 'monday', 'tuesday', 'wednesday', 'thursday', 'friday', 'saturday');
5269 $context = (object) [
5270 'name' => $this->get_full_name(),
5271 'id' => $this->get_id(),
5272 'days' => array_map(function($index) use ($days, $data) {
5273 return [
5274 'index' => $index,
5275 'label' => get_string($days[$index], 'calendar'),
5276 'checked' => in_array($index, $data)
5278 }, array_keys($days))
5281 $element = $OUTPUT->render_from_template('core_admin/setting_special_calendar_weekend', $context);
5283 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', NULL, $query);
5290 * Admin setting that allows a user to pick a behaviour.
5292 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5294 class admin_setting_question_behaviour extends admin_setting_configselect {
5296 * @param string $name name of config variable
5297 * @param string $visiblename display name
5298 * @param string $description description
5299 * @param string $default default.
5301 public function __construct($name, $visiblename, $description, $default) {
5302 parent::__construct($name, $visiblename, $description, $default, null);
5306 * Load list of behaviours as choices
5307 * @return bool true => success, false => error.
5309 public function load_choices() {
5310 global $CFG;
5311 require_once($CFG->dirroot . '/question/engine/lib.php');
5312 $this->choices = question_engine::get_behaviour_options('');
5313 return true;
5319 * Admin setting that allows a user to pick appropriate roles for something.
5321 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5323 class admin_setting_pickroles extends admin_setting_configmulticheckbox {
5324 /** @var array Array of capabilities which identify roles */
5325 private $types;
5328 * @param string $name Name of config variable
5329 * @param string $visiblename Display name
5330 * @param string $description Description
5331 * @param array $types Array of archetypes which identify
5332 * roles that will be enabled by default.
5334 public function __construct($name, $visiblename, $description, $types) {
5335 parent::__construct($name, $visiblename, $description, NULL, NULL);
5336 $this->types = $types;
5340 * Load roles as choices
5342 * @return bool true=>success, false=>error
5344 public function load_choices() {
5345 global $CFG, $DB;
5346 if (during_initial_install()) {
5347 return false;
5349 if (is_array($this->choices)) {
5350 return true;
5352 if ($roles = get_all_roles()) {
5353 $this->choices = role_fix_names($roles, null, ROLENAME_ORIGINAL, true);
5354 return true;
5355 } else {
5356 return false;
5361 * Return the default setting for this control
5363 * @return array Array of default settings
5365 public function get_defaultsetting() {
5366 global $CFG;
5368 if (during_initial_install()) {
5369 return null;
5371 $result = array();
5372 foreach($this->types as $archetype) {
5373 if ($caproles = get_archetype_roles($archetype)) {
5374 foreach ($caproles as $caprole) {
5375 $result[$caprole->id] = 1;
5379 return $result;
5385 * Admin setting that is a list of installed filter plugins.
5387 * @copyright 2015 The Open University
5388 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5390 class admin_setting_pickfilters extends admin_setting_configmulticheckbox {
5393 * Constructor
5395 * @param string $name unique ascii name, either 'mysetting' for settings
5396 * that in config, or 'myplugin/mysetting' for ones in config_plugins.
5397 * @param string $visiblename localised name
5398 * @param string $description localised long description
5399 * @param array $default the default. E.g. array('urltolink' => 1, 'emoticons' => 1)
5401 public function __construct($name, $visiblename, $description, $default) {
5402 if (empty($default)) {
5403 $default = array();
5405 $this->load_choices();
5406 foreach ($default as $plugin) {
5407 if (!isset($this->choices[$plugin])) {
5408 unset($default[$plugin]);
5411 parent::__construct($name, $visiblename, $description, $default, null);
5414 public function load_choices() {
5415 if (is_array($this->choices)) {
5416 return true;
5418 $this->choices = array();
5420 foreach (core_component::get_plugin_list('filter') as $plugin => $unused) {
5421 $this->choices[$plugin] = filter_get_name($plugin);
5423 return true;
5429 * Text field with an advanced checkbox, that controls a additional $name.'_adv' setting.
5431 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5433 class admin_setting_configtext_with_advanced extends admin_setting_configtext {
5435 * Constructor
5436 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5437 * @param string $visiblename localised
5438 * @param string $description long localised info
5439 * @param array $defaultsetting ('value'=>string, '__construct'=>bool)
5440 * @param mixed $paramtype int means PARAM_XXX type, string is a allowed format in regex
5441 * @param int $size default field size
5443 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype=PARAM_RAW, $size=null) {
5444 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $paramtype, $size);
5445 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5451 * Checkbox with an advanced checkbox that controls an additional $name.'_adv' config setting.
5453 * @copyright 2009 Petr Skoda (http://skodak.org)
5454 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5456 class admin_setting_configcheckbox_with_advanced extends admin_setting_configcheckbox {
5459 * Constructor
5460 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5461 * @param string $visiblename localised
5462 * @param string $description long localised info
5463 * @param array $defaultsetting ('value'=>string, 'adv'=>bool)
5464 * @param string $yes value used when checked
5465 * @param string $no value used when not checked
5467 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5468 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5469 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5476 * Checkbox with an advanced checkbox that controls an additional $name.'_locked' config setting.
5478 * This is nearly a copy/paste of admin_setting_configcheckbox_with_adv
5480 * @copyright 2010 Sam Hemelryk
5481 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5483 class admin_setting_configcheckbox_with_lock extends admin_setting_configcheckbox {
5485 * Constructor
5486 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting' for ones in config_plugins.
5487 * @param string $visiblename localised
5488 * @param string $description long localised info
5489 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5490 * @param string $yes value used when checked
5491 * @param string $no value used when not checked
5493 public function __construct($name, $visiblename, $description, $defaultsetting, $yes='1', $no='0') {
5494 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $yes, $no);
5495 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5502 * Dropdown menu with an advanced checkbox, that controls a additional $name.'_adv' setting.
5504 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5506 class admin_setting_configselect_with_advanced extends admin_setting_configselect {
5508 * Calls parent::__construct with specific arguments
5510 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5511 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5512 $this->set_advanced_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['adv']));
5518 * Select with an advanced checkbox that controls an additional $name.'_locked' config setting.
5520 * @copyright 2017 Marina Glancy
5521 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5523 class admin_setting_configselect_with_lock extends admin_setting_configselect {
5525 * Constructor
5526 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
5527 * or 'myplugin/mysetting' for ones in config_plugins.
5528 * @param string $visiblename localised
5529 * @param string $description long localised info
5530 * @param array $defaultsetting ('value'=>string, 'locked'=>bool)
5531 * @param array $choices array of $value=>$label for each selection
5533 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5534 parent::__construct($name, $visiblename, $description, $defaultsetting['value'], $choices);
5535 $this->set_locked_flag_options(admin_setting_flag::ENABLED, !empty($defaultsetting['locked']));
5541 * Graded roles in gradebook
5543 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5545 class admin_setting_special_gradebookroles extends admin_setting_pickroles {
5547 * Calls parent::__construct with specific arguments
5549 public function __construct() {
5550 parent::__construct('gradebookroles', get_string('gradebookroles', 'admin'),
5551 get_string('configgradebookroles', 'admin'),
5552 array('student'));
5559 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5561 class admin_setting_regradingcheckbox extends admin_setting_configcheckbox {
5563 * Saves the new settings passed in $data
5565 * @param string $data
5566 * @return mixed string or Array
5568 public function write_setting($data) {
5569 global $CFG, $DB;
5571 $oldvalue = $this->config_read($this->name);
5572 $return = parent::write_setting($data);
5573 $newvalue = $this->config_read($this->name);
5575 if ($oldvalue !== $newvalue) {
5576 // force full regrading
5577 $DB->set_field('grade_items', 'needsupdate', 1, array('needsupdate'=>0));
5580 return $return;
5586 * Which roles to show on course description page
5588 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5590 class admin_setting_special_coursecontact extends admin_setting_pickroles {
5592 * Calls parent::__construct with specific arguments
5594 public function __construct() {
5595 parent::__construct('coursecontact', get_string('coursecontact', 'admin'),
5596 get_string('coursecontact_desc', 'admin'),
5597 array('editingteacher'));
5598 $this->set_updatedcallback(function (){
5599 cache::make('core', 'coursecontacts')->purge();
5607 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5609 class admin_setting_special_gradelimiting extends admin_setting_configcheckbox {
5611 * Calls parent::__construct with specific arguments
5613 public function __construct() {
5614 parent::__construct('unlimitedgrades', get_string('unlimitedgrades', 'grades'),
5615 get_string('unlimitedgrades_help', 'grades'), '0', '1', '0');
5619 * Old syntax of class constructor. Deprecated in PHP7.
5621 * @deprecated since Moodle 3.1
5623 public function admin_setting_special_gradelimiting() {
5624 debugging('Use of class name as constructor is deprecated', DEBUG_DEVELOPER);
5625 self::__construct();
5629 * Force site regrading
5631 function regrade_all() {
5632 global $CFG;
5633 require_once("$CFG->libdir/gradelib.php");
5634 grade_force_site_regrading();
5638 * Saves the new settings
5640 * @param mixed $data
5641 * @return string empty string or error message
5643 function write_setting($data) {
5644 $previous = $this->get_setting();
5646 if ($previous === null) {
5647 if ($data) {
5648 $this->regrade_all();
5650 } else {
5651 if ($data != $previous) {
5652 $this->regrade_all();
5655 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
5661 * Special setting for $CFG->grade_minmaxtouse.
5663 * @package core
5664 * @copyright 2015 Frédéric Massart - FMCorz.net
5665 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5667 class admin_setting_special_grademinmaxtouse extends admin_setting_configselect {
5670 * Constructor.
5672 public function __construct() {
5673 parent::__construct('grade_minmaxtouse', new lang_string('minmaxtouse', 'grades'),
5674 new lang_string('minmaxtouse_desc', 'grades'), GRADE_MIN_MAX_FROM_GRADE_ITEM,
5675 array(
5676 GRADE_MIN_MAX_FROM_GRADE_ITEM => get_string('gradeitemminmax', 'grades'),
5677 GRADE_MIN_MAX_FROM_GRADE_GRADE => get_string('gradegrademinmax', 'grades')
5683 * Saves the new setting.
5685 * @param mixed $data
5686 * @return string empty string or error message
5688 function write_setting($data) {
5689 global $CFG;
5691 $previous = $this->get_setting();
5692 $result = parent::write_setting($data);
5694 // If saved and the value has changed.
5695 if (empty($result) && $previous != $data) {
5696 require_once($CFG->libdir . '/gradelib.php');
5697 grade_force_site_regrading();
5700 return $result;
5707 * Primary grade export plugin - has state tracking.
5709 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5711 class admin_setting_special_gradeexport extends admin_setting_configmulticheckbox {
5713 * Calls parent::__construct with specific arguments
5715 public function __construct() {
5716 parent::__construct('gradeexport', get_string('gradeexport', 'admin'),
5717 get_string('configgradeexport', 'admin'), array(), NULL);
5721 * Load the available choices for the multicheckbox
5723 * @return bool always returns true
5725 public function load_choices() {
5726 if (is_array($this->choices)) {
5727 return true;
5729 $this->choices = array();
5731 if ($plugins = core_component::get_plugin_list('gradeexport')) {
5732 foreach($plugins as $plugin => $unused) {
5733 $this->choices[$plugin] = get_string('pluginname', 'gradeexport_'.$plugin);
5736 return true;
5742 * A setting for setting the default grade point value. Must be an integer between 1 and $CFG->gradepointmax.
5744 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5746 class admin_setting_special_gradepointdefault extends admin_setting_configtext {
5748 * Config gradepointmax constructor
5750 * @param string $name Overidden by "gradepointmax"
5751 * @param string $visiblename Overridden by "gradepointmax" language string.
5752 * @param string $description Overridden by "gradepointmax_help" language string.
5753 * @param string $defaultsetting Not used, overridden by 100.
5754 * @param mixed $paramtype Overridden by PARAM_INT.
5755 * @param int $size Overridden by 5.
5757 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
5758 $name = 'gradepointdefault';
5759 $visiblename = get_string('gradepointdefault', 'grades');
5760 $description = get_string('gradepointdefault_help', 'grades');
5761 $defaultsetting = 100;
5762 $paramtype = PARAM_INT;
5763 $size = 5;
5764 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
5768 * Validate data before storage
5769 * @param string $data The submitted data
5770 * @return bool|string true if ok, string if error found
5772 public function validate($data) {
5773 global $CFG;
5774 if (((string)(int)$data === (string)$data && $data > 0 && $data <= $CFG->gradepointmax)) {
5775 return true;
5776 } else {
5777 return get_string('gradepointdefault_validateerror', 'grades');
5784 * A setting for setting the maximum grade value. Must be an integer between 1 and 10000.
5786 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5788 class admin_setting_special_gradepointmax extends admin_setting_configtext {
5791 * Config gradepointmax constructor
5793 * @param string $name Overidden by "gradepointmax"
5794 * @param string $visiblename Overridden by "gradepointmax" language string.
5795 * @param string $description Overridden by "gradepointmax_help" language string.
5796 * @param string $defaultsetting Not used, overridden by 100.
5797 * @param mixed $paramtype Overridden by PARAM_INT.
5798 * @param int $size Overridden by 5.
5800 public function __construct($name = '', $visiblename = '', $description = '', $defaultsetting = '', $paramtype = PARAM_INT, $size = 5) {
5801 $name = 'gradepointmax';
5802 $visiblename = get_string('gradepointmax', 'grades');
5803 $description = get_string('gradepointmax_help', 'grades');
5804 $defaultsetting = 100;
5805 $paramtype = PARAM_INT;
5806 $size = 5;
5807 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $size);
5811 * Save the selected setting
5813 * @param string $data The selected site
5814 * @return string empty string or error message
5816 public function write_setting($data) {
5817 if ($data === '') {
5818 $data = (int)$this->defaultsetting;
5819 } else {
5820 $data = $data;
5822 return parent::write_setting($data);
5826 * Validate data before storage
5827 * @param string $data The submitted data
5828 * @return bool|string true if ok, string if error found
5830 public function validate($data) {
5831 if (((string)(int)$data === (string)$data && $data > 0 && $data <= 10000)) {
5832 return true;
5833 } else {
5834 return get_string('gradepointmax_validateerror', 'grades');
5839 * Return an XHTML string for the setting
5840 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5841 * @param string $query search query to be highlighted
5842 * @return string XHTML to display control
5844 public function output_html($data, $query = '') {
5845 global $OUTPUT;
5847 $default = $this->get_defaultsetting();
5848 $context = (object) [
5849 'size' => $this->size,
5850 'id' => $this->get_id(),
5851 'name' => $this->get_full_name(),
5852 'value' => $data,
5853 'attributes' => [
5854 'maxlength' => 5
5856 'forceltr' => $this->get_force_ltr()
5858 $element = $OUTPUT->render_from_template('core_admin/setting_configtext', $context);
5860 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
5866 * Grade category settings
5868 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
5870 class admin_setting_gradecat_combo extends admin_setting {
5871 /** @var array Array of choices */
5872 public $choices;
5875 * Sets choices and calls parent::__construct with passed arguments
5876 * @param string $name
5877 * @param string $visiblename
5878 * @param string $description
5879 * @param mixed $defaultsetting string or array depending on implementation
5880 * @param array $choices An array of choices for the control
5882 public function __construct($name, $visiblename, $description, $defaultsetting, $choices) {
5883 $this->choices = $choices;
5884 parent::__construct($name, $visiblename, $description, $defaultsetting);
5888 * Return the current setting(s) array
5890 * @return array Array of value=>xx, forced=>xx, adv=>xx
5892 public function get_setting() {
5893 global $CFG;
5895 $value = $this->config_read($this->name);
5896 $flag = $this->config_read($this->name.'_flag');
5898 if (is_null($value) or is_null($flag)) {
5899 return NULL;
5902 $flag = (int)$flag;
5903 $forced = (boolean)(1 & $flag); // first bit
5904 $adv = (boolean)(2 & $flag); // second bit
5906 return array('value' => $value, 'forced' => $forced, 'adv' => $adv);
5910 * Save the new settings passed in $data
5912 * @todo Add vartype handling to ensure $data is array
5913 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5914 * @return string empty or error message
5916 public function write_setting($data) {
5917 global $CFG;
5919 $value = $data['value'];
5920 $forced = empty($data['forced']) ? 0 : 1;
5921 $adv = empty($data['adv']) ? 0 : 2;
5922 $flag = ($forced | $adv); //bitwise or
5924 if (!in_array($value, array_keys($this->choices))) {
5925 return 'Error setting ';
5928 $oldvalue = $this->config_read($this->name);
5929 $oldflag = (int)$this->config_read($this->name.'_flag');
5930 $oldforced = (1 & $oldflag); // first bit
5932 $result1 = $this->config_write($this->name, $value);
5933 $result2 = $this->config_write($this->name.'_flag', $flag);
5935 // force regrade if needed
5936 if ($oldforced != $forced or ($forced and $value != $oldvalue)) {
5937 require_once($CFG->libdir.'/gradelib.php');
5938 grade_category::updated_forced_settings();
5941 if ($result1 and $result2) {
5942 return '';
5943 } else {
5944 return get_string('errorsetting', 'admin');
5949 * Return XHTML to display the field and wrapping div
5951 * @todo Add vartype handling to ensure $data is array
5952 * @param array $data Associative array of value=>xx, forced=>xx, adv=>xx
5953 * @param string $query
5954 * @return string XHTML to display control
5956 public function output_html($data, $query='') {
5957 global $OUTPUT;
5959 $value = $data['value'];
5961 $default = $this->get_defaultsetting();
5962 if (!is_null($default)) {
5963 $defaultinfo = array();
5964 if (isset($this->choices[$default['value']])) {
5965 $defaultinfo[] = $this->choices[$default['value']];
5967 if (!empty($default['forced'])) {
5968 $defaultinfo[] = get_string('force');
5970 if (!empty($default['adv'])) {
5971 $defaultinfo[] = get_string('advanced');
5973 $defaultinfo = implode(', ', $defaultinfo);
5975 } else {
5976 $defaultinfo = NULL;
5979 $options = $this->choices;
5980 $context = (object) [
5981 'id' => $this->get_id(),
5982 'name' => $this->get_full_name(),
5983 'forced' => !empty($data['forced']),
5984 'advanced' => !empty($data['adv']),
5985 'options' => array_map(function($option) use ($options, $value) {
5986 return [
5987 'value' => $option,
5988 'name' => $options[$option],
5989 'selected' => $option == $value
5991 }, array_keys($options)),
5994 $element = $OUTPUT->render_from_template('core_admin/setting_gradecat_combo', $context);
5996 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $defaultinfo, $query);
6002 * Selection of grade report in user profiles
6004 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6006 class admin_setting_grade_profilereport extends admin_setting_configselect {
6008 * Calls parent::__construct with specific arguments
6010 public function __construct() {
6011 parent::__construct('grade_profilereport', get_string('profilereport', 'grades'), get_string('profilereport_help', 'grades'), 'user', null);
6015 * Loads an array of choices for the configselect control
6017 * @return bool always return true
6019 public function load_choices() {
6020 if (is_array($this->choices)) {
6021 return true;
6023 $this->choices = array();
6025 global $CFG;
6026 require_once($CFG->libdir.'/gradelib.php');
6028 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6029 if (file_exists($plugindir.'/lib.php')) {
6030 require_once($plugindir.'/lib.php');
6031 $functionname = 'grade_report_'.$plugin.'_profilereport';
6032 if (function_exists($functionname)) {
6033 $this->choices[$plugin] = get_string('pluginname', 'gradereport_'.$plugin);
6037 return true;
6042 * Provides a selection of grade reports to be used for "grades".
6044 * @copyright 2015 Adrian Greeve <adrian@moodle.com>
6045 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6047 class admin_setting_my_grades_report extends admin_setting_configselect {
6050 * Calls parent::__construct with specific arguments.
6052 public function __construct() {
6053 parent::__construct('grade_mygrades_report', new lang_string('mygrades', 'grades'),
6054 new lang_string('mygrades_desc', 'grades'), 'overview', null);
6058 * Loads an array of choices for the configselect control.
6060 * @return bool always returns true.
6062 public function load_choices() {
6063 global $CFG; // Remove this line and behold the horror of behat test failures!
6064 $this->choices = array();
6065 foreach (core_component::get_plugin_list('gradereport') as $plugin => $plugindir) {
6066 if (file_exists($plugindir . '/lib.php')) {
6067 require_once($plugindir . '/lib.php');
6068 // Check to see if the class exists. Check the correct plugin convention first.
6069 if (class_exists('gradereport_' . $plugin)) {
6070 $classname = 'gradereport_' . $plugin;
6071 } else if (class_exists('grade_report_' . $plugin)) {
6072 // We are using the old plugin naming convention.
6073 $classname = 'grade_report_' . $plugin;
6074 } else {
6075 continue;
6077 if ($classname::supports_mygrades()) {
6078 $this->choices[$plugin] = get_string('pluginname', 'gradereport_' . $plugin);
6082 // Add an option to specify an external url.
6083 $this->choices['external'] = get_string('externalurl', 'grades');
6084 return true;
6089 * Special class for register auth selection
6091 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6093 class admin_setting_special_registerauth extends admin_setting_configselect {
6095 * Calls parent::__construct with specific arguments
6097 public function __construct() {
6098 parent::__construct('registerauth', get_string('selfregistration', 'auth'), get_string('selfregistration_help', 'auth'), '', null);
6102 * Returns the default option
6104 * @return string empty or default option
6106 public function get_defaultsetting() {
6107 $this->load_choices();
6108 $defaultsetting = parent::get_defaultsetting();
6109 if (array_key_exists($defaultsetting, $this->choices)) {
6110 return $defaultsetting;
6111 } else {
6112 return '';
6117 * Loads the possible choices for the array
6119 * @return bool always returns true
6121 public function load_choices() {
6122 global $CFG;
6124 if (is_array($this->choices)) {
6125 return true;
6127 $this->choices = array();
6128 $this->choices[''] = get_string('disable');
6130 $authsenabled = get_enabled_auth_plugins();
6132 foreach ($authsenabled as $auth) {
6133 $authplugin = get_auth_plugin($auth);
6134 if (!$authplugin->can_signup()) {
6135 continue;
6137 // Get the auth title (from core or own auth lang files)
6138 $authtitle = $authplugin->get_title();
6139 $this->choices[$auth] = $authtitle;
6141 return true;
6147 * General plugins manager
6149 class admin_page_pluginsoverview extends admin_externalpage {
6152 * Sets basic information about the external page
6154 public function __construct() {
6155 global $CFG;
6156 parent::__construct('pluginsoverview', get_string('pluginsoverview', 'core_admin'),
6157 "$CFG->wwwroot/$CFG->admin/plugins.php");
6162 * Module manage page
6164 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6166 class admin_page_managemods extends admin_externalpage {
6168 * Calls parent::__construct with specific arguments
6170 public function __construct() {
6171 global $CFG;
6172 parent::__construct('managemodules', get_string('modsettings', 'admin'), "$CFG->wwwroot/$CFG->admin/modules.php");
6176 * Try to find the specified module
6178 * @param string $query The module to search for
6179 * @return array
6181 public function search($query) {
6182 global $CFG, $DB;
6183 if ($result = parent::search($query)) {
6184 return $result;
6187 $found = false;
6188 if ($modules = $DB->get_records('modules')) {
6189 foreach ($modules as $module) {
6190 if (!file_exists("$CFG->dirroot/mod/$module->name/lib.php")) {
6191 continue;
6193 if (strpos($module->name, $query) !== false) {
6194 $found = true;
6195 break;
6197 $strmodulename = get_string('modulename', $module->name);
6198 if (strpos(core_text::strtolower($strmodulename), $query) !== false) {
6199 $found = true;
6200 break;
6204 if ($found) {
6205 $result = new stdClass();
6206 $result->page = $this;
6207 $result->settings = array();
6208 return array($this->name => $result);
6209 } else {
6210 return array();
6217 * Special class for enrol plugins management.
6219 * @copyright 2010 Petr Skoda {@link http://skodak.org}
6220 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6222 class admin_setting_manageenrols extends admin_setting {
6224 * Calls parent::__construct with specific arguments
6226 public function __construct() {
6227 $this->nosave = true;
6228 parent::__construct('enrolsui', get_string('manageenrols', 'enrol'), '', '');
6232 * Always returns true, does nothing
6234 * @return true
6236 public function get_setting() {
6237 return true;
6241 * Always returns true, does nothing
6243 * @return true
6245 public function get_defaultsetting() {
6246 return true;
6250 * Always returns '', does not write anything
6252 * @return string Always returns ''
6254 public function write_setting($data) {
6255 // do not write any setting
6256 return '';
6260 * Checks if $query is one of the available enrol plugins
6262 * @param string $query The string to search for
6263 * @return bool Returns true if found, false if not
6265 public function is_related($query) {
6266 if (parent::is_related($query)) {
6267 return true;
6270 $query = core_text::strtolower($query);
6271 $enrols = enrol_get_plugins(false);
6272 foreach ($enrols as $name=>$enrol) {
6273 $localised = get_string('pluginname', 'enrol_'.$name);
6274 if (strpos(core_text::strtolower($name), $query) !== false) {
6275 return true;
6277 if (strpos(core_text::strtolower($localised), $query) !== false) {
6278 return true;
6281 return false;
6285 * Builds the XHTML to display the control
6287 * @param string $data Unused
6288 * @param string $query
6289 * @return string
6291 public function output_html($data, $query='') {
6292 global $CFG, $OUTPUT, $DB, $PAGE;
6294 // Display strings.
6295 $strup = get_string('up');
6296 $strdown = get_string('down');
6297 $strsettings = get_string('settings');
6298 $strenable = get_string('enable');
6299 $strdisable = get_string('disable');
6300 $struninstall = get_string('uninstallplugin', 'core_admin');
6301 $strusage = get_string('enrolusage', 'enrol');
6302 $strversion = get_string('version');
6303 $strtest = get_string('testsettings', 'core_enrol');
6305 $pluginmanager = core_plugin_manager::instance();
6307 $enrols_available = enrol_get_plugins(false);
6308 $active_enrols = enrol_get_plugins(true);
6310 $allenrols = array();
6311 foreach ($active_enrols as $key=>$enrol) {
6312 $allenrols[$key] = true;
6314 foreach ($enrols_available as $key=>$enrol) {
6315 $allenrols[$key] = true;
6317 // Now find all borked plugins and at least allow then to uninstall.
6318 $condidates = $DB->get_fieldset_sql("SELECT DISTINCT enrol FROM {enrol}");
6319 foreach ($condidates as $candidate) {
6320 if (empty($allenrols[$candidate])) {
6321 $allenrols[$candidate] = true;
6325 $return = $OUTPUT->heading(get_string('actenrolshhdr', 'enrol'), 3, 'main', true);
6326 $return .= $OUTPUT->box_start('generalbox enrolsui');
6328 $table = new html_table();
6329 $table->head = array(get_string('name'), $strusage, $strversion, $strenable, $strup.'/'.$strdown, $strsettings, $strtest, $struninstall);
6330 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6331 $table->id = 'courseenrolmentplugins';
6332 $table->attributes['class'] = 'admintable generaltable';
6333 $table->data = array();
6335 // Iterate through enrol plugins and add to the display table.
6336 $updowncount = 1;
6337 $enrolcount = count($active_enrols);
6338 $url = new moodle_url('/admin/enrol.php', array('sesskey'=>sesskey()));
6339 $printed = array();
6340 foreach($allenrols as $enrol => $unused) {
6341 $plugininfo = $pluginmanager->get_plugin_info('enrol_'.$enrol);
6342 $version = get_config('enrol_'.$enrol, 'version');
6343 if ($version === false) {
6344 $version = '';
6347 if (get_string_manager()->string_exists('pluginname', 'enrol_'.$enrol)) {
6348 $name = get_string('pluginname', 'enrol_'.$enrol);
6349 } else {
6350 $name = $enrol;
6352 // Usage.
6353 $ci = $DB->count_records('enrol', array('enrol'=>$enrol));
6354 $cp = $DB->count_records_select('user_enrolments', "enrolid IN (SELECT id FROM {enrol} WHERE enrol = ?)", array($enrol));
6355 $usage = "$ci / $cp";
6357 // Hide/show links.
6358 $class = '';
6359 if (isset($active_enrols[$enrol])) {
6360 $aurl = new moodle_url($url, array('action'=>'disable', 'enrol'=>$enrol));
6361 $hideshow = "<a href=\"$aurl\">";
6362 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
6363 $enabled = true;
6364 $displayname = $name;
6365 } else if (isset($enrols_available[$enrol])) {
6366 $aurl = new moodle_url($url, array('action'=>'enable', 'enrol'=>$enrol));
6367 $hideshow = "<a href=\"$aurl\">";
6368 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
6369 $enabled = false;
6370 $displayname = $name;
6371 $class = 'dimmed_text';
6372 } else {
6373 $hideshow = '';
6374 $enabled = false;
6375 $displayname = '<span class="notifyproblem">'.$name.'</span>';
6377 if ($PAGE->theme->resolve_image_location('icon', 'enrol_' . $name, false)) {
6378 $icon = $OUTPUT->pix_icon('icon', '', 'enrol_' . $name, array('class' => 'icon pluginicon'));
6379 } else {
6380 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
6383 // Up/down link (only if enrol is enabled).
6384 $updown = '';
6385 if ($enabled) {
6386 if ($updowncount > 1) {
6387 $aurl = new moodle_url($url, array('action'=>'up', 'enrol'=>$enrol));
6388 $updown .= "<a href=\"$aurl\">";
6389 $updown .= $OUTPUT->pix_icon('t/up', $strup) . '</a>&nbsp;';
6390 } else {
6391 $updown .= $OUTPUT->spacer() . '&nbsp;';
6393 if ($updowncount < $enrolcount) {
6394 $aurl = new moodle_url($url, array('action'=>'down', 'enrol'=>$enrol));
6395 $updown .= "<a href=\"$aurl\">";
6396 $updown .= $OUTPUT->pix_icon('t/down', $strdown) . '</a>&nbsp;';
6397 } else {
6398 $updown .= $OUTPUT->spacer() . '&nbsp;';
6400 ++$updowncount;
6403 // Add settings link.
6404 if (!$version) {
6405 $settings = '';
6406 } else if ($surl = $plugininfo->get_settings_url()) {
6407 $settings = html_writer::link($surl, $strsettings);
6408 } else {
6409 $settings = '';
6412 // Add uninstall info.
6413 $uninstall = '';
6414 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('enrol_'.$enrol, 'manage')) {
6415 $uninstall = html_writer::link($uninstallurl, $struninstall);
6418 $test = '';
6419 if (!empty($enrols_available[$enrol]) and method_exists($enrols_available[$enrol], 'test_settings')) {
6420 $testsettingsurl = new moodle_url('/enrol/test_settings.php', array('enrol'=>$enrol, 'sesskey'=>sesskey()));
6421 $test = html_writer::link($testsettingsurl, $strtest);
6424 // Add a row to the table.
6425 $row = new html_table_row(array($icon.$displayname, $usage, $version, $hideshow, $updown, $settings, $test, $uninstall));
6426 if ($class) {
6427 $row->attributes['class'] = $class;
6429 $table->data[] = $row;
6431 $printed[$enrol] = true;
6434 $return .= html_writer::table($table);
6435 $return .= get_string('configenrolplugins', 'enrol').'<br />'.get_string('tablenosave', 'admin');
6436 $return .= $OUTPUT->box_end();
6437 return highlight($query, $return);
6443 * Blocks manage page
6445 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6447 class admin_page_manageblocks extends admin_externalpage {
6449 * Calls parent::__construct with specific arguments
6451 public function __construct() {
6452 global $CFG;
6453 parent::__construct('manageblocks', get_string('blocksettings', 'admin'), "$CFG->wwwroot/$CFG->admin/blocks.php");
6457 * Search for a specific block
6459 * @param string $query The string to search for
6460 * @return array
6462 public function search($query) {
6463 global $CFG, $DB;
6464 if ($result = parent::search($query)) {
6465 return $result;
6468 $found = false;
6469 if ($blocks = $DB->get_records('block')) {
6470 foreach ($blocks as $block) {
6471 if (!file_exists("$CFG->dirroot/blocks/$block->name/")) {
6472 continue;
6474 if (strpos($block->name, $query) !== false) {
6475 $found = true;
6476 break;
6478 $strblockname = get_string('pluginname', 'block_'.$block->name);
6479 if (strpos(core_text::strtolower($strblockname), $query) !== false) {
6480 $found = true;
6481 break;
6485 if ($found) {
6486 $result = new stdClass();
6487 $result->page = $this;
6488 $result->settings = array();
6489 return array($this->name => $result);
6490 } else {
6491 return array();
6497 * Message outputs configuration
6499 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6501 class admin_page_managemessageoutputs extends admin_externalpage {
6503 * Calls parent::__construct with specific arguments
6505 public function __construct() {
6506 global $CFG;
6507 parent::__construct('managemessageoutputs',
6508 get_string('defaultmessageoutputs', 'message'),
6509 new moodle_url('/admin/message.php')
6514 * Search for a specific message processor
6516 * @param string $query The string to search for
6517 * @return array
6519 public function search($query) {
6520 global $CFG, $DB;
6521 if ($result = parent::search($query)) {
6522 return $result;
6525 $found = false;
6526 if ($processors = get_message_processors()) {
6527 foreach ($processors as $processor) {
6528 if (!$processor->available) {
6529 continue;
6531 if (strpos($processor->name, $query) !== false) {
6532 $found = true;
6533 break;
6535 $strprocessorname = get_string('pluginname', 'message_'.$processor->name);
6536 if (strpos(core_text::strtolower($strprocessorname), $query) !== false) {
6537 $found = true;
6538 break;
6542 if ($found) {
6543 $result = new stdClass();
6544 $result->page = $this;
6545 $result->settings = array();
6546 return array($this->name => $result);
6547 } else {
6548 return array();
6554 * Default message outputs configuration
6556 * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
6557 * @todo MDL-64866 This will be deleted in Moodle 4.1.
6559 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6561 class admin_page_defaultmessageoutputs extends admin_page_managemessageoutputs {
6563 * Calls parent::__construct with specific arguments
6565 * @deprecated since Moodle 3.7 MDL-64495. Please use admin_page_managemessageoutputs instead.
6566 * @todo MDL-64866 This will be deleted in Moodle 4.1.
6568 public function __construct() {
6569 global $CFG;
6571 debugging('admin_page_defaultmessageoutputs class is deprecated. Please use admin_page_managemessageoutputs instead.',
6572 DEBUG_DEVELOPER);
6574 admin_externalpage::__construct('defaultmessageoutputs', get_string('defaultmessageoutputs', 'message'), new moodle_url('/message/defaultoutputs.php'));
6580 * Manage question behaviours page
6582 * @copyright 2011 The Open University
6583 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6585 class admin_page_manageqbehaviours extends admin_externalpage {
6587 * Constructor
6589 public function __construct() {
6590 global $CFG;
6591 parent::__construct('manageqbehaviours', get_string('manageqbehaviours', 'admin'),
6592 new moodle_url('/admin/qbehaviours.php'));
6596 * Search question behaviours for the specified string
6598 * @param string $query The string to search for in question behaviours
6599 * @return array
6601 public function search($query) {
6602 global $CFG;
6603 if ($result = parent::search($query)) {
6604 return $result;
6607 $found = false;
6608 require_once($CFG->dirroot . '/question/engine/lib.php');
6609 foreach (core_component::get_plugin_list('qbehaviour') as $behaviour => $notused) {
6610 if (strpos(core_text::strtolower(question_engine::get_behaviour_name($behaviour)),
6611 $query) !== false) {
6612 $found = true;
6613 break;
6616 if ($found) {
6617 $result = new stdClass();
6618 $result->page = $this;
6619 $result->settings = array();
6620 return array($this->name => $result);
6621 } else {
6622 return array();
6629 * Question type manage page
6631 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6633 class admin_page_manageqtypes extends admin_externalpage {
6635 * Calls parent::__construct with specific arguments
6637 public function __construct() {
6638 global $CFG;
6639 parent::__construct('manageqtypes', get_string('manageqtypes', 'admin'),
6640 new moodle_url('/admin/qtypes.php'));
6644 * Search question types for the specified string
6646 * @param string $query The string to search for in question types
6647 * @return array
6649 public function search($query) {
6650 global $CFG;
6651 if ($result = parent::search($query)) {
6652 return $result;
6655 $found = false;
6656 require_once($CFG->dirroot . '/question/engine/bank.php');
6657 foreach (question_bank::get_all_qtypes() as $qtype) {
6658 if (strpos(core_text::strtolower($qtype->local_name()), $query) !== false) {
6659 $found = true;
6660 break;
6663 if ($found) {
6664 $result = new stdClass();
6665 $result->page = $this;
6666 $result->settings = array();
6667 return array($this->name => $result);
6668 } else {
6669 return array();
6675 class admin_page_manageportfolios extends admin_externalpage {
6677 * Calls parent::__construct with specific arguments
6679 public function __construct() {
6680 global $CFG;
6681 parent::__construct('manageportfolios', get_string('manageportfolios', 'portfolio'),
6682 "$CFG->wwwroot/$CFG->admin/portfolio.php");
6686 * Searches page for the specified string.
6687 * @param string $query The string to search for
6688 * @return bool True if it is found on this page
6690 public function search($query) {
6691 global $CFG;
6692 if ($result = parent::search($query)) {
6693 return $result;
6696 $found = false;
6697 $portfolios = core_component::get_plugin_list('portfolio');
6698 foreach ($portfolios as $p => $dir) {
6699 if (strpos($p, $query) !== false) {
6700 $found = true;
6701 break;
6704 if (!$found) {
6705 foreach (portfolio_instances(false, false) as $instance) {
6706 $title = $instance->get('name');
6707 if (strpos(core_text::strtolower($title), $query) !== false) {
6708 $found = true;
6709 break;
6714 if ($found) {
6715 $result = new stdClass();
6716 $result->page = $this;
6717 $result->settings = array();
6718 return array($this->name => $result);
6719 } else {
6720 return array();
6726 class admin_page_managerepositories extends admin_externalpage {
6728 * Calls parent::__construct with specific arguments
6730 public function __construct() {
6731 global $CFG;
6732 parent::__construct('managerepositories', get_string('manage',
6733 'repository'), "$CFG->wwwroot/$CFG->admin/repository.php");
6737 * Searches page for the specified string.
6738 * @param string $query The string to search for
6739 * @return bool True if it is found on this page
6741 public function search($query) {
6742 global $CFG;
6743 if ($result = parent::search($query)) {
6744 return $result;
6747 $found = false;
6748 $repositories= core_component::get_plugin_list('repository');
6749 foreach ($repositories as $p => $dir) {
6750 if (strpos($p, $query) !== false) {
6751 $found = true;
6752 break;
6755 if (!$found) {
6756 foreach (repository::get_types() as $instance) {
6757 $title = $instance->get_typename();
6758 if (strpos(core_text::strtolower($title), $query) !== false) {
6759 $found = true;
6760 break;
6765 if ($found) {
6766 $result = new stdClass();
6767 $result->page = $this;
6768 $result->settings = array();
6769 return array($this->name => $result);
6770 } else {
6771 return array();
6778 * Special class for authentication administration.
6780 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
6782 class admin_setting_manageauths extends admin_setting {
6784 * Calls parent::__construct with specific arguments
6786 public function __construct() {
6787 $this->nosave = true;
6788 parent::__construct('authsui', get_string('authsettings', 'admin'), '', '');
6792 * Always returns true
6794 * @return true
6796 public function get_setting() {
6797 return true;
6801 * Always returns true
6803 * @return true
6805 public function get_defaultsetting() {
6806 return true;
6810 * Always returns '' and doesn't write anything
6812 * @return string Always returns ''
6814 public function write_setting($data) {
6815 // do not write any setting
6816 return '';
6820 * Search to find if Query is related to auth plugin
6822 * @param string $query The string to search for
6823 * @return bool true for related false for not
6825 public function is_related($query) {
6826 if (parent::is_related($query)) {
6827 return true;
6830 $authsavailable = core_component::get_plugin_list('auth');
6831 foreach ($authsavailable as $auth => $dir) {
6832 if (strpos($auth, $query) !== false) {
6833 return true;
6835 $authplugin = get_auth_plugin($auth);
6836 $authtitle = $authplugin->get_title();
6837 if (strpos(core_text::strtolower($authtitle), $query) !== false) {
6838 return true;
6841 return false;
6845 * Return XHTML to display control
6847 * @param mixed $data Unused
6848 * @param string $query
6849 * @return string highlight
6851 public function output_html($data, $query='') {
6852 global $CFG, $OUTPUT, $DB;
6854 // display strings
6855 $txt = get_strings(array('authenticationplugins', 'users', 'administration',
6856 'settings', 'edit', 'name', 'enable', 'disable',
6857 'up', 'down', 'none', 'users'));
6858 $txt->updown = "$txt->up/$txt->down";
6859 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
6860 $txt->testsettings = get_string('testsettings', 'core_auth');
6862 $authsavailable = core_component::get_plugin_list('auth');
6863 get_enabled_auth_plugins(true); // fix the list of enabled auths
6864 if (empty($CFG->auth)) {
6865 $authsenabled = array();
6866 } else {
6867 $authsenabled = explode(',', $CFG->auth);
6870 // construct the display array, with enabled auth plugins at the top, in order
6871 $displayauths = array();
6872 $registrationauths = array();
6873 $registrationauths[''] = $txt->disable;
6874 $authplugins = array();
6875 foreach ($authsenabled as $auth) {
6876 $authplugin = get_auth_plugin($auth);
6877 $authplugins[$auth] = $authplugin;
6878 /// Get the auth title (from core or own auth lang files)
6879 $authtitle = $authplugin->get_title();
6880 /// Apply titles
6881 $displayauths[$auth] = $authtitle;
6882 if ($authplugin->can_signup()) {
6883 $registrationauths[$auth] = $authtitle;
6887 foreach ($authsavailable as $auth => $dir) {
6888 if (array_key_exists($auth, $displayauths)) {
6889 continue; //already in the list
6891 $authplugin = get_auth_plugin($auth);
6892 $authplugins[$auth] = $authplugin;
6893 /// Get the auth title (from core or own auth lang files)
6894 $authtitle = $authplugin->get_title();
6895 /// Apply titles
6896 $displayauths[$auth] = $authtitle;
6897 if ($authplugin->can_signup()) {
6898 $registrationauths[$auth] = $authtitle;
6902 $return = $OUTPUT->heading(get_string('actauthhdr', 'auth'), 3, 'main');
6903 $return .= $OUTPUT->box_start('generalbox authsui');
6905 $table = new html_table();
6906 $table->head = array($txt->name, $txt->users, $txt->enable, $txt->updown, $txt->settings, $txt->testsettings, $txt->uninstall);
6907 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
6908 $table->data = array();
6909 $table->attributes['class'] = 'admintable generaltable';
6910 $table->id = 'manageauthtable';
6912 //add always enabled plugins first
6913 $displayname = $displayauths['manual'];
6914 $settings = "<a href=\"settings.php?section=authsettingmanual\">{$txt->settings}</a>";
6915 $usercount = $DB->count_records('user', array('auth'=>'manual', 'deleted'=>0));
6916 $table->data[] = array($displayname, $usercount, '', '', $settings, '', '');
6917 $displayname = $displayauths['nologin'];
6918 $usercount = $DB->count_records('user', array('auth'=>'nologin', 'deleted'=>0));
6919 $table->data[] = array($displayname, $usercount, '', '', '', '', '');
6922 // iterate through auth plugins and add to the display table
6923 $updowncount = 1;
6924 $authcount = count($authsenabled);
6925 $url = "auth.php?sesskey=" . sesskey();
6926 foreach ($displayauths as $auth => $name) {
6927 if ($auth == 'manual' or $auth == 'nologin') {
6928 continue;
6930 $class = '';
6931 // hide/show link
6932 if (in_array($auth, $authsenabled)) {
6933 $hideshow = "<a href=\"$url&amp;action=disable&amp;auth=$auth\">";
6934 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
6935 $enabled = true;
6936 $displayname = $name;
6938 else {
6939 $hideshow = "<a href=\"$url&amp;action=enable&amp;auth=$auth\">";
6940 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
6941 $enabled = false;
6942 $displayname = $name;
6943 $class = 'dimmed_text';
6946 $usercount = $DB->count_records('user', array('auth'=>$auth, 'deleted'=>0));
6948 // up/down link (only if auth is enabled)
6949 $updown = '';
6950 if ($enabled) {
6951 if ($updowncount > 1) {
6952 $updown .= "<a href=\"$url&amp;action=up&amp;auth=$auth\">";
6953 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
6955 else {
6956 $updown .= $OUTPUT->spacer() . '&nbsp;';
6958 if ($updowncount < $authcount) {
6959 $updown .= "<a href=\"$url&amp;action=down&amp;auth=$auth\">";
6960 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
6962 else {
6963 $updown .= $OUTPUT->spacer() . '&nbsp;';
6965 ++ $updowncount;
6968 // settings link
6969 if (file_exists($CFG->dirroot.'/auth/'.$auth.'/settings.php')) {
6970 $settings = "<a href=\"settings.php?section=authsetting$auth\">{$txt->settings}</a>";
6971 } else if (file_exists($CFG->dirroot.'/auth/'.$auth.'/config.html')) {
6972 $settings = "<a href=\"auth_config.php?auth=$auth\">{$txt->settings}</a>";
6973 } else {
6974 $settings = '';
6977 // Uninstall link.
6978 $uninstall = '';
6979 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('auth_'.$auth, 'manage')) {
6980 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
6983 $test = '';
6984 if (!empty($authplugins[$auth]) and method_exists($authplugins[$auth], 'test_settings')) {
6985 $testurl = new moodle_url('/auth/test_settings.php', array('auth'=>$auth, 'sesskey'=>sesskey()));
6986 $test = html_writer::link($testurl, $txt->testsettings);
6989 // Add a row to the table.
6990 $row = new html_table_row(array($displayname, $usercount, $hideshow, $updown, $settings, $test, $uninstall));
6991 if ($class) {
6992 $row->attributes['class'] = $class;
6994 $table->data[] = $row;
6996 $return .= html_writer::table($table);
6997 $return .= get_string('configauthenticationplugins', 'admin').'<br />'.get_string('tablenosave', 'filters');
6998 $return .= $OUTPUT->box_end();
6999 return highlight($query, $return);
7005 * Special class for authentication administration.
7007 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7009 class admin_setting_manageeditors extends admin_setting {
7011 * Calls parent::__construct with specific arguments
7013 public function __construct() {
7014 $this->nosave = true;
7015 parent::__construct('editorsui', get_string('editorsettings', 'editor'), '', '');
7019 * Always returns true, does nothing
7021 * @return true
7023 public function get_setting() {
7024 return true;
7028 * Always returns true, does nothing
7030 * @return true
7032 public function get_defaultsetting() {
7033 return true;
7037 * Always returns '', does not write anything
7039 * @return string Always returns ''
7041 public function write_setting($data) {
7042 // do not write any setting
7043 return '';
7047 * Checks if $query is one of the available editors
7049 * @param string $query The string to search for
7050 * @return bool Returns true if found, false if not
7052 public function is_related($query) {
7053 if (parent::is_related($query)) {
7054 return true;
7057 $editors_available = editors_get_available();
7058 foreach ($editors_available as $editor=>$editorstr) {
7059 if (strpos($editor, $query) !== false) {
7060 return true;
7062 if (strpos(core_text::strtolower($editorstr), $query) !== false) {
7063 return true;
7066 return false;
7070 * Builds the XHTML to display the control
7072 * @param string $data Unused
7073 * @param string $query
7074 * @return string
7076 public function output_html($data, $query='') {
7077 global $CFG, $OUTPUT;
7079 // display strings
7080 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7081 'up', 'down', 'none'));
7082 $struninstall = get_string('uninstallplugin', 'core_admin');
7084 $txt->updown = "$txt->up/$txt->down";
7086 $editors_available = editors_get_available();
7087 $active_editors = explode(',', $CFG->texteditors);
7089 $active_editors = array_reverse($active_editors);
7090 foreach ($active_editors as $key=>$editor) {
7091 if (empty($editors_available[$editor])) {
7092 unset($active_editors[$key]);
7093 } else {
7094 $name = $editors_available[$editor];
7095 unset($editors_available[$editor]);
7096 $editors_available[$editor] = $name;
7099 if (empty($active_editors)) {
7100 //$active_editors = array('textarea');
7102 $editors_available = array_reverse($editors_available, true);
7103 $return = $OUTPUT->heading(get_string('acteditorshhdr', 'editor'), 3, 'main', true);
7104 $return .= $OUTPUT->box_start('generalbox editorsui');
7106 $table = new html_table();
7107 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7108 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7109 $table->id = 'editormanagement';
7110 $table->attributes['class'] = 'admintable generaltable';
7111 $table->data = array();
7113 // iterate through auth plugins and add to the display table
7114 $updowncount = 1;
7115 $editorcount = count($active_editors);
7116 $url = "editors.php?sesskey=" . sesskey();
7117 foreach ($editors_available as $editor => $name) {
7118 // hide/show link
7119 $class = '';
7120 if (in_array($editor, $active_editors)) {
7121 $hideshow = "<a href=\"$url&amp;action=disable&amp;editor=$editor\">";
7122 $hideshow .= $OUTPUT->pix_icon('t/hide', get_string('disable')) . '</a>';
7123 $enabled = true;
7124 $displayname = $name;
7126 else {
7127 $hideshow = "<a href=\"$url&amp;action=enable&amp;editor=$editor\">";
7128 $hideshow .= $OUTPUT->pix_icon('t/show', get_string('enable')) . '</a>';
7129 $enabled = false;
7130 $displayname = $name;
7131 $class = 'dimmed_text';
7134 // up/down link (only if auth is enabled)
7135 $updown = '';
7136 if ($enabled) {
7137 if ($updowncount > 1) {
7138 $updown .= "<a href=\"$url&amp;action=up&amp;editor=$editor\">";
7139 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
7141 else {
7142 $updown .= $OUTPUT->spacer() . '&nbsp;';
7144 if ($updowncount < $editorcount) {
7145 $updown .= "<a href=\"$url&amp;action=down&amp;editor=$editor\">";
7146 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
7148 else {
7149 $updown .= $OUTPUT->spacer() . '&nbsp;';
7151 ++ $updowncount;
7154 // settings link
7155 if (file_exists($CFG->dirroot.'/lib/editor/'.$editor.'/settings.php')) {
7156 $eurl = new moodle_url('/admin/settings.php', array('section'=>'editorsettings'.$editor));
7157 $settings = "<a href='$eurl'>{$txt->settings}</a>";
7158 } else {
7159 $settings = '';
7162 $uninstall = '';
7163 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('editor_'.$editor, 'manage')) {
7164 $uninstall = html_writer::link($uninstallurl, $struninstall);
7167 // Add a row to the table.
7168 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7169 if ($class) {
7170 $row->attributes['class'] = $class;
7172 $table->data[] = $row;
7174 $return .= html_writer::table($table);
7175 $return .= get_string('configeditorplugins', 'editor').'<br />'.get_string('tablenosave', 'admin');
7176 $return .= $OUTPUT->box_end();
7177 return highlight($query, $return);
7182 * Special class for antiviruses administration.
7184 * @copyright 2015 Ruslan Kabalin, Lancaster University.
7185 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7187 class admin_setting_manageantiviruses extends admin_setting {
7189 * Calls parent::__construct with specific arguments
7191 public function __construct() {
7192 $this->nosave = true;
7193 parent::__construct('antivirusesui', get_string('antivirussettings', 'antivirus'), '', '');
7197 * Always returns true, does nothing
7199 * @return true
7201 public function get_setting() {
7202 return true;
7206 * Always returns true, does nothing
7208 * @return true
7210 public function get_defaultsetting() {
7211 return true;
7215 * Always returns '', does not write anything
7217 * @param string $data Unused
7218 * @return string Always returns ''
7220 public function write_setting($data) {
7221 // Do not write any setting.
7222 return '';
7226 * Checks if $query is one of the available editors
7228 * @param string $query The string to search for
7229 * @return bool Returns true if found, false if not
7231 public function is_related($query) {
7232 if (parent::is_related($query)) {
7233 return true;
7236 $antivirusesavailable = \core\antivirus\manager::get_available();
7237 foreach ($antivirusesavailable as $antivirus => $antivirusstr) {
7238 if (strpos($antivirus, $query) !== false) {
7239 return true;
7241 if (strpos(core_text::strtolower($antivirusstr), $query) !== false) {
7242 return true;
7245 return false;
7249 * Builds the XHTML to display the control
7251 * @param string $data Unused
7252 * @param string $query
7253 * @return string
7255 public function output_html($data, $query='') {
7256 global $CFG, $OUTPUT;
7258 // Display strings.
7259 $txt = get_strings(array('administration', 'settings', 'edit', 'name', 'enable', 'disable',
7260 'up', 'down', 'none'));
7261 $struninstall = get_string('uninstallplugin', 'core_admin');
7263 $txt->updown = "$txt->up/$txt->down";
7265 $antivirusesavailable = \core\antivirus\manager::get_available();
7266 $activeantiviruses = explode(',', $CFG->antiviruses);
7268 $activeantiviruses = array_reverse($activeantiviruses);
7269 foreach ($activeantiviruses as $key => $antivirus) {
7270 if (empty($antivirusesavailable[$antivirus])) {
7271 unset($activeantiviruses[$key]);
7272 } else {
7273 $name = $antivirusesavailable[$antivirus];
7274 unset($antivirusesavailable[$antivirus]);
7275 $antivirusesavailable[$antivirus] = $name;
7278 $antivirusesavailable = array_reverse($antivirusesavailable, true);
7279 $return = $OUTPUT->heading(get_string('actantivirushdr', 'antivirus'), 3, 'main', true);
7280 $return .= $OUTPUT->box_start('generalbox antivirusesui');
7282 $table = new html_table();
7283 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->settings, $struninstall);
7284 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
7285 $table->id = 'antivirusmanagement';
7286 $table->attributes['class'] = 'admintable generaltable';
7287 $table->data = array();
7289 // Iterate through auth plugins and add to the display table.
7290 $updowncount = 1;
7291 $antiviruscount = count($activeantiviruses);
7292 $baseurl = new moodle_url('/admin/antiviruses.php', array('sesskey' => sesskey()));
7293 foreach ($antivirusesavailable as $antivirus => $name) {
7294 // Hide/show link.
7295 $class = '';
7296 if (in_array($antivirus, $activeantiviruses)) {
7297 $hideshowurl = $baseurl;
7298 $hideshowurl->params(array('action' => 'disable', 'antivirus' => $antivirus));
7299 $hideshowimg = $OUTPUT->pix_icon('t/hide', get_string('disable'));
7300 $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7301 $enabled = true;
7302 $displayname = $name;
7303 } else {
7304 $hideshowurl = $baseurl;
7305 $hideshowurl->params(array('action' => 'enable', 'antivirus' => $antivirus));
7306 $hideshowimg = $OUTPUT->pix_icon('t/show', get_string('enable'));
7307 $hideshow = html_writer::link($hideshowurl, $hideshowimg);
7308 $enabled = false;
7309 $displayname = $name;
7310 $class = 'dimmed_text';
7313 // Up/down link.
7314 $updown = '';
7315 if ($enabled) {
7316 if ($updowncount > 1) {
7317 $updownurl = $baseurl;
7318 $updownurl->params(array('action' => 'up', 'antivirus' => $antivirus));
7319 $updownimg = $OUTPUT->pix_icon('t/up', get_string('moveup'));
7320 $updown = html_writer::link($updownurl, $updownimg);
7321 } else {
7322 $updownimg = $OUTPUT->spacer();
7324 if ($updowncount < $antiviruscount) {
7325 $updownurl = $baseurl;
7326 $updownurl->params(array('action' => 'down', 'antivirus' => $antivirus));
7327 $updownimg = $OUTPUT->pix_icon('t/down', get_string('movedown'));
7328 $updown = html_writer::link($updownurl, $updownimg);
7329 } else {
7330 $updownimg = $OUTPUT->spacer();
7332 ++ $updowncount;
7335 // Settings link.
7336 if (file_exists($CFG->dirroot.'/lib/antivirus/'.$antivirus.'/settings.php')) {
7337 $eurl = new moodle_url('/admin/settings.php', array('section' => 'antivirussettings'.$antivirus));
7338 $settings = html_writer::link($eurl, $txt->settings);
7339 } else {
7340 $settings = '';
7343 $uninstall = '';
7344 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('antivirus_'.$antivirus, 'manage')) {
7345 $uninstall = html_writer::link($uninstallurl, $struninstall);
7348 // Add a row to the table.
7349 $row = new html_table_row(array($displayname, $hideshow, $updown, $settings, $uninstall));
7350 if ($class) {
7351 $row->attributes['class'] = $class;
7353 $table->data[] = $row;
7355 $return .= html_writer::table($table);
7356 $return .= get_string('configantivirusplugins', 'antivirus') . html_writer::empty_tag('br') . get_string('tablenosave', 'admin');
7357 $return .= $OUTPUT->box_end();
7358 return highlight($query, $return);
7363 * Special class for license administration.
7365 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7366 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7367 * @todo MDL-45184 This class will be deleted in Moodle 4.3.
7369 class admin_setting_managelicenses extends admin_setting {
7371 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7372 * @todo MDL-45184 This class will be deleted in Moodle 4.3
7374 public function __construct() {
7375 global $ADMIN;
7377 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7378 DEBUG_DEVELOPER);
7380 // Replace admin setting load with new external page load for tool_licensemanager, if not loaded already.
7381 if (!is_null($ADMIN->locate('licensemanager'))) {
7382 $temp = new admin_externalpage('licensemanager',
7383 get_string('licensemanager', 'tool_licensemanager'),
7384 \tool_licensemanager\helper::get_licensemanager_url());
7386 $ADMIN->add('license', $temp);
7391 * Always returns true, does nothing
7393 * @deprecated since Moodle 3.9 MDL-45184.
7394 * @todo MDL-45184 This method will be deleted in Moodle 4.3
7396 * @return true
7398 public function get_setting() {
7399 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7400 DEBUG_DEVELOPER);
7402 return true;
7406 * Always returns true, does nothing
7408 * @deprecated since Moodle 3.9 MDL-45184.
7409 * @todo MDL-45184 This method will be deleted in Moodle 4.3
7411 * @return true
7413 public function get_defaultsetting() {
7414 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7415 DEBUG_DEVELOPER);
7417 return true;
7421 * Always returns '', does not write anything
7423 * @deprecated since Moodle 3.9 MDL-45184.
7424 * @todo MDL-45184 This method will be deleted in Moodle 4.3
7426 * @return string Always returns ''
7428 public function write_setting($data) {
7429 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7430 DEBUG_DEVELOPER);
7432 // do not write any setting
7433 return '';
7437 * Builds the XHTML to display the control
7439 * @deprecated since Moodle 3.9 MDL-45184. Please use \tool_licensemanager\manager instead.
7440 * @todo MDL-45184 This method will be deleted in Moodle 4.3
7442 * @param string $data Unused
7443 * @param string $query
7444 * @return string
7446 public function output_html($data, $query='') {
7447 debugging('admin_setting_managelicenses class is deprecated. Please use \tool_licensemanager\manager instead.',
7448 DEBUG_DEVELOPER);
7450 redirect(\tool_licensemanager\helper::get_licensemanager_url());
7455 * Course formats manager. Allows to enable/disable formats and jump to settings
7457 class admin_setting_manageformats extends admin_setting {
7460 * Calls parent::__construct with specific arguments
7462 public function __construct() {
7463 $this->nosave = true;
7464 parent::__construct('formatsui', new lang_string('manageformats', 'core_admin'), '', '');
7468 * Always returns true
7470 * @return true
7472 public function get_setting() {
7473 return true;
7477 * Always returns true
7479 * @return true
7481 public function get_defaultsetting() {
7482 return true;
7486 * Always returns '' and doesn't write anything
7488 * @param mixed $data string or array, must not be NULL
7489 * @return string Always returns ''
7491 public function write_setting($data) {
7492 // do not write any setting
7493 return '';
7497 * Search to find if Query is related to format plugin
7499 * @param string $query The string to search for
7500 * @return bool true for related false for not
7502 public function is_related($query) {
7503 if (parent::is_related($query)) {
7504 return true;
7506 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7507 foreach ($formats as $format) {
7508 if (strpos($format->component, $query) !== false ||
7509 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7510 return true;
7513 return false;
7517 * Return XHTML to display control
7519 * @param mixed $data Unused
7520 * @param string $query
7521 * @return string highlight
7523 public function output_html($data, $query='') {
7524 global $CFG, $OUTPUT;
7525 $return = '';
7526 $return = $OUTPUT->heading(new lang_string('courseformats'), 3, 'main');
7527 $return .= $OUTPUT->box_start('generalbox formatsui');
7529 $formats = core_plugin_manager::instance()->get_plugins_of_type('format');
7531 // display strings
7532 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7533 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7534 $txt->updown = "$txt->up/$txt->down";
7536 $table = new html_table();
7537 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7538 $table->align = array('left', 'center', 'center', 'center', 'center');
7539 $table->attributes['class'] = 'manageformattable generaltable admintable';
7540 $table->data = array();
7542 $cnt = 0;
7543 $defaultformat = get_config('moodlecourse', 'format');
7544 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7545 foreach ($formats as $format) {
7546 $url = new moodle_url('/admin/courseformats.php',
7547 array('sesskey' => sesskey(), 'format' => $format->name));
7548 $isdefault = '';
7549 $class = '';
7550 if ($format->is_enabled()) {
7551 $strformatname = $format->displayname;
7552 if ($defaultformat === $format->name) {
7553 $hideshow = $txt->default;
7554 } else {
7555 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7556 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7558 } else {
7559 $strformatname = $format->displayname;
7560 $class = 'dimmed_text';
7561 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7562 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7564 $updown = '';
7565 if ($cnt) {
7566 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7567 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7568 } else {
7569 $updown .= $spacer;
7571 if ($cnt < count($formats) - 1) {
7572 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7573 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7574 } else {
7575 $updown .= $spacer;
7577 $cnt++;
7578 $settings = '';
7579 if ($format->get_settings_url()) {
7580 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7582 $uninstall = '';
7583 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('format_'.$format->name, 'manage')) {
7584 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7586 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7587 if ($class) {
7588 $row->attributes['class'] = $class;
7590 $table->data[] = $row;
7592 $return .= html_writer::table($table);
7593 $link = html_writer::link(new moodle_url('/admin/settings.php', array('section' => 'coursesettings')), new lang_string('coursesettings'));
7594 $return .= html_writer::tag('p', get_string('manageformatsgotosettings', 'admin', $link));
7595 $return .= $OUTPUT->box_end();
7596 return highlight($query, $return);
7601 * Custom fields manager. Allows to enable/disable custom fields and jump to settings.
7603 * @package core
7604 * @copyright 2018 Toni Barbera
7605 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7607 class admin_setting_managecustomfields extends admin_setting {
7610 * Calls parent::__construct with specific arguments
7612 public function __construct() {
7613 $this->nosave = true;
7614 parent::__construct('customfieldsui', new lang_string('managecustomfields', 'core_admin'), '', '');
7618 * Always returns true
7620 * @return true
7622 public function get_setting() {
7623 return true;
7627 * Always returns true
7629 * @return true
7631 public function get_defaultsetting() {
7632 return true;
7636 * Always returns '' and doesn't write anything
7638 * @param mixed $data string or array, must not be NULL
7639 * @return string Always returns ''
7641 public function write_setting($data) {
7642 // Do not write any setting.
7643 return '';
7647 * Search to find if Query is related to format plugin
7649 * @param string $query The string to search for
7650 * @return bool true for related false for not
7652 public function is_related($query) {
7653 if (parent::is_related($query)) {
7654 return true;
7656 $formats = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7657 foreach ($formats as $format) {
7658 if (strpos($format->component, $query) !== false ||
7659 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7660 return true;
7663 return false;
7667 * Return XHTML to display control
7669 * @param mixed $data Unused
7670 * @param string $query
7671 * @return string highlight
7673 public function output_html($data, $query='') {
7674 global $CFG, $OUTPUT;
7675 $return = '';
7676 $return = $OUTPUT->heading(new lang_string('customfields', 'core_customfield'), 3, 'main');
7677 $return .= $OUTPUT->box_start('generalbox customfieldsui');
7679 $fields = core_plugin_manager::instance()->get_plugins_of_type('customfield');
7681 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down'));
7682 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7683 $txt->updown = "$txt->up/$txt->down";
7685 $table = new html_table();
7686 $table->head = array($txt->name, $txt->enable, $txt->uninstall, $txt->settings);
7687 $table->align = array('left', 'center', 'center', 'center');
7688 $table->attributes['class'] = 'managecustomfieldtable generaltable admintable';
7689 $table->data = array();
7691 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7692 foreach ($fields as $field) {
7693 $url = new moodle_url('/admin/customfields.php',
7694 array('sesskey' => sesskey(), 'field' => $field->name));
7696 if ($field->is_enabled()) {
7697 $strfieldname = $field->displayname;
7698 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7699 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7700 } else {
7701 $strfieldname = $field->displayname;
7702 $class = 'dimmed_text';
7703 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7704 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7706 $settings = '';
7707 if ($field->get_settings_url()) {
7708 $settings = html_writer::link($field->get_settings_url(), $txt->settings);
7710 $uninstall = '';
7711 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('customfield_'.$field->name, 'manage')) {
7712 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7714 $row = new html_table_row(array($strfieldname, $hideshow, $uninstall, $settings));
7715 $table->data[] = $row;
7717 $return .= html_writer::table($table);
7718 $return .= $OUTPUT->box_end();
7719 return highlight($query, $return);
7724 * Data formats manager. Allow reorder and to enable/disable data formats and jump to settings
7726 * @copyright 2016 Brendan Heywood (brendan@catalyst-au.net)
7727 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7729 class admin_setting_managedataformats extends admin_setting {
7732 * Calls parent::__construct with specific arguments
7734 public function __construct() {
7735 $this->nosave = true;
7736 parent::__construct('managedataformats', new lang_string('managedataformats'), '', '');
7740 * Always returns true
7742 * @return true
7744 public function get_setting() {
7745 return true;
7749 * Always returns true
7751 * @return true
7753 public function get_defaultsetting() {
7754 return true;
7758 * Always returns '' and doesn't write anything
7760 * @param mixed $data string or array, must not be NULL
7761 * @return string Always returns ''
7763 public function write_setting($data) {
7764 // Do not write any setting.
7765 return '';
7769 * Search to find if Query is related to format plugin
7771 * @param string $query The string to search for
7772 * @return bool true for related false for not
7774 public function is_related($query) {
7775 if (parent::is_related($query)) {
7776 return true;
7778 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7779 foreach ($formats as $format) {
7780 if (strpos($format->component, $query) !== false ||
7781 strpos(core_text::strtolower($format->displayname), $query) !== false) {
7782 return true;
7785 return false;
7789 * Return XHTML to display control
7791 * @param mixed $data Unused
7792 * @param string $query
7793 * @return string highlight
7795 public function output_html($data, $query='') {
7796 global $CFG, $OUTPUT;
7797 $return = '';
7799 $formats = core_plugin_manager::instance()->get_plugins_of_type('dataformat');
7801 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'up', 'down', 'default'));
7802 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
7803 $txt->updown = "$txt->up/$txt->down";
7805 $table = new html_table();
7806 $table->head = array($txt->name, $txt->enable, $txt->updown, $txt->uninstall, $txt->settings);
7807 $table->align = array('left', 'center', 'center', 'center', 'center');
7808 $table->attributes['class'] = 'manageformattable generaltable admintable';
7809 $table->data = array();
7811 $cnt = 0;
7812 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
7813 $totalenabled = 0;
7814 foreach ($formats as $format) {
7815 if ($format->is_enabled() && $format->is_installed_and_upgraded()) {
7816 $totalenabled++;
7819 foreach ($formats as $format) {
7820 $status = $format->get_status();
7821 $url = new moodle_url('/admin/dataformats.php',
7822 array('sesskey' => sesskey(), 'name' => $format->name));
7824 $class = '';
7825 if ($format->is_enabled()) {
7826 $strformatname = $format->displayname;
7827 if ($totalenabled == 1&& $format->is_enabled()) {
7828 $hideshow = '';
7829 } else {
7830 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
7831 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
7833 } else {
7834 $class = 'dimmed_text';
7835 $strformatname = $format->displayname;
7836 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
7837 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
7840 $updown = '';
7841 if ($cnt) {
7842 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
7843 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
7844 } else {
7845 $updown .= $spacer;
7847 if ($cnt < count($formats) - 1) {
7848 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
7849 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
7850 } else {
7851 $updown .= $spacer;
7854 $uninstall = '';
7855 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
7856 $uninstall = get_string('status_missing', 'core_plugin');
7857 } else if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
7858 $uninstall = get_string('status_new', 'core_plugin');
7859 } else if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('dataformat_'.$format->name, 'manage')) {
7860 if ($totalenabled != 1 || !$format->is_enabled()) {
7861 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
7865 $settings = '';
7866 if ($format->get_settings_url()) {
7867 $settings = html_writer::link($format->get_settings_url(), $txt->settings);
7870 $row = new html_table_row(array($strformatname, $hideshow, $updown, $uninstall, $settings));
7871 if ($class) {
7872 $row->attributes['class'] = $class;
7874 $table->data[] = $row;
7875 $cnt++;
7877 $return .= html_writer::table($table);
7878 return highlight($query, $return);
7883 * Special class for filter administration.
7885 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7887 class admin_page_managefilters extends admin_externalpage {
7889 * Calls parent::__construct with specific arguments
7891 public function __construct() {
7892 global $CFG;
7893 parent::__construct('managefilters', get_string('filtersettings', 'admin'), "$CFG->wwwroot/$CFG->admin/filters.php");
7897 * Searches all installed filters for specified filter
7899 * @param string $query The filter(string) to search for
7900 * @param string $query
7902 public function search($query) {
7903 global $CFG;
7904 if ($result = parent::search($query)) {
7905 return $result;
7908 $found = false;
7909 $filternames = filter_get_all_installed();
7910 foreach ($filternames as $path => $strfiltername) {
7911 if (strpos(core_text::strtolower($strfiltername), $query) !== false) {
7912 $found = true;
7913 break;
7915 if (strpos($path, $query) !== false) {
7916 $found = true;
7917 break;
7921 if ($found) {
7922 $result = new stdClass;
7923 $result->page = $this;
7924 $result->settings = array();
7925 return array($this->name => $result);
7926 } else {
7927 return array();
7933 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
7934 * Requires a get_rank method on the plugininfo class for sorting.
7936 * @copyright 2017 Damyon Wiese
7937 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
7939 abstract class admin_setting_manage_plugins extends admin_setting {
7942 * Get the admin settings section name (just a unique string)
7944 * @return string
7946 public function get_section_name() {
7947 return 'manage' . $this->get_plugin_type() . 'plugins';
7951 * Get the admin settings section title (use get_string).
7953 * @return string
7955 abstract public function get_section_title();
7958 * Get the type of plugin to manage.
7960 * @return string
7962 abstract public function get_plugin_type();
7965 * Get the name of the second column.
7967 * @return string
7969 public function get_info_column_name() {
7970 return '';
7974 * Get the type of plugin to manage.
7976 * @param plugininfo The plugin info class.
7977 * @return string
7979 abstract public function get_info_column($plugininfo);
7982 * Calls parent::__construct with specific arguments
7984 public function __construct() {
7985 $this->nosave = true;
7986 parent::__construct($this->get_section_name(), $this->get_section_title(), '', '');
7990 * Always returns true, does nothing
7992 * @return true
7994 public function get_setting() {
7995 return true;
7999 * Always returns true, does nothing
8001 * @return true
8003 public function get_defaultsetting() {
8004 return true;
8008 * Always returns '', does not write anything
8010 * @param mixed $data
8011 * @return string Always returns ''
8013 public function write_setting($data) {
8014 // Do not write any setting.
8015 return '';
8019 * Checks if $query is one of the available plugins of this type
8021 * @param string $query The string to search for
8022 * @return bool Returns true if found, false if not
8024 public function is_related($query) {
8025 if (parent::is_related($query)) {
8026 return true;
8029 $query = core_text::strtolower($query);
8030 $plugins = core_plugin_manager::instance()->get_plugins_of_type($this->get_plugin_type());
8031 foreach ($plugins as $name => $plugin) {
8032 $localised = $plugin->displayname;
8033 if (strpos(core_text::strtolower($name), $query) !== false) {
8034 return true;
8036 if (strpos(core_text::strtolower($localised), $query) !== false) {
8037 return true;
8040 return false;
8044 * The URL for the management page for this plugintype.
8046 * @return moodle_url
8048 protected function get_manage_url() {
8049 return new moodle_url('/admin/updatesetting.php');
8053 * Builds the HTML to display the control.
8055 * @param string $data Unused
8056 * @param string $query
8057 * @return string
8059 public function output_html($data, $query = '') {
8060 global $CFG, $OUTPUT, $DB, $PAGE;
8062 $context = (object) [
8063 'manageurl' => new moodle_url($this->get_manage_url(), [
8064 'type' => $this->get_plugin_type(),
8065 'sesskey' => sesskey(),
8067 'infocolumnname' => $this->get_info_column_name(),
8068 'plugins' => [],
8071 $pluginmanager = core_plugin_manager::instance();
8072 $allplugins = $pluginmanager->get_plugins_of_type($this->get_plugin_type());
8073 $enabled = $pluginmanager->get_enabled_plugins($this->get_plugin_type());
8074 $plugins = array_merge($enabled, $allplugins);
8075 foreach ($plugins as $key => $plugin) {
8076 $pluginlink = new moodle_url($context->manageurl, ['plugin' => $key]);
8078 $pluginkey = (object) [
8079 'plugin' => $plugin->displayname,
8080 'enabled' => $plugin->is_enabled(),
8081 'togglelink' => '',
8082 'moveuplink' => '',
8083 'movedownlink' => '',
8084 'settingslink' => $plugin->get_settings_url(),
8085 'uninstalllink' => '',
8086 'info' => '',
8089 // Enable/Disable link.
8090 $togglelink = new moodle_url($pluginlink);
8091 if ($plugin->is_enabled()) {
8092 $toggletarget = false;
8093 $togglelink->param('action', 'disable');
8095 if (count($context->plugins)) {
8096 // This is not the first plugin.
8097 $pluginkey->moveuplink = new moodle_url($pluginlink, ['action' => 'up']);
8100 if (count($enabled) > count($context->plugins) + 1) {
8101 // This is not the last plugin.
8102 $pluginkey->movedownlink = new moodle_url($pluginlink, ['action' => 'down']);
8105 $pluginkey->info = $this->get_info_column($plugin);
8106 } else {
8107 $toggletarget = true;
8108 $togglelink->param('action', 'enable');
8111 $pluginkey->toggletarget = $toggletarget;
8112 $pluginkey->togglelink = $togglelink;
8114 $frankenstyle = $plugin->type . '_' . $plugin->name;
8115 if ($uninstalllink = core_plugin_manager::instance()->get_uninstall_url($frankenstyle, 'manage')) {
8116 // This plugin supports uninstallation.
8117 $pluginkey->uninstalllink = $uninstalllink;
8120 if (!empty($this->get_info_column_name())) {
8121 // This plugintype has an info column.
8122 $pluginkey->info = $this->get_info_column($plugin);
8125 $context->plugins[] = $pluginkey;
8128 $str = $OUTPUT->render_from_template('core_admin/setting_manage_plugins', $context);
8129 return highlight($query, $str);
8134 * Generic class for managing plugins in a table that allows re-ordering and enable/disable of each plugin.
8135 * Requires a get_rank method on the plugininfo class for sorting.
8137 * @copyright 2017 Andrew Nicols <andrew@nicols.co.uk>
8138 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8140 class admin_setting_manage_fileconverter_plugins extends admin_setting_manage_plugins {
8141 public function get_section_title() {
8142 return get_string('type_fileconverter_plural', 'plugin');
8145 public function get_plugin_type() {
8146 return 'fileconverter';
8149 public function get_info_column_name() {
8150 return get_string('supportedconversions', 'plugin');
8153 public function get_info_column($plugininfo) {
8154 return $plugininfo->get_supported_conversions();
8159 * Special class for media player plugins management.
8161 * @copyright 2016 Marina Glancy
8162 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8164 class admin_setting_managemediaplayers extends admin_setting {
8166 * Calls parent::__construct with specific arguments
8168 public function __construct() {
8169 $this->nosave = true;
8170 parent::__construct('managemediaplayers', get_string('managemediaplayers', 'media'), '', '');
8174 * Always returns true, does nothing
8176 * @return true
8178 public function get_setting() {
8179 return true;
8183 * Always returns true, does nothing
8185 * @return true
8187 public function get_defaultsetting() {
8188 return true;
8192 * Always returns '', does not write anything
8194 * @param mixed $data
8195 * @return string Always returns ''
8197 public function write_setting($data) {
8198 // Do not write any setting.
8199 return '';
8203 * Checks if $query is one of the available enrol plugins
8205 * @param string $query The string to search for
8206 * @return bool Returns true if found, false if not
8208 public function is_related($query) {
8209 if (parent::is_related($query)) {
8210 return true;
8213 $query = core_text::strtolower($query);
8214 $plugins = core_plugin_manager::instance()->get_plugins_of_type('media');
8215 foreach ($plugins as $name => $plugin) {
8216 $localised = $plugin->displayname;
8217 if (strpos(core_text::strtolower($name), $query) !== false) {
8218 return true;
8220 if (strpos(core_text::strtolower($localised), $query) !== false) {
8221 return true;
8224 return false;
8228 * Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8229 * @return \core\plugininfo\media[]
8231 protected function get_sorted_plugins() {
8232 $pluginmanager = core_plugin_manager::instance();
8234 $plugins = $pluginmanager->get_plugins_of_type('media');
8235 $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8237 // Sort plugins so enabled plugins are displayed first and all others are displayed in the end sorted by rank.
8238 \core_collator::asort_objects_by_method($plugins, 'get_rank', \core_collator::SORT_NUMERIC);
8240 $order = array_values($enabledplugins);
8241 $order = array_merge($order, array_diff(array_reverse(array_keys($plugins)), $order));
8243 $sortedplugins = array();
8244 foreach ($order as $name) {
8245 $sortedplugins[$name] = $plugins[$name];
8248 return $sortedplugins;
8252 * Builds the XHTML to display the control
8254 * @param string $data Unused
8255 * @param string $query
8256 * @return string
8258 public function output_html($data, $query='') {
8259 global $CFG, $OUTPUT, $DB, $PAGE;
8261 // Display strings.
8262 $strup = get_string('up');
8263 $strdown = get_string('down');
8264 $strsettings = get_string('settings');
8265 $strenable = get_string('enable');
8266 $strdisable = get_string('disable');
8267 $struninstall = get_string('uninstallplugin', 'core_admin');
8268 $strversion = get_string('version');
8269 $strname = get_string('name');
8270 $strsupports = get_string('supports', 'core_media');
8272 $pluginmanager = core_plugin_manager::instance();
8274 $plugins = $this->get_sorted_plugins();
8275 $enabledplugins = $pluginmanager->get_enabled_plugins('media');
8277 $return = $OUTPUT->box_start('generalbox mediaplayersui');
8279 $table = new html_table();
8280 $table->head = array($strname, $strsupports, $strversion,
8281 $strenable, $strup.'/'.$strdown, $strsettings, $struninstall);
8282 $table->colclasses = array('leftalign', 'leftalign', 'centeralign',
8283 'centeralign', 'centeralign', 'centeralign', 'centeralign');
8284 $table->id = 'mediaplayerplugins';
8285 $table->attributes['class'] = 'admintable generaltable';
8286 $table->data = array();
8288 // Iterate through media plugins and add to the display table.
8289 $updowncount = 1;
8290 $url = new moodle_url('/admin/media.php', array('sesskey' => sesskey()));
8291 $printed = array();
8292 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8294 $usedextensions = [];
8295 foreach ($plugins as $name => $plugin) {
8296 $url->param('media', $name);
8297 $plugininfo = $pluginmanager->get_plugin_info('media_'.$name);
8298 $version = $plugininfo->versiondb;
8299 $supports = $plugininfo->supports($usedextensions);
8301 // Hide/show links.
8302 $class = '';
8303 if (!$plugininfo->is_installed_and_upgraded()) {
8304 $hideshow = '';
8305 $enabled = false;
8306 $displayname = '<span class="notifyproblem">'.$name.'</span>';
8307 } else {
8308 $enabled = $plugininfo->is_enabled();
8309 if ($enabled) {
8310 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'disable')),
8311 $OUTPUT->pix_icon('t/hide', $strdisable, 'moodle', array('class' => 'iconsmall')));
8312 } else {
8313 $hideshow = html_writer::link(new moodle_url($url, array('action' => 'enable')),
8314 $OUTPUT->pix_icon('t/show', $strenable, 'moodle', array('class' => 'iconsmall')));
8315 $class = 'dimmed_text';
8317 $displayname = $plugin->displayname;
8318 if (get_string_manager()->string_exists('pluginname_help', 'media_' . $name)) {
8319 $displayname .= '&nbsp;' . $OUTPUT->help_icon('pluginname', 'media_' . $name);
8322 if ($PAGE->theme->resolve_image_location('icon', 'media_' . $name, false)) {
8323 $icon = $OUTPUT->pix_icon('icon', '', 'media_' . $name, array('class' => 'icon pluginicon'));
8324 } else {
8325 $icon = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'icon pluginicon noicon'));
8328 // Up/down link (only if enrol is enabled).
8329 $updown = '';
8330 if ($enabled) {
8331 if ($updowncount > 1) {
8332 $updown = html_writer::link(new moodle_url($url, array('action' => 'up')),
8333 $OUTPUT->pix_icon('t/up', $strup, 'moodle', array('class' => 'iconsmall')));
8334 } else {
8335 $updown = $spacer;
8337 if ($updowncount < count($enabledplugins)) {
8338 $updown .= html_writer::link(new moodle_url($url, array('action' => 'down')),
8339 $OUTPUT->pix_icon('t/down', $strdown, 'moodle', array('class' => 'iconsmall')));
8340 } else {
8341 $updown .= $spacer;
8343 ++$updowncount;
8346 $uninstall = '';
8347 $status = $plugininfo->get_status();
8348 if ($status === core_plugin_manager::PLUGIN_STATUS_MISSING) {
8349 $uninstall = get_string('status_missing', 'core_plugin') . '<br/>';
8351 if ($status === core_plugin_manager::PLUGIN_STATUS_NEW) {
8352 $uninstall = get_string('status_new', 'core_plugin');
8353 } else if ($uninstallurl = $pluginmanager->get_uninstall_url('media_'.$name, 'manage')) {
8354 $uninstall .= html_writer::link($uninstallurl, $struninstall);
8357 $settings = '';
8358 if ($plugininfo->get_settings_url()) {
8359 $settings = html_writer::link($plugininfo->get_settings_url(), $strsettings);
8362 // Add a row to the table.
8363 $row = new html_table_row(array($icon.$displayname, $supports, $version, $hideshow, $updown, $settings, $uninstall));
8364 if ($class) {
8365 $row->attributes['class'] = $class;
8367 $table->data[] = $row;
8369 $printed[$name] = true;
8372 $return .= html_writer::table($table);
8373 $return .= $OUTPUT->box_end();
8374 return highlight($query, $return);
8380 * Content bank content types manager. Allow reorder and to enable/disable content bank content types and jump to settings
8382 * @copyright 2020 Amaia Anabitarte <amaia@moodle.com>
8383 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
8385 class admin_setting_managecontentbankcontenttypes extends admin_setting {
8388 * Calls parent::__construct with specific arguments
8390 public function __construct() {
8391 $this->nosave = true;
8392 parent::__construct('contentbank', new lang_string('managecontentbanktypes'), '', '');
8396 * Always returns true
8398 * @return true
8400 public function get_setting() {
8401 return true;
8405 * Always returns true
8407 * @return true
8409 public function get_defaultsetting() {
8410 return true;
8414 * Always returns '' and doesn't write anything
8416 * @param mixed $data string or array, must not be NULL
8417 * @return string Always returns ''
8419 public function write_setting($data) {
8420 // Do not write any setting.
8421 return '';
8425 * Search to find if Query is related to content bank plugin
8427 * @param string $query The string to search for
8428 * @return bool true for related false for not
8430 public function is_related($query) {
8431 if (parent::is_related($query)) {
8432 return true;
8434 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8435 foreach ($types as $type) {
8436 if (strpos($type->component, $query) !== false ||
8437 strpos(core_text::strtolower($type->displayname), $query) !== false) {
8438 return true;
8441 return false;
8445 * Return XHTML to display control
8447 * @param mixed $data Unused
8448 * @param string $query
8449 * @return string highlight
8451 public function output_html($data, $query='') {
8452 global $CFG, $OUTPUT;
8453 $return = '';
8455 $types = core_plugin_manager::instance()->get_plugins_of_type('contenttype');
8456 $txt = get_strings(array('settings', 'name', 'enable', 'disable', 'order', 'up', 'down', 'default'));
8457 $txt->uninstall = get_string('uninstallplugin', 'core_admin');
8459 $table = new html_table();
8460 $table->head = array($txt->name, $txt->enable, $txt->order, $txt->settings, $txt->uninstall);
8461 $table->align = array('left', 'center', 'center', 'center', 'center');
8462 $table->attributes['class'] = 'managecontentbanktable generaltable admintable';
8463 $table->data = array();
8464 $spacer = $OUTPUT->pix_icon('spacer', '', 'moodle', array('class' => 'iconsmall'));
8466 $totalenabled = 0;
8467 $count = 0;
8468 foreach ($types as $type) {
8469 if ($type->is_enabled() && $type->is_installed_and_upgraded()) {
8470 $totalenabled++;
8474 foreach ($types as $type) {
8475 $url = new moodle_url('/admin/contentbank.php',
8476 array('sesskey' => sesskey(), 'name' => $type->name));
8478 $class = '';
8479 $strtypename = $type->displayname;
8480 if ($type->is_enabled()) {
8481 $hideshow = html_writer::link($url->out(false, array('action' => 'disable')),
8482 $OUTPUT->pix_icon('t/hide', $txt->disable, 'moodle', array('class' => 'iconsmall')));
8483 } else {
8484 $class = 'dimmed_text';
8485 $hideshow = html_writer::link($url->out(false, array('action' => 'enable')),
8486 $OUTPUT->pix_icon('t/show', $txt->enable, 'moodle', array('class' => 'iconsmall')));
8489 $updown = '';
8490 if ($count) {
8491 $updown .= html_writer::link($url->out(false, array('action' => 'up')),
8492 $OUTPUT->pix_icon('t/up', $txt->up, 'moodle', array('class' => 'iconsmall'))). '';
8493 } else {
8494 $updown .= $spacer;
8496 if ($count < count($types) - 1) {
8497 $updown .= '&nbsp;'.html_writer::link($url->out(false, array('action' => 'down')),
8498 $OUTPUT->pix_icon('t/down', $txt->down, 'moodle', array('class' => 'iconsmall')));
8499 } else {
8500 $updown .= $spacer;
8503 $settings = '';
8504 if ($type->get_settings_url()) {
8505 $settings = html_writer::link($type->get_settings_url(), $txt->settings);
8508 $uninstall = '';
8509 if ($uninstallurl = core_plugin_manager::instance()->get_uninstall_url('contenttype_'.$type->name, 'manage')) {
8510 $uninstall = html_writer::link($uninstallurl, $txt->uninstall);
8513 $row = new html_table_row(array($strtypename, $hideshow, $updown, $settings, $uninstall));
8514 if ($class) {
8515 $row->attributes['class'] = $class;
8517 $table->data[] = $row;
8518 $count++;
8520 $return .= html_writer::table($table);
8521 return highlight($query, $return);
8526 * Initialise admin page - this function does require login and permission
8527 * checks specified in page definition.
8529 * This function must be called on each admin page before other code.
8531 * @global moodle_page $PAGE
8533 * @param string $section name of page
8534 * @param string $extrabutton extra HTML that is added after the blocks editing on/off button.
8535 * @param array $extraurlparams an array paramname => paramvalue, or parameters that need to be
8536 * added to the turn blocks editing on/off form, so this page reloads correctly.
8537 * @param string $actualurl if the actual page being viewed is not the normal one for this
8538 * page (e.g. admin/roles/allow.php, instead of admin/roles/manage.php, you can pass the alternate URL here.
8539 * @param array $options Additional options that can be specified for page setup.
8540 * pagelayout - This option can be used to set a specific pagelyaout, admin is default.
8542 function admin_externalpage_setup($section, $extrabutton = '', array $extraurlparams = null, $actualurl = '', array $options = array()) {
8543 global $CFG, $PAGE, $USER, $SITE, $OUTPUT;
8545 $PAGE->set_context(null); // hack - set context to something, by default to system context
8547 $site = get_site();
8548 require_login(null, false);
8550 if (!empty($options['pagelayout'])) {
8551 // A specific page layout has been requested.
8552 $PAGE->set_pagelayout($options['pagelayout']);
8553 } else if ($section === 'upgradesettings') {
8554 $PAGE->set_pagelayout('maintenance');
8555 } else {
8556 $PAGE->set_pagelayout('admin');
8559 $adminroot = admin_get_root(false, false); // settings not required for external pages
8560 $extpage = $adminroot->locate($section, true);
8562 if (empty($extpage) or !($extpage instanceof admin_externalpage)) {
8563 // The requested section isn't in the admin tree
8564 // It could be because the user has inadequate capapbilities or because the section doesn't exist
8565 if (!has_capability('moodle/site:config', context_system::instance())) {
8566 // The requested section could depend on a different capability
8567 // but most likely the user has inadequate capabilities
8568 print_error('accessdenied', 'admin');
8569 } else {
8570 print_error('sectionerror', 'admin', "$CFG->wwwroot/$CFG->admin/");
8574 // this eliminates our need to authenticate on the actual pages
8575 if (!$extpage->check_access()) {
8576 print_error('accessdenied', 'admin');
8577 die;
8580 navigation_node::require_admin_tree();
8582 // $PAGE->set_extra_button($extrabutton); TODO
8584 if (!$actualurl) {
8585 $actualurl = $extpage->url;
8588 $PAGE->set_url($actualurl, $extraurlparams);
8589 if (strpos($PAGE->pagetype, 'admin-') !== 0) {
8590 $PAGE->set_pagetype('admin-' . $PAGE->pagetype);
8593 if (empty($SITE->fullname) || empty($SITE->shortname)) {
8594 // During initial install.
8595 $strinstallation = get_string('installation', 'install');
8596 $strsettings = get_string('settings');
8597 $PAGE->navbar->add($strsettings);
8598 $PAGE->set_title($strinstallation);
8599 $PAGE->set_heading($strinstallation);
8600 $PAGE->set_cacheable(false);
8601 return;
8604 // Locate the current item on the navigation and make it active when found.
8605 $path = $extpage->path;
8606 $node = $PAGE->settingsnav;
8607 while ($node && count($path) > 0) {
8608 $node = $node->get(array_pop($path));
8610 if ($node) {
8611 $node->make_active();
8614 // Normal case.
8615 $adminediting = optional_param('adminedit', -1, PARAM_BOOL);
8616 if ($PAGE->user_allowed_editing() && $adminediting != -1) {
8617 $USER->editing = $adminediting;
8620 $visiblepathtosection = array_reverse($extpage->visiblepath);
8622 if ($PAGE->user_allowed_editing()) {
8623 if ($PAGE->user_is_editing()) {
8624 $caption = get_string('blockseditoff');
8625 $url = new moodle_url($PAGE->url, array('adminedit'=>'0', 'sesskey'=>sesskey()));
8626 } else {
8627 $caption = get_string('blocksediton');
8628 $url = new moodle_url($PAGE->url, array('adminedit'=>'1', 'sesskey'=>sesskey()));
8630 $PAGE->set_button($OUTPUT->single_button($url, $caption, 'get'));
8633 $PAGE->set_title("$SITE->shortname: " . implode(": ", $visiblepathtosection));
8634 $PAGE->set_heading($SITE->fullname);
8636 // prevent caching in nav block
8637 $PAGE->navigation->clear_cache();
8641 * Returns the reference to admin tree root
8643 * @return object admin_root object
8645 function admin_get_root($reload=false, $requirefulltree=true) {
8646 global $CFG, $DB, $OUTPUT, $ADMIN;
8648 if (is_null($ADMIN)) {
8649 // create the admin tree!
8650 $ADMIN = new admin_root($requirefulltree);
8653 if ($reload or ($requirefulltree and !$ADMIN->fulltree)) {
8654 $ADMIN->purge_children($requirefulltree);
8657 if (!$ADMIN->loaded) {
8658 // we process this file first to create categories first and in correct order
8659 require($CFG->dirroot.'/'.$CFG->admin.'/settings/top.php');
8661 // now we process all other files in admin/settings to build the admin tree
8662 foreach (glob($CFG->dirroot.'/'.$CFG->admin.'/settings/*.php') as $file) {
8663 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/top.php') {
8664 continue;
8666 if ($file == $CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php') {
8667 // plugins are loaded last - they may insert pages anywhere
8668 continue;
8670 require($file);
8672 require($CFG->dirroot.'/'.$CFG->admin.'/settings/plugins.php');
8674 $ADMIN->loaded = true;
8677 return $ADMIN;
8680 /// settings utility functions
8683 * This function applies default settings.
8684 * Because setting the defaults of some settings can enable other settings,
8685 * this function is called recursively until no more new settings are found.
8687 * @param object $node, NULL means complete tree, null by default
8688 * @param bool $unconditional if true overrides all values with defaults, true by default
8689 * @param array $admindefaultsettings default admin settings to apply. Used recursively
8690 * @param array $settingsoutput The names and values of the changed settings. Used recursively
8691 * @return array $settingsoutput The names and values of the changed settings
8693 function admin_apply_default_settings($node=null, $unconditional=true, $admindefaultsettings=array(), $settingsoutput=array()) {
8694 $counter = 0;
8696 if (is_null($node)) {
8697 core_plugin_manager::reset_caches();
8698 $node = admin_get_root(true, true);
8699 $counter = count($settingsoutput);
8702 if ($node instanceof admin_category) {
8703 $entries = array_keys($node->children);
8704 foreach ($entries as $entry) {
8705 $settingsoutput = admin_apply_default_settings(
8706 $node->children[$entry], $unconditional, $admindefaultsettings, $settingsoutput
8710 } else if ($node instanceof admin_settingpage) {
8711 foreach ($node->settings as $setting) {
8712 if (!$unconditional && !is_null($setting->get_setting())) {
8713 // Do not override existing defaults.
8714 continue;
8716 $defaultsetting = $setting->get_defaultsetting();
8717 if (is_null($defaultsetting)) {
8718 // No value yet - default maybe applied after admin user creation or in upgradesettings.
8719 continue;
8722 $settingname = $node->name . '_' . $setting->name; // Get a unique name for the setting.
8724 if (!array_key_exists($settingname, $admindefaultsettings)) { // Only update a setting if not already processed.
8725 $admindefaultsettings[$settingname] = $settingname;
8726 $settingsoutput[$settingname] = $defaultsetting;
8728 // Set the default for this setting.
8729 $setting->write_setting($defaultsetting);
8730 $setting->write_setting_flags(null);
8731 } else {
8732 unset($admindefaultsettings[$settingname]); // Remove processed settings.
8737 // Call this function recursively until all settings are processed.
8738 if (($node instanceof admin_root) && ($counter != count($settingsoutput))) {
8739 $settingsoutput = admin_apply_default_settings(null, $unconditional, $admindefaultsettings, $settingsoutput);
8741 // Just in case somebody modifies the list of active plugins directly.
8742 core_plugin_manager::reset_caches();
8744 return $settingsoutput;
8748 * Store changed settings, this function updates the errors variable in $ADMIN
8750 * @param object $formdata from form
8751 * @return int number of changed settings
8753 function admin_write_settings($formdata) {
8754 global $CFG, $SITE, $DB;
8756 $olddbsessions = !empty($CFG->dbsessions);
8757 $formdata = (array)$formdata;
8759 $data = array();
8760 foreach ($formdata as $fullname=>$value) {
8761 if (strpos($fullname, 's_') !== 0) {
8762 continue; // not a config value
8764 $data[$fullname] = $value;
8767 $adminroot = admin_get_root();
8768 $settings = admin_find_write_settings($adminroot, $data);
8770 $count = 0;
8771 foreach ($settings as $fullname=>$setting) {
8772 /** @var $setting admin_setting */
8773 $original = $setting->get_setting();
8774 $error = $setting->write_setting($data[$fullname]);
8775 if ($error !== '') {
8776 $adminroot->errors[$fullname] = new stdClass();
8777 $adminroot->errors[$fullname]->data = $data[$fullname];
8778 $adminroot->errors[$fullname]->id = $setting->get_id();
8779 $adminroot->errors[$fullname]->error = $error;
8780 } else {
8781 $setting->write_setting_flags($data);
8783 if ($setting->post_write_settings($original)) {
8784 $count++;
8788 if ($olddbsessions != !empty($CFG->dbsessions)) {
8789 require_logout();
8792 // Now update $SITE - just update the fields, in case other people have a
8793 // a reference to it (e.g. $PAGE, $COURSE).
8794 $newsite = $DB->get_record('course', array('id'=>$SITE->id));
8795 foreach (get_object_vars($newsite) as $field => $value) {
8796 $SITE->$field = $value;
8799 // now reload all settings - some of them might depend on the changed
8800 admin_get_root(true);
8801 return $count;
8805 * Internal recursive function - finds all settings from submitted form
8807 * @param object $node Instance of admin_category, or admin_settingpage
8808 * @param array $data
8809 * @return array
8811 function admin_find_write_settings($node, $data) {
8812 $return = array();
8814 if (empty($data)) {
8815 return $return;
8818 if ($node instanceof admin_category) {
8819 if ($node->check_access()) {
8820 $entries = array_keys($node->children);
8821 foreach ($entries as $entry) {
8822 $return = array_merge($return, admin_find_write_settings($node->children[$entry], $data));
8826 } else if ($node instanceof admin_settingpage) {
8827 if ($node->check_access()) {
8828 foreach ($node->settings as $setting) {
8829 $fullname = $setting->get_full_name();
8830 if (array_key_exists($fullname, $data)) {
8831 $return[$fullname] = $setting;
8838 return $return;
8842 * Internal function - prints the search results
8844 * @param string $query String to search for
8845 * @return string empty or XHTML
8847 function admin_search_settings_html($query) {
8848 global $CFG, $OUTPUT, $PAGE;
8850 if (core_text::strlen($query) < 2) {
8851 return '';
8853 $query = core_text::strtolower($query);
8855 $adminroot = admin_get_root();
8856 $findings = $adminroot->search($query);
8857 $savebutton = false;
8859 $tpldata = (object) [
8860 'actionurl' => $PAGE->url->out(false),
8861 'results' => [],
8862 'sesskey' => sesskey(),
8865 foreach ($findings as $found) {
8866 $page = $found->page;
8867 $settings = $found->settings;
8868 if ($page->is_hidden()) {
8869 // hidden pages are not displayed in search results
8870 continue;
8873 $heading = highlight($query, $page->visiblename);
8874 $headingurl = null;
8875 if ($page instanceof admin_externalpage) {
8876 $headingurl = new moodle_url($page->url);
8877 } else if ($page instanceof admin_settingpage) {
8878 $headingurl = new moodle_url('/admin/settings.php', ['section' => $page->name]);
8879 } else {
8880 continue;
8883 // Locate the page in the admin root and populate its visiblepath attribute.
8884 $path = array();
8885 $located = $adminroot->locate($page->name, true);
8886 if ($located) {
8887 foreach ($located->visiblepath as $pathitem) {
8888 array_unshift($path, (string) $pathitem);
8892 $sectionsettings = [];
8893 if (!empty($settings)) {
8894 foreach ($settings as $setting) {
8895 if (empty($setting->nosave)) {
8896 $savebutton = true;
8898 $fullname = $setting->get_full_name();
8899 if (array_key_exists($fullname, $adminroot->errors)) {
8900 $data = $adminroot->errors[$fullname]->data;
8901 } else {
8902 $data = $setting->get_setting();
8903 // do not use defaults if settings not available - upgradesettings handles the defaults!
8905 $sectionsettings[] = $setting->output_html($data, $query);
8909 $tpldata->results[] = (object) [
8910 'title' => $heading,
8911 'path' => $path,
8912 'url' => $headingurl->out(false),
8913 'settings' => $sectionsettings
8917 $tpldata->showsave = $savebutton;
8918 $tpldata->hasresults = !empty($tpldata->results);
8920 return $OUTPUT->render_from_template('core_admin/settings_search_results', $tpldata);
8924 * Internal function - returns arrays of html pages with uninitialised settings
8926 * @param object $node Instance of admin_category or admin_settingpage
8927 * @return array
8929 function admin_output_new_settings_by_page($node) {
8930 global $OUTPUT;
8931 $return = array();
8933 if ($node instanceof admin_category) {
8934 $entries = array_keys($node->children);
8935 foreach ($entries as $entry) {
8936 $return += admin_output_new_settings_by_page($node->children[$entry]);
8939 } else if ($node instanceof admin_settingpage) {
8940 $newsettings = array();
8941 foreach ($node->settings as $setting) {
8942 if (is_null($setting->get_setting())) {
8943 $newsettings[] = $setting;
8946 if (count($newsettings) > 0) {
8947 $adminroot = admin_get_root();
8948 $page = $OUTPUT->heading(get_string('upgradesettings','admin').' - '.$node->visiblename, 2, 'main');
8949 $page .= '<fieldset class="adminsettings">'."\n";
8950 foreach ($newsettings as $setting) {
8951 $fullname = $setting->get_full_name();
8952 if (array_key_exists($fullname, $adminroot->errors)) {
8953 $data = $adminroot->errors[$fullname]->data;
8954 } else {
8955 $data = $setting->get_setting();
8956 if (is_null($data)) {
8957 $data = $setting->get_defaultsetting();
8960 $page .= '<div class="clearer"><!-- --></div>'."\n";
8961 $page .= $setting->output_html($data);
8963 $page .= '</fieldset>';
8964 $return[$node->name] = $page;
8968 return $return;
8972 * Format admin settings
8974 * @param object $setting
8975 * @param string $title label element
8976 * @param string $form form fragment, html code - not highlighted automatically
8977 * @param string $description
8978 * @param mixed $label link label to id, true by default or string being the label to connect it to
8979 * @param string $warning warning text
8980 * @param sting $defaultinfo defaults info, null means nothing, '' is converted to "Empty" string, defaults to null
8981 * @param string $query search query to be highlighted
8982 * @return string XHTML
8984 function format_admin_setting($setting, $title='', $form='', $description='', $label=true, $warning='', $defaultinfo=NULL, $query='') {
8985 global $CFG, $OUTPUT;
8987 $context = (object) [
8988 'name' => empty($setting->plugin) ? $setting->name : "$setting->plugin | $setting->name",
8989 'fullname' => $setting->get_full_name(),
8992 // Sometimes the id is not id_s_name, but id_s_name_m or something, and this does not validate.
8993 if ($label === true) {
8994 $context->labelfor = $setting->get_id();
8995 } else if ($label === false) {
8996 $context->labelfor = '';
8997 } else {
8998 $context->labelfor = $label;
9001 $form .= $setting->output_setting_flags();
9003 $context->warning = $warning;
9004 $context->override = '';
9005 if (empty($setting->plugin)) {
9006 if (array_key_exists($setting->name, $CFG->config_php_settings)) {
9007 $context->override = get_string('configoverride', 'admin');
9009 } else {
9010 if (array_key_exists($setting->plugin, $CFG->forced_plugin_settings) and array_key_exists($setting->name, $CFG->forced_plugin_settings[$setting->plugin])) {
9011 $context->override = get_string('configoverride', 'admin');
9015 $defaults = array();
9016 if (!is_null($defaultinfo)) {
9017 if ($defaultinfo === '') {
9018 $defaultinfo = get_string('emptysettingvalue', 'admin');
9020 $defaults[] = $defaultinfo;
9023 $context->default = null;
9024 $setting->get_setting_flag_defaults($defaults);
9025 if (!empty($defaults)) {
9026 $defaultinfo = implode(', ', $defaults);
9027 $defaultinfo = highlight($query, nl2br(s($defaultinfo)));
9028 $context->default = get_string('defaultsettinginfo', 'admin', $defaultinfo);
9032 $context->error = '';
9033 $adminroot = admin_get_root();
9034 if (array_key_exists($context->fullname, $adminroot->errors)) {
9035 $context->error = $adminroot->errors[$context->fullname]->error;
9038 if ($dependenton = $setting->get_dependent_on()) {
9039 $context->dependenton = get_string('settingdependenton', 'admin', implode(', ', $dependenton));
9042 $context->id = 'admin-' . $setting->name;
9043 $context->title = highlightfast($query, $title);
9044 $context->name = highlightfast($query, $context->name);
9045 $context->description = highlight($query, markdown_to_html($description));
9046 $context->element = $form;
9047 $context->forceltr = $setting->get_force_ltr();
9048 $context->customcontrol = $setting->has_custom_form_control();
9050 return $OUTPUT->render_from_template('core_admin/setting', $context);
9054 * Based on find_new_settings{@link ()} in upgradesettings.php
9055 * Looks to find any admin settings that have not been initialized. Returns 1 if it finds any.
9057 * @param object $node Instance of admin_category, or admin_settingpage
9058 * @return boolean true if any settings haven't been initialised, false if they all have
9060 function any_new_admin_settings($node) {
9062 if ($node instanceof admin_category) {
9063 $entries = array_keys($node->children);
9064 foreach ($entries as $entry) {
9065 if (any_new_admin_settings($node->children[$entry])) {
9066 return true;
9070 } else if ($node instanceof admin_settingpage) {
9071 foreach ($node->settings as $setting) {
9072 if ($setting->get_setting() === NULL) {
9073 return true;
9078 return false;
9082 * Given a table and optionally a column name should replaces be done?
9084 * @param string $table name
9085 * @param string $column name
9086 * @return bool success or fail
9088 function db_should_replace($table, $column = ''): bool {
9090 // TODO: this is horrible hack, we should do whitelisting and each plugin should be responsible for proper replacing...
9091 $skiptables = ['config', 'config_plugins', 'filter_config', 'sessions',
9092 'events_queue', 'repository_instance_config', 'block_instances', 'files'];
9094 // Don't process these.
9095 if (in_array($table, $skiptables)) {
9096 return false;
9099 // To be safe never replace inside a table that looks related to logging.
9100 if (preg_match('/(^|_)logs?($|_)/', $table)) {
9101 return false;
9104 // Do column based exclusions.
9105 if (!empty($column)) {
9106 // Don't touch anything that looks like a hash.
9107 if (preg_match('/hash$/', $column)) {
9108 return false;
9112 return true;
9116 * Moved from admin/replace.php so that we can use this in cron
9118 * @param string $search string to look for
9119 * @param string $replace string to replace
9120 * @return bool success or fail
9122 function db_replace($search, $replace) {
9123 global $DB, $CFG, $OUTPUT;
9125 // Turn off time limits, sometimes upgrades can be slow.
9126 core_php_time_limit::raise();
9128 if (!$tables = $DB->get_tables() ) { // No tables yet at all.
9129 return false;
9131 foreach ($tables as $table) {
9133 if (!db_should_replace($table)) {
9134 continue;
9137 if ($columns = $DB->get_columns($table)) {
9138 $DB->set_debug(true);
9139 foreach ($columns as $column) {
9140 if (!db_should_replace($table, $column->name)) {
9141 continue;
9143 $DB->replace_all_text($table, $column, $search, $replace);
9145 $DB->set_debug(false);
9149 // delete modinfo caches
9150 rebuild_course_cache(0, true);
9152 // TODO: we should ask all plugins to do the search&replace, for now let's do only blocks...
9153 $blocks = core_component::get_plugin_list('block');
9154 foreach ($blocks as $blockname=>$fullblock) {
9155 if ($blockname === 'NEWBLOCK') { // Someone has unzipped the template, ignore it
9156 continue;
9159 if (!is_readable($fullblock.'/lib.php')) {
9160 continue;
9163 $function = 'block_'.$blockname.'_global_db_replace';
9164 include_once($fullblock.'/lib.php');
9165 if (!function_exists($function)) {
9166 continue;
9169 echo $OUTPUT->notification("Replacing in $blockname blocks...", 'notifysuccess');
9170 $function($search, $replace);
9171 echo $OUTPUT->notification("...finished", 'notifysuccess');
9174 // Trigger an event.
9175 $eventargs = [
9176 'context' => context_system::instance(),
9177 'other' => [
9178 'search' => $search,
9179 'replace' => $replace
9182 $event = \core\event\database_text_field_content_replaced::create($eventargs);
9183 $event->trigger();
9185 purge_all_caches();
9187 return true;
9191 * Manage repository settings
9193 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
9195 class admin_setting_managerepository extends admin_setting {
9196 /** @var string */
9197 private $baseurl;
9200 * calls parent::__construct with specific arguments
9202 public function __construct() {
9203 global $CFG;
9204 parent::__construct('managerepository', get_string('manage', 'repository'), '', '');
9205 $this->baseurl = $CFG->wwwroot . '/' . $CFG->admin . '/repository.php?sesskey=' . sesskey();
9209 * Always returns true, does nothing
9211 * @return true
9213 public function get_setting() {
9214 return true;
9218 * Always returns true does nothing
9220 * @return true
9222 public function get_defaultsetting() {
9223 return true;
9227 * Always returns s_managerepository
9229 * @return string Always return 's_managerepository'
9231 public function get_full_name() {
9232 return 's_managerepository';
9236 * Always returns '' doesn't do anything
9238 public function write_setting($data) {
9239 $url = $this->baseurl . '&amp;new=' . $data;
9240 return '';
9241 // TODO
9242 // Should not use redirect and exit here
9243 // Find a better way to do this.
9244 // redirect($url);
9245 // exit;
9249 * Searches repository plugins for one that matches $query
9251 * @param string $query The string to search for
9252 * @return bool true if found, false if not
9254 public function is_related($query) {
9255 if (parent::is_related($query)) {
9256 return true;
9259 $repositories= core_component::get_plugin_list('repository');
9260 foreach ($repositories as $p => $dir) {
9261 if (strpos($p, $query) !== false) {
9262 return true;
9265 foreach (repository::get_types() as $instance) {
9266 $title = $instance->get_typename();
9267 if (strpos(core_text::strtolower($title), $query) !== false) {
9268 return true;
9271 return false;
9275 * Helper function that generates a moodle_url object
9276 * relevant to the repository
9279 function repository_action_url($repository) {
9280 return new moodle_url($this->baseurl, array('sesskey'=>sesskey(), 'repos'=>$repository));
9284 * Builds XHTML to display the control
9286 * @param string $data Unused
9287 * @param string $query
9288 * @return string XHTML
9290 public function output_html($data, $query='') {
9291 global $CFG, $USER, $OUTPUT;
9293 // Get strings that are used
9294 $strshow = get_string('on', 'repository');
9295 $strhide = get_string('off', 'repository');
9296 $strdelete = get_string('disabled', 'repository');
9298 $actionchoicesforexisting = array(
9299 'show' => $strshow,
9300 'hide' => $strhide,
9301 'delete' => $strdelete
9304 $actionchoicesfornew = array(
9305 'newon' => $strshow,
9306 'newoff' => $strhide,
9307 'delete' => $strdelete
9310 $return = '';
9311 $return .= $OUTPUT->box_start('generalbox');
9313 // Set strings that are used multiple times
9314 $settingsstr = get_string('settings');
9315 $disablestr = get_string('disable');
9317 // Table to list plug-ins
9318 $table = new html_table();
9319 $table->head = array(get_string('name'), get_string('isactive', 'repository'), get_string('order'), $settingsstr);
9320 $table->align = array('left', 'center', 'center', 'center', 'center');
9321 $table->data = array();
9323 // Get list of used plug-ins
9324 $repositorytypes = repository::get_types();
9325 if (!empty($repositorytypes)) {
9326 // Array to store plugins being used
9327 $alreadyplugins = array();
9328 $totalrepositorytypes = count($repositorytypes);
9329 $updowncount = 1;
9330 foreach ($repositorytypes as $i) {
9331 $settings = '';
9332 $typename = $i->get_typename();
9333 // Display edit link only if you can config the type or if it has multiple instances (e.g. has instance config)
9334 $typeoptionnames = repository::static_function($typename, 'get_type_option_names');
9335 $instanceoptionnames = repository::static_function($typename, 'get_instance_option_names');
9337 if (!empty($typeoptionnames) || !empty($instanceoptionnames)) {
9338 // Calculate number of instances in order to display them for the Moodle administrator
9339 if (!empty($instanceoptionnames)) {
9340 $params = array();
9341 $params['context'] = array(context_system::instance());
9342 $params['onlyvisible'] = false;
9343 $params['type'] = $typename;
9344 $admininstancenumber = count(repository::static_function($typename, 'get_instances', $params));
9345 // site instances
9346 $admininstancenumbertext = get_string('instancesforsite', 'repository', $admininstancenumber);
9347 $params['context'] = array();
9348 $instances = repository::static_function($typename, 'get_instances', $params);
9349 $courseinstances = array();
9350 $userinstances = array();
9352 foreach ($instances as $instance) {
9353 $repocontext = context::instance_by_id($instance->instance->contextid);
9354 if ($repocontext->contextlevel == CONTEXT_COURSE) {
9355 $courseinstances[] = $instance;
9356 } else if ($repocontext->contextlevel == CONTEXT_USER) {
9357 $userinstances[] = $instance;
9360 // course instances
9361 $instancenumber = count($courseinstances);
9362 $courseinstancenumbertext = get_string('instancesforcourses', 'repository', $instancenumber);
9364 // user private instances
9365 $instancenumber = count($userinstances);
9366 $userinstancenumbertext = get_string('instancesforusers', 'repository', $instancenumber);
9367 } else {
9368 $admininstancenumbertext = "";
9369 $courseinstancenumbertext = "";
9370 $userinstancenumbertext = "";
9373 $settings .= '<a href="' . $this->baseurl . '&amp;action=edit&amp;repos=' . $typename . '">' . $settingsstr .'</a>';
9375 $settings .= $OUTPUT->container_start('mdl-left');
9376 $settings .= '<br/>';
9377 $settings .= $admininstancenumbertext;
9378 $settings .= '<br/>';
9379 $settings .= $courseinstancenumbertext;
9380 $settings .= '<br/>';
9381 $settings .= $userinstancenumbertext;
9382 $settings .= $OUTPUT->container_end();
9384 // Get the current visibility
9385 if ($i->get_visible()) {
9386 $currentaction = 'show';
9387 } else {
9388 $currentaction = 'hide';
9391 $select = new single_select($this->repository_action_url($typename, 'repos'), 'action', $actionchoicesforexisting, $currentaction, null, 'applyto' . basename($typename));
9393 // Display up/down link
9394 $updown = '';
9395 // Should be done with CSS instead.
9396 $spacer = $OUTPUT->spacer(array('height' => 15, 'width' => 15, 'class' => 'smallicon'));
9398 if ($updowncount > 1) {
9399 $updown .= "<a href=\"$this->baseurl&amp;action=moveup&amp;repos=".$typename."\">";
9400 $updown .= $OUTPUT->pix_icon('t/up', get_string('moveup')) . '</a>&nbsp;';
9402 else {
9403 $updown .= $spacer;
9405 if ($updowncount < $totalrepositorytypes) {
9406 $updown .= "<a href=\"$this->baseurl&amp;action=movedown&amp;repos=".$typename."\">";
9407 $updown .= $OUTPUT->pix_icon('t/down', get_string('movedown')) . '</a>&nbsp;';
9409 else {
9410 $updown .= $spacer;
9413 $updowncount++;
9415 $table->data[] = array($i->get_readablename(), $OUTPUT->render($select), $updown, $settings);
9417 if (!in_array($typename, $alreadyplugins)) {
9418 $alreadyplugins[] = $typename;
9423 // Get all the plugins that exist on disk
9424 $plugins = core_component::get_plugin_list('repository');
9425 if (!empty($plugins)) {
9426 foreach ($plugins as $plugin => $dir) {
9427 // Check that it has not already been listed
9428 if (!in_array($plugin, $alreadyplugins)) {
9429 $select = new single_select($this->repository_action_url($plugin, 'repos'), 'action', $actionchoicesfornew, 'delete', null, 'applyto' . basename($plugin));
9430 $table->data[] = array(get_string('pluginname', 'repository_'.$plugin), $OUTPUT->render($select), '', '');
9435 $return .= html_writer::table($table);
9436 $return .= $OUTPUT->box_end();
9437 return highlight($query, $return);
9442 * Special checkbox for enable mobile web service
9443 * If enable then we store the service id of the mobile service into config table
9444 * If disable then we unstore the service id from the config table
9446 class admin_setting_enablemobileservice extends admin_setting_configcheckbox {
9448 /** @var boolean True means that the capability 'webservice/rest:use' is set for authenticated user role */
9449 private $restuse;
9452 * Return true if Authenticated user role has the capability 'webservice/rest:use', otherwise false.
9454 * @return boolean
9456 private function is_protocol_cap_allowed() {
9457 global $DB, $CFG;
9459 // If the $this->restuse variable is not set, it needs to be set.
9460 if (empty($this->restuse) and $this->restuse!==false) {
9461 $params = array();
9462 $params['permission'] = CAP_ALLOW;
9463 $params['roleid'] = $CFG->defaultuserroleid;
9464 $params['capability'] = 'webservice/rest:use';
9465 $this->restuse = $DB->record_exists('role_capabilities', $params);
9468 return $this->restuse;
9472 * Set the 'webservice/rest:use' to the Authenticated user role (allow or not)
9473 * @param type $status true to allow, false to not set
9475 private function set_protocol_cap($status) {
9476 global $CFG;
9477 if ($status and !$this->is_protocol_cap_allowed()) {
9478 //need to allow the cap
9479 $permission = CAP_ALLOW;
9480 $assign = true;
9481 } else if (!$status and $this->is_protocol_cap_allowed()){
9482 //need to disallow the cap
9483 $permission = CAP_INHERIT;
9484 $assign = true;
9486 if (!empty($assign)) {
9487 $systemcontext = context_system::instance();
9488 assign_capability('webservice/rest:use', $permission, $CFG->defaultuserroleid, $systemcontext->id, true);
9493 * Builds XHTML to display the control.
9494 * The main purpose of this overloading is to display a warning when https
9495 * is not supported by the server
9496 * @param string $data Unused
9497 * @param string $query
9498 * @return string XHTML
9500 public function output_html($data, $query='') {
9501 global $OUTPUT;
9502 $html = parent::output_html($data, $query);
9504 if ((string)$data === $this->yes) {
9505 $notifications = tool_mobile\api::get_potential_config_issues(); // Safe to call, plugin available if we reach here.
9506 foreach ($notifications as $notification) {
9507 $message = get_string($notification[0], $notification[1]);
9508 $html .= $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING);
9512 return $html;
9516 * Retrieves the current setting using the objects name
9518 * @return string
9520 public function get_setting() {
9521 global $CFG;
9523 // First check if is not set.
9524 $result = $this->config_read($this->name);
9525 if (is_null($result)) {
9526 return null;
9529 // For install cli script, $CFG->defaultuserroleid is not set so return 0
9530 // Or if web services aren't enabled this can't be,
9531 if (empty($CFG->defaultuserroleid) || empty($CFG->enablewebservices)) {
9532 return 0;
9535 require_once($CFG->dirroot . '/webservice/lib.php');
9536 $webservicemanager = new webservice();
9537 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9538 if ($mobileservice->enabled and $this->is_protocol_cap_allowed()) {
9539 return $result;
9540 } else {
9541 return 0;
9546 * Save the selected setting
9548 * @param string $data The selected site
9549 * @return string empty string or error message
9551 public function write_setting($data) {
9552 global $DB, $CFG;
9554 //for install cli script, $CFG->defaultuserroleid is not set so do nothing
9555 if (empty($CFG->defaultuserroleid)) {
9556 return '';
9559 $servicename = MOODLE_OFFICIAL_MOBILE_SERVICE;
9561 require_once($CFG->dirroot . '/webservice/lib.php');
9562 $webservicemanager = new webservice();
9564 $updateprotocol = false;
9565 if ((string)$data === $this->yes) {
9566 //code run when enable mobile web service
9567 //enable web service systeme if necessary
9568 set_config('enablewebservices', true);
9570 //enable mobile service
9571 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9572 $mobileservice->enabled = 1;
9573 $webservicemanager->update_external_service($mobileservice);
9575 // Enable REST server.
9576 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9578 if (!in_array('rest', $activeprotocols)) {
9579 $activeprotocols[] = 'rest';
9580 $updateprotocol = true;
9583 if ($updateprotocol) {
9584 set_config('webserviceprotocols', implode(',', $activeprotocols));
9587 // Allow rest:use capability for authenticated user.
9588 $this->set_protocol_cap(true);
9590 } else {
9591 //disable web service system if no other services are enabled
9592 $otherenabledservices = $DB->get_records_select('external_services',
9593 'enabled = :enabled AND (shortname != :shortname OR shortname IS NULL)', array('enabled' => 1,
9594 'shortname' => MOODLE_OFFICIAL_MOBILE_SERVICE));
9595 if (empty($otherenabledservices)) {
9596 set_config('enablewebservices', false);
9598 // Also disable REST server.
9599 $activeprotocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
9601 $protocolkey = array_search('rest', $activeprotocols);
9602 if ($protocolkey !== false) {
9603 unset($activeprotocols[$protocolkey]);
9604 $updateprotocol = true;
9607 if ($updateprotocol) {
9608 set_config('webserviceprotocols', implode(',', $activeprotocols));
9611 // Disallow rest:use capability for authenticated user.
9612 $this->set_protocol_cap(false);
9615 //disable the mobile service
9616 $mobileservice = $webservicemanager->get_external_service_by_shortname(MOODLE_OFFICIAL_MOBILE_SERVICE);
9617 $mobileservice->enabled = 0;
9618 $webservicemanager->update_external_service($mobileservice);
9621 return (parent::write_setting($data));
9626 * Special class for management of external services
9628 * @author Petr Skoda (skodak)
9630 class admin_setting_manageexternalservices extends admin_setting {
9632 * Calls parent::__construct with specific arguments
9634 public function __construct() {
9635 $this->nosave = true;
9636 parent::__construct('webservicesui', get_string('externalservices', 'webservice'), '', '');
9640 * Always returns true, does nothing
9642 * @return true
9644 public function get_setting() {
9645 return true;
9649 * Always returns true, does nothing
9651 * @return true
9653 public function get_defaultsetting() {
9654 return true;
9658 * Always returns '', does not write anything
9660 * @return string Always returns ''
9662 public function write_setting($data) {
9663 // do not write any setting
9664 return '';
9668 * Checks if $query is one of the available external services
9670 * @param string $query The string to search for
9671 * @return bool Returns true if found, false if not
9673 public function is_related($query) {
9674 global $DB;
9676 if (parent::is_related($query)) {
9677 return true;
9680 $services = $DB->get_records('external_services', array(), 'id, name');
9681 foreach ($services as $service) {
9682 if (strpos(core_text::strtolower($service->name), $query) !== false) {
9683 return true;
9686 return false;
9690 * Builds the XHTML to display the control
9692 * @param string $data Unused
9693 * @param string $query
9694 * @return string
9696 public function output_html($data, $query='') {
9697 global $CFG, $OUTPUT, $DB;
9699 // display strings
9700 $stradministration = get_string('administration');
9701 $stredit = get_string('edit');
9702 $strservice = get_string('externalservice', 'webservice');
9703 $strdelete = get_string('delete');
9704 $strplugin = get_string('plugin', 'admin');
9705 $stradd = get_string('add');
9706 $strfunctions = get_string('functions', 'webservice');
9707 $strusers = get_string('users');
9708 $strserviceusers = get_string('serviceusers', 'webservice');
9710 $esurl = "$CFG->wwwroot/$CFG->admin/webservice/service.php";
9711 $efurl = "$CFG->wwwroot/$CFG->admin/webservice/service_functions.php";
9712 $euurl = "$CFG->wwwroot/$CFG->admin/webservice/service_users.php";
9714 // built in services
9715 $services = $DB->get_records_select('external_services', 'component IS NOT NULL', null, 'name');
9716 $return = "";
9717 if (!empty($services)) {
9718 $return .= $OUTPUT->heading(get_string('servicesbuiltin', 'webservice'), 3, 'main');
9722 $table = new html_table();
9723 $table->head = array($strservice, $strplugin, $strfunctions, $strusers, $stredit);
9724 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9725 $table->id = 'builtinservices';
9726 $table->attributes['class'] = 'admintable externalservices generaltable';
9727 $table->data = array();
9729 // iterate through auth plugins and add to the display table
9730 foreach ($services as $service) {
9731 $name = $service->name;
9733 // hide/show link
9734 if ($service->enabled) {
9735 $displayname = "<span>$name</span>";
9736 } else {
9737 $displayname = "<span class=\"dimmed_text\">$name</span>";
9740 $plugin = $service->component;
9742 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9744 if ($service->restrictedusers) {
9745 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9746 } else {
9747 $users = get_string('allusers', 'webservice');
9750 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9752 // add a row to the table
9753 $table->data[] = array($displayname, $plugin, $functions, $users, $edit);
9755 $return .= html_writer::table($table);
9758 // Custom services
9759 $return .= $OUTPUT->heading(get_string('servicescustom', 'webservice'), 3, 'main');
9760 $services = $DB->get_records_select('external_services', 'component IS NULL', null, 'name');
9762 $table = new html_table();
9763 $table->head = array($strservice, $strdelete, $strfunctions, $strusers, $stredit);
9764 $table->colclasses = array('leftalign service', 'leftalign plugin', 'centeralign functions', 'centeralign users', 'centeralign ');
9765 $table->id = 'customservices';
9766 $table->attributes['class'] = 'admintable externalservices generaltable';
9767 $table->data = array();
9769 // iterate through auth plugins and add to the display table
9770 foreach ($services as $service) {
9771 $name = $service->name;
9773 // hide/show link
9774 if ($service->enabled) {
9775 $displayname = "<span>$name</span>";
9776 } else {
9777 $displayname = "<span class=\"dimmed_text\">$name</span>";
9780 // delete link
9781 $delete = "<a href=\"$esurl?action=delete&amp;sesskey=".sesskey()."&amp;id=$service->id\">$strdelete</a>";
9783 $functions = "<a href=\"$efurl?id=$service->id\">$strfunctions</a>";
9785 if ($service->restrictedusers) {
9786 $users = "<a href=\"$euurl?id=$service->id\">$strserviceusers</a>";
9787 } else {
9788 $users = get_string('allusers', 'webservice');
9791 $edit = "<a href=\"$esurl?id=$service->id\">$stredit</a>";
9793 // add a row to the table
9794 $table->data[] = array($displayname, $delete, $functions, $users, $edit);
9796 // add new custom service option
9797 $return .= html_writer::table($table);
9799 $return .= '<br />';
9800 // add a token to the table
9801 $return .= "<a href=\"$esurl?id=0\">$stradd</a>";
9803 return highlight($query, $return);
9808 * Special class for overview of external services
9810 * @author Jerome Mouneyrac
9812 class admin_setting_webservicesoverview extends admin_setting {
9815 * Calls parent::__construct with specific arguments
9817 public function __construct() {
9818 $this->nosave = true;
9819 parent::__construct('webservicesoverviewui',
9820 get_string('webservicesoverview', 'webservice'), '', '');
9824 * Always returns true, does nothing
9826 * @return true
9828 public function get_setting() {
9829 return true;
9833 * Always returns true, does nothing
9835 * @return true
9837 public function get_defaultsetting() {
9838 return true;
9842 * Always returns '', does not write anything
9844 * @return string Always returns ''
9846 public function write_setting($data) {
9847 // do not write any setting
9848 return '';
9852 * Builds the XHTML to display the control
9854 * @param string $data Unused
9855 * @param string $query
9856 * @return string
9858 public function output_html($data, $query='') {
9859 global $CFG, $OUTPUT;
9861 $return = "";
9862 $brtag = html_writer::empty_tag('br');
9864 /// One system controlling Moodle with Token
9865 $return .= $OUTPUT->heading(get_string('onesystemcontrolling', 'webservice'), 3, 'main');
9866 $table = new html_table();
9867 $table->head = array(get_string('step', 'webservice'), get_string('status'),
9868 get_string('description'));
9869 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
9870 $table->id = 'onesystemcontrol';
9871 $table->attributes['class'] = 'admintable wsoverview generaltable';
9872 $table->data = array();
9874 $return .= $brtag . get_string('onesystemcontrollingdescription', 'webservice')
9875 . $brtag . $brtag;
9877 /// 1. Enable Web Services
9878 $row = array();
9879 $url = new moodle_url("/admin/search.php?query=enablewebservices");
9880 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
9881 array('href' => $url));
9882 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
9883 if ($CFG->enablewebservices) {
9884 $status = get_string('yes');
9886 $row[1] = $status;
9887 $row[2] = get_string('enablewsdescription', 'webservice');
9888 $table->data[] = $row;
9890 /// 2. Enable protocols
9891 $row = array();
9892 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
9893 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
9894 array('href' => $url));
9895 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
9896 //retrieve activated protocol
9897 $active_protocols = empty($CFG->webserviceprotocols) ?
9898 array() : explode(',', $CFG->webserviceprotocols);
9899 if (!empty($active_protocols)) {
9900 $status = "";
9901 foreach ($active_protocols as $protocol) {
9902 $status .= $protocol . $brtag;
9905 $row[1] = $status;
9906 $row[2] = get_string('enableprotocolsdescription', 'webservice');
9907 $table->data[] = $row;
9909 /// 3. Create user account
9910 $row = array();
9911 $url = new moodle_url("/user/editadvanced.php?id=-1");
9912 $row[0] = "3. " . html_writer::tag('a', get_string('createuser', 'webservice'),
9913 array('href' => $url));
9914 $row[1] = "";
9915 $row[2] = get_string('createuserdescription', 'webservice');
9916 $table->data[] = $row;
9918 /// 4. Add capability to users
9919 $row = array();
9920 $url = new moodle_url("/admin/roles/check.php?contextid=1");
9921 $row[0] = "4. " . html_writer::tag('a', get_string('checkusercapability', 'webservice'),
9922 array('href' => $url));
9923 $row[1] = "";
9924 $row[2] = get_string('checkusercapabilitydescription', 'webservice');
9925 $table->data[] = $row;
9927 /// 5. Select a web service
9928 $row = array();
9929 $url = new moodle_url("/admin/settings.php?section=externalservices");
9930 $row[0] = "5. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
9931 array('href' => $url));
9932 $row[1] = "";
9933 $row[2] = get_string('createservicedescription', 'webservice');
9934 $table->data[] = $row;
9936 /// 6. Add functions
9937 $row = array();
9938 $url = new moodle_url("/admin/settings.php?section=externalservices");
9939 $row[0] = "6. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
9940 array('href' => $url));
9941 $row[1] = "";
9942 $row[2] = get_string('addfunctionsdescription', 'webservice');
9943 $table->data[] = $row;
9945 /// 7. Add the specific user
9946 $row = array();
9947 $url = new moodle_url("/admin/settings.php?section=externalservices");
9948 $row[0] = "7. " . html_writer::tag('a', get_string('selectspecificuser', 'webservice'),
9949 array('href' => $url));
9950 $row[1] = "";
9951 $row[2] = get_string('selectspecificuserdescription', 'webservice');
9952 $table->data[] = $row;
9954 /// 8. Create token for the specific user
9955 $row = array();
9956 $url = new moodle_url("/admin/webservice/tokens.php?sesskey=" . sesskey() . "&action=create");
9957 $row[0] = "8. " . html_writer::tag('a', get_string('createtokenforuser', 'webservice'),
9958 array('href' => $url));
9959 $row[1] = "";
9960 $row[2] = get_string('createtokenforuserdescription', 'webservice');
9961 $table->data[] = $row;
9963 /// 9. Enable the documentation
9964 $row = array();
9965 $url = new moodle_url("/admin/search.php?query=enablewsdocumentation");
9966 $row[0] = "9. " . html_writer::tag('a', get_string('enabledocumentation', 'webservice'),
9967 array('href' => $url));
9968 $status = '<span class="warning">' . get_string('no') . '</span>';
9969 if ($CFG->enablewsdocumentation) {
9970 $status = get_string('yes');
9972 $row[1] = $status;
9973 $row[2] = get_string('enabledocumentationdescription', 'webservice');
9974 $table->data[] = $row;
9976 /// 10. Test the service
9977 $row = array();
9978 $url = new moodle_url("/admin/webservice/testclient.php");
9979 $row[0] = "10. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
9980 array('href' => $url));
9981 $row[1] = "";
9982 $row[2] = get_string('testwithtestclientdescription', 'webservice');
9983 $table->data[] = $row;
9985 $return .= html_writer::table($table);
9987 /// Users as clients with token
9988 $return .= $brtag . $brtag . $brtag;
9989 $return .= $OUTPUT->heading(get_string('userasclients', 'webservice'), 3, 'main');
9990 $table = new html_table();
9991 $table->head = array(get_string('step', 'webservice'), get_string('status'),
9992 get_string('description'));
9993 $table->colclasses = array('leftalign step', 'leftalign status', 'leftalign description');
9994 $table->id = 'userasclients';
9995 $table->attributes['class'] = 'admintable wsoverview generaltable';
9996 $table->data = array();
9998 $return .= $brtag . get_string('userasclientsdescription', 'webservice') .
9999 $brtag . $brtag;
10001 /// 1. Enable Web Services
10002 $row = array();
10003 $url = new moodle_url("/admin/search.php?query=enablewebservices");
10004 $row[0] = "1. " . html_writer::tag('a', get_string('enablews', 'webservice'),
10005 array('href' => $url));
10006 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
10007 if ($CFG->enablewebservices) {
10008 $status = get_string('yes');
10010 $row[1] = $status;
10011 $row[2] = get_string('enablewsdescription', 'webservice');
10012 $table->data[] = $row;
10014 /// 2. Enable protocols
10015 $row = array();
10016 $url = new moodle_url("/admin/settings.php?section=webserviceprotocols");
10017 $row[0] = "2. " . html_writer::tag('a', get_string('enableprotocols', 'webservice'),
10018 array('href' => $url));
10019 $status = html_writer::tag('span', get_string('none'), array('class' => 'badge badge-danger'));
10020 //retrieve activated protocol
10021 $active_protocols = empty($CFG->webserviceprotocols) ?
10022 array() : explode(',', $CFG->webserviceprotocols);
10023 if (!empty($active_protocols)) {
10024 $status = "";
10025 foreach ($active_protocols as $protocol) {
10026 $status .= $protocol . $brtag;
10029 $row[1] = $status;
10030 $row[2] = get_string('enableprotocolsdescription', 'webservice');
10031 $table->data[] = $row;
10034 /// 3. Select a web service
10035 $row = array();
10036 $url = new moodle_url("/admin/settings.php?section=externalservices");
10037 $row[0] = "3. " . html_writer::tag('a', get_string('selectservice', 'webservice'),
10038 array('href' => $url));
10039 $row[1] = "";
10040 $row[2] = get_string('createserviceforusersdescription', 'webservice');
10041 $table->data[] = $row;
10043 /// 4. Add functions
10044 $row = array();
10045 $url = new moodle_url("/admin/settings.php?section=externalservices");
10046 $row[0] = "4. " . html_writer::tag('a', get_string('addfunctions', 'webservice'),
10047 array('href' => $url));
10048 $row[1] = "";
10049 $row[2] = get_string('addfunctionsdescription', 'webservice');
10050 $table->data[] = $row;
10052 /// 5. Add capability to users
10053 $row = array();
10054 $url = new moodle_url("/admin/roles/check.php?contextid=1");
10055 $row[0] = "5. " . html_writer::tag('a', get_string('addcapabilitytousers', 'webservice'),
10056 array('href' => $url));
10057 $row[1] = "";
10058 $row[2] = get_string('addcapabilitytousersdescription', 'webservice');
10059 $table->data[] = $row;
10061 /// 6. Test the service
10062 $row = array();
10063 $url = new moodle_url("/admin/webservice/testclient.php");
10064 $row[0] = "6. " . html_writer::tag('a', get_string('testwithtestclient', 'webservice'),
10065 array('href' => $url));
10066 $row[1] = "";
10067 $row[2] = get_string('testauserwithtestclientdescription', 'webservice');
10068 $table->data[] = $row;
10070 $return .= html_writer::table($table);
10072 return highlight($query, $return);
10079 * Special class for web service protocol administration.
10081 * @author Petr Skoda (skodak)
10083 class admin_setting_managewebserviceprotocols extends admin_setting {
10086 * Calls parent::__construct with specific arguments
10088 public function __construct() {
10089 $this->nosave = true;
10090 parent::__construct('webservicesui', get_string('manageprotocols', 'webservice'), '', '');
10094 * Always returns true, does nothing
10096 * @return true
10098 public function get_setting() {
10099 return true;
10103 * Always returns true, does nothing
10105 * @return true
10107 public function get_defaultsetting() {
10108 return true;
10112 * Always returns '', does not write anything
10114 * @return string Always returns ''
10116 public function write_setting($data) {
10117 // do not write any setting
10118 return '';
10122 * Checks if $query is one of the available webservices
10124 * @param string $query The string to search for
10125 * @return bool Returns true if found, false if not
10127 public function is_related($query) {
10128 if (parent::is_related($query)) {
10129 return true;
10132 $protocols = core_component::get_plugin_list('webservice');
10133 foreach ($protocols as $protocol=>$location) {
10134 if (strpos($protocol, $query) !== false) {
10135 return true;
10137 $protocolstr = get_string('pluginname', 'webservice_'.$protocol);
10138 if (strpos(core_text::strtolower($protocolstr), $query) !== false) {
10139 return true;
10142 return false;
10146 * Builds the XHTML to display the control
10148 * @param string $data Unused
10149 * @param string $query
10150 * @return string
10152 public function output_html($data, $query='') {
10153 global $CFG, $OUTPUT;
10155 // display strings
10156 $stradministration = get_string('administration');
10157 $strsettings = get_string('settings');
10158 $stredit = get_string('edit');
10159 $strprotocol = get_string('protocol', 'webservice');
10160 $strenable = get_string('enable');
10161 $strdisable = get_string('disable');
10162 $strversion = get_string('version');
10164 $protocols_available = core_component::get_plugin_list('webservice');
10165 $active_protocols = empty($CFG->webserviceprotocols) ? array() : explode(',', $CFG->webserviceprotocols);
10166 ksort($protocols_available);
10168 foreach ($active_protocols as $key=>$protocol) {
10169 if (empty($protocols_available[$protocol])) {
10170 unset($active_protocols[$key]);
10174 $return = $OUTPUT->heading(get_string('actwebserviceshhdr', 'webservice'), 3, 'main');
10175 $return .= $OUTPUT->box_start('generalbox webservicesui');
10177 $table = new html_table();
10178 $table->head = array($strprotocol, $strversion, $strenable, $strsettings);
10179 $table->colclasses = array('leftalign', 'centeralign', 'centeralign', 'centeralign', 'centeralign');
10180 $table->id = 'webserviceprotocols';
10181 $table->attributes['class'] = 'admintable generaltable';
10182 $table->data = array();
10184 // iterate through auth plugins and add to the display table
10185 $url = "$CFG->wwwroot/$CFG->admin/webservice/protocols.php?sesskey=" . sesskey();
10186 foreach ($protocols_available as $protocol => $location) {
10187 $name = get_string('pluginname', 'webservice_'.$protocol);
10189 $plugin = new stdClass();
10190 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/version.php')) {
10191 include($CFG->dirroot.'/webservice/'.$protocol.'/version.php');
10193 $version = isset($plugin->version) ? $plugin->version : '';
10195 // hide/show link
10196 if (in_array($protocol, $active_protocols)) {
10197 $hideshow = "<a href=\"$url&amp;action=disable&amp;webservice=$protocol\">";
10198 $hideshow .= $OUTPUT->pix_icon('t/hide', $strdisable) . '</a>';
10199 $displayname = "<span>$name</span>";
10200 } else {
10201 $hideshow = "<a href=\"$url&amp;action=enable&amp;webservice=$protocol\">";
10202 $hideshow .= $OUTPUT->pix_icon('t/show', $strenable) . '</a>';
10203 $displayname = "<span class=\"dimmed_text\">$name</span>";
10206 // settings link
10207 if (file_exists($CFG->dirroot.'/webservice/'.$protocol.'/settings.php')) {
10208 $settings = "<a href=\"settings.php?section=webservicesetting$protocol\">$strsettings</a>";
10209 } else {
10210 $settings = '';
10213 // add a row to the table
10214 $table->data[] = array($displayname, $version, $hideshow, $settings);
10216 $return .= html_writer::table($table);
10217 $return .= get_string('configwebserviceplugins', 'webservice');
10218 $return .= $OUTPUT->box_end();
10220 return highlight($query, $return);
10226 * Special class for web service token administration.
10228 * @author Jerome Mouneyrac
10230 class admin_setting_managewebservicetokens extends admin_setting {
10233 * Calls parent::__construct with specific arguments
10235 public function __construct() {
10236 $this->nosave = true;
10237 parent::__construct('webservicestokenui', get_string('managetokens', 'webservice'), '', '');
10241 * Always returns true, does nothing
10243 * @return true
10245 public function get_setting() {
10246 return true;
10250 * Always returns true, does nothing
10252 * @return true
10254 public function get_defaultsetting() {
10255 return true;
10259 * Always returns '', does not write anything
10261 * @return string Always returns ''
10263 public function write_setting($data) {
10264 // do not write any setting
10265 return '';
10269 * Builds the XHTML to display the control
10271 * @param string $data Unused
10272 * @param string $query
10273 * @return string
10275 public function output_html($data, $query='') {
10276 global $CFG, $OUTPUT;
10278 require_once($CFG->dirroot . '/webservice/classes/token_table.php');
10279 $baseurl = new moodle_url('/' . $CFG->admin . '/settings.php?section=webservicetokens');
10281 $return = $OUTPUT->box_start('generalbox webservicestokenui');
10283 if (has_capability('moodle/webservice:managealltokens', context_system::instance())) {
10284 $return .= \html_writer::div(get_string('onlyseecreatedtokens', 'webservice'));
10287 $table = new \webservice\token_table('webservicetokens');
10288 $table->define_baseurl($baseurl);
10289 $table->attributes['class'] = 'admintable generaltable'; // Any need changing?
10290 $table->data = array();
10291 ob_start();
10292 $table->out(10, false);
10293 $tablehtml = ob_get_contents();
10294 ob_end_clean();
10295 $return .= $tablehtml;
10297 $tokenpageurl = "$CFG->wwwroot/$CFG->admin/webservice/tokens.php?sesskey=" . sesskey();
10299 $return .= $OUTPUT->box_end();
10300 // add a token to the table
10301 $return .= "<a href=\"".$tokenpageurl."&amp;action=create\">";
10302 $return .= get_string('add')."</a>";
10304 return highlight($query, $return);
10310 * Colour picker
10312 * @copyright 2010 Sam Hemelryk
10313 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10315 class admin_setting_configcolourpicker extends admin_setting {
10318 * Information for previewing the colour
10320 * @var array|null
10322 protected $previewconfig = null;
10325 * Use default when empty.
10327 protected $usedefaultwhenempty = true;
10331 * @param string $name
10332 * @param string $visiblename
10333 * @param string $description
10334 * @param string $defaultsetting
10335 * @param array $previewconfig Array('selector'=>'.some .css .selector','style'=>'backgroundColor');
10337 public function __construct($name, $visiblename, $description, $defaultsetting, array $previewconfig = null,
10338 $usedefaultwhenempty = true) {
10339 $this->previewconfig = $previewconfig;
10340 $this->usedefaultwhenempty = $usedefaultwhenempty;
10341 parent::__construct($name, $visiblename, $description, $defaultsetting);
10342 $this->set_force_ltr(true);
10346 * Return the setting
10348 * @return mixed returns config if successful else null
10350 public function get_setting() {
10351 return $this->config_read($this->name);
10355 * Saves the setting
10357 * @param string $data
10358 * @return bool
10360 public function write_setting($data) {
10361 $data = $this->validate($data);
10362 if ($data === false) {
10363 return get_string('validateerror', 'admin');
10365 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
10369 * Validates the colour that was entered by the user
10371 * @param string $data
10372 * @return string|false
10374 protected function validate($data) {
10376 * List of valid HTML colour names
10378 * @var array
10380 $colornames = array(
10381 'aliceblue', 'antiquewhite', 'aqua', 'aquamarine', 'azure',
10382 'beige', 'bisque', 'black', 'blanchedalmond', 'blue',
10383 'blueviolet', 'brown', 'burlywood', 'cadetblue', 'chartreuse',
10384 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson',
10385 'cyan', 'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray',
10386 'darkgrey', 'darkgreen', 'darkkhaki', 'darkmagenta',
10387 'darkolivegreen', 'darkorange', 'darkorchid', 'darkred',
10388 'darksalmon', 'darkseagreen', 'darkslateblue', 'darkslategray',
10389 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink',
10390 'deepskyblue', 'dimgray', 'dimgrey', 'dodgerblue', 'firebrick',
10391 'floralwhite', 'forestgreen', 'fuchsia', 'gainsboro',
10392 'ghostwhite', 'gold', 'goldenrod', 'gray', 'grey', 'green',
10393 'greenyellow', 'honeydew', 'hotpink', 'indianred', 'indigo',
10394 'ivory', 'khaki', 'lavender', 'lavenderblush', 'lawngreen',
10395 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan',
10396 'lightgoldenrodyellow', 'lightgray', 'lightgrey', 'lightgreen',
10397 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue',
10398 'lightslategray', 'lightslategrey', 'lightsteelblue', 'lightyellow',
10399 'lime', 'limegreen', 'linen', 'magenta', 'maroon',
10400 'mediumaquamarine', 'mediumblue', 'mediumorchid', 'mediumpurple',
10401 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
10402 'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream',
10403 'mistyrose', 'moccasin', 'navajowhite', 'navy', 'oldlace', 'olive',
10404 'olivedrab', 'orange', 'orangered', 'orchid', 'palegoldenrod',
10405 'palegreen', 'paleturquoise', 'palevioletred', 'papayawhip',
10406 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'purple', 'red',
10407 'rosybrown', 'royalblue', 'saddlebrown', 'salmon', 'sandybrown',
10408 'seagreen', 'seashell', 'sienna', 'silver', 'skyblue', 'slateblue',
10409 'slategray', 'slategrey', 'snow', 'springgreen', 'steelblue', 'tan',
10410 'teal', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'white',
10411 'whitesmoke', 'yellow', 'yellowgreen'
10414 if (preg_match('/^#?([[:xdigit:]]{3}){1,2}$/', $data)) {
10415 if (strpos($data, '#')!==0) {
10416 $data = '#'.$data;
10418 return $data;
10419 } else if (in_array(strtolower($data), $colornames)) {
10420 return $data;
10421 } else if (preg_match('/rgb\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\)/i', $data)) {
10422 return $data;
10423 } else if (preg_match('/rgba\(\d{0,3}%?\, ?\d{0,3}%?, ?\d{0,3}%?\, ?\d(\.\d)?\)/i', $data)) {
10424 return $data;
10425 } else if (preg_match('/hsl\(\d{0,3}\, ?\d{0,3}%, ?\d{0,3}%\)/i', $data)) {
10426 return $data;
10427 } else if (preg_match('/hsla\(\d{0,3}\, ?\d{0,3}%,\d{0,3}%\, ?\d(\.\d)?\)/i', $data)) {
10428 return $data;
10429 } else if (($data == 'transparent') || ($data == 'currentColor') || ($data == 'inherit')) {
10430 return $data;
10431 } else if (empty($data)) {
10432 if ($this->usedefaultwhenempty){
10433 return $this->defaultsetting;
10434 } else {
10435 return '';
10437 } else {
10438 return false;
10443 * Generates the HTML for the setting
10445 * @global moodle_page $PAGE
10446 * @global core_renderer $OUTPUT
10447 * @param string $data
10448 * @param string $query
10450 public function output_html($data, $query = '') {
10451 global $PAGE, $OUTPUT;
10453 $icon = new pix_icon('i/loading', get_string('loading', 'admin'), 'moodle', ['class' => 'loadingicon']);
10454 $context = (object) [
10455 'id' => $this->get_id(),
10456 'name' => $this->get_full_name(),
10457 'value' => $data,
10458 'icon' => $icon->export_for_template($OUTPUT),
10459 'haspreviewconfig' => !empty($this->previewconfig),
10460 'forceltr' => $this->get_force_ltr(),
10461 'readonly' => $this->is_readonly(),
10464 $element = $OUTPUT->render_from_template('core_admin/setting_configcolourpicker', $context);
10465 $PAGE->requires->js_init_call('M.util.init_colour_picker', array($this->get_id(), $this->previewconfig));
10467 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '',
10468 $this->get_defaultsetting(), $query);
10475 * Class used for uploading of one file into file storage,
10476 * the file name is stored in config table.
10478 * Please note you need to implement your own '_pluginfile' callback function,
10479 * this setting only stores the file, it does not deal with file serving.
10481 * @copyright 2013 Petr Skoda {@link http://skodak.org}
10482 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10484 class admin_setting_configstoredfile extends admin_setting {
10485 /** @var array file area options - should be one file only */
10486 protected $options;
10487 /** @var string name of the file area */
10488 protected $filearea;
10489 /** @var int intemid */
10490 protected $itemid;
10491 /** @var string used for detection of changes */
10492 protected $oldhashes;
10495 * Create new stored file setting.
10497 * @param string $name low level setting name
10498 * @param string $visiblename human readable setting name
10499 * @param string $description description of setting
10500 * @param mixed $filearea file area for file storage
10501 * @param int $itemid itemid for file storage
10502 * @param array $options file area options
10504 public function __construct($name, $visiblename, $description, $filearea, $itemid = 0, array $options = null) {
10505 parent::__construct($name, $visiblename, $description, '');
10506 $this->filearea = $filearea;
10507 $this->itemid = $itemid;
10508 $this->options = (array)$options;
10509 $this->customcontrol = true;
10513 * Applies defaults and returns all options.
10514 * @return array
10516 protected function get_options() {
10517 global $CFG;
10519 require_once("$CFG->libdir/filelib.php");
10520 require_once("$CFG->dirroot/repository/lib.php");
10521 $defaults = array(
10522 'mainfile' => '', 'subdirs' => 0, 'maxbytes' => -1, 'maxfiles' => 1,
10523 'accepted_types' => '*', 'return_types' => FILE_INTERNAL, 'areamaxbytes' => FILE_AREA_MAX_BYTES_UNLIMITED,
10524 'context' => context_system::instance());
10525 foreach($this->options as $k => $v) {
10526 $defaults[$k] = $v;
10529 return $defaults;
10532 public function get_setting() {
10533 return $this->config_read($this->name);
10536 public function write_setting($data) {
10537 global $USER;
10539 // Let's not deal with validation here, this is for admins only.
10540 $current = $this->get_setting();
10541 if (empty($data) && $current === null) {
10542 // This will be the case when applying default settings (installation).
10543 return ($this->config_write($this->name, '') ? '' : get_string('errorsetting', 'admin'));
10544 } else if (!is_number($data)) {
10545 // Draft item id is expected here!
10546 return get_string('errorsetting', 'admin');
10549 $options = $this->get_options();
10550 $fs = get_file_storage();
10551 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10553 $this->oldhashes = null;
10554 if ($current) {
10555 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10556 if ($file = $fs->get_file_by_hash($hash)) {
10557 $this->oldhashes = $file->get_contenthash().$file->get_pathnamehash();
10559 unset($file);
10562 if ($fs->file_exists($options['context']->id, $component, $this->filearea, $this->itemid, '/', '.')) {
10563 // Make sure the settings form was not open for more than 4 days and draft areas deleted in the meantime.
10564 // But we can safely ignore that if the destination area is empty, so that the user is not prompt
10565 // with an error because the draft area does not exist, as he did not use it.
10566 $usercontext = context_user::instance($USER->id);
10567 if (!$fs->file_exists($usercontext->id, 'user', 'draft', $data, '/', '.') && $current !== '') {
10568 return get_string('errorsetting', 'admin');
10572 file_save_draft_area_files($data, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10573 $files = $fs->get_area_files($options['context']->id, $component, $this->filearea, $this->itemid, 'sortorder,filepath,filename', false);
10575 $filepath = '';
10576 if ($files) {
10577 /** @var stored_file $file */
10578 $file = reset($files);
10579 $filepath = $file->get_filepath().$file->get_filename();
10582 return ($this->config_write($this->name, $filepath) ? '' : get_string('errorsetting', 'admin'));
10585 public function post_write_settings($original) {
10586 $options = $this->get_options();
10587 $fs = get_file_storage();
10588 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10590 $current = $this->get_setting();
10591 $newhashes = null;
10592 if ($current) {
10593 $hash = sha1('/'.$options['context']->id.'/'.$component.'/'.$this->filearea.'/'.$this->itemid.$current);
10594 if ($file = $fs->get_file_by_hash($hash)) {
10595 $newhashes = $file->get_contenthash().$file->get_pathnamehash();
10597 unset($file);
10600 if ($this->oldhashes === $newhashes) {
10601 $this->oldhashes = null;
10602 return false;
10604 $this->oldhashes = null;
10606 $callbackfunction = $this->updatedcallback;
10607 if (!empty($callbackfunction) and function_exists($callbackfunction)) {
10608 $callbackfunction($this->get_full_name());
10610 return true;
10613 public function output_html($data, $query = '') {
10614 global $PAGE, $CFG;
10616 $options = $this->get_options();
10617 $id = $this->get_id();
10618 $elname = $this->get_full_name();
10619 $draftitemid = file_get_submitted_draft_itemid($elname);
10620 $component = is_null($this->plugin) ? 'core' : $this->plugin;
10621 file_prepare_draft_area($draftitemid, $options['context']->id, $component, $this->filearea, $this->itemid, $options);
10623 // Filemanager form element implementation is far from optimal, we need to rework this if we ever fix it...
10624 require_once("$CFG->dirroot/lib/form/filemanager.php");
10626 $fmoptions = new stdClass();
10627 $fmoptions->mainfile = $options['mainfile'];
10628 $fmoptions->maxbytes = $options['maxbytes'];
10629 $fmoptions->maxfiles = $options['maxfiles'];
10630 $fmoptions->client_id = uniqid();
10631 $fmoptions->itemid = $draftitemid;
10632 $fmoptions->subdirs = $options['subdirs'];
10633 $fmoptions->target = $id;
10634 $fmoptions->accepted_types = $options['accepted_types'];
10635 $fmoptions->return_types = $options['return_types'];
10636 $fmoptions->context = $options['context'];
10637 $fmoptions->areamaxbytes = $options['areamaxbytes'];
10639 $fm = new form_filemanager($fmoptions);
10640 $output = $PAGE->get_renderer('core', 'files');
10641 $html = $output->render($fm);
10643 $html .= '<input value="'.$draftitemid.'" name="'.$elname.'" type="hidden" />';
10644 $html .= '<input value="" id="'.$id.'" type="hidden" />';
10646 return format_admin_setting($this, $this->visiblename,
10647 '<div class="form-filemanager" data-fieldtype="filemanager">'.$html.'</div>',
10648 $this->description, true, '', '', $query);
10654 * Administration interface for user specified regular expressions for device detection.
10656 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10658 class admin_setting_devicedetectregex extends admin_setting {
10661 * Calls parent::__construct with specific args
10663 * @param string $name
10664 * @param string $visiblename
10665 * @param string $description
10666 * @param mixed $defaultsetting
10668 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
10669 global $CFG;
10670 parent::__construct($name, $visiblename, $description, $defaultsetting);
10674 * Return the current setting(s)
10676 * @return array Current settings array
10678 public function get_setting() {
10679 global $CFG;
10681 $config = $this->config_read($this->name);
10682 if (is_null($config)) {
10683 return null;
10686 return $this->prepare_form_data($config);
10690 * Save selected settings
10692 * @param array $data Array of settings to save
10693 * @return bool
10695 public function write_setting($data) {
10696 if (empty($data)) {
10697 $data = array();
10700 if ($this->config_write($this->name, $this->process_form_data($data))) {
10701 return ''; // success
10702 } else {
10703 return get_string('errorsetting', 'admin') . $this->visiblename . html_writer::empty_tag('br');
10708 * Return XHTML field(s) for regexes
10710 * @param array $data Array of options to set in HTML
10711 * @return string XHTML string for the fields and wrapping div(s)
10713 public function output_html($data, $query='') {
10714 global $OUTPUT;
10716 $context = (object) [
10717 'expressions' => [],
10718 'name' => $this->get_full_name()
10721 if (empty($data)) {
10722 $looplimit = 1;
10723 } else {
10724 $looplimit = (count($data)/2)+1;
10727 for ($i=0; $i<$looplimit; $i++) {
10729 $expressionname = 'expression'.$i;
10731 if (!empty($data[$expressionname])){
10732 $expression = $data[$expressionname];
10733 } else {
10734 $expression = '';
10737 $valuename = 'value'.$i;
10739 if (!empty($data[$valuename])){
10740 $value = $data[$valuename];
10741 } else {
10742 $value= '';
10745 $context->expressions[] = [
10746 'index' => $i,
10747 'expression' => $expression,
10748 'value' => $value
10752 $element = $OUTPUT->render_from_template('core_admin/setting_devicedetectregex', $context);
10754 return format_admin_setting($this, $this->visiblename, $element, $this->description, false, '', null, $query);
10758 * Converts the string of regexes
10760 * @see self::process_form_data()
10761 * @param $regexes string of regexes
10762 * @return array of form fields and their values
10764 protected function prepare_form_data($regexes) {
10766 $regexes = json_decode($regexes);
10768 $form = array();
10770 $i = 0;
10772 foreach ($regexes as $value => $regex) {
10773 $expressionname = 'expression'.$i;
10774 $valuename = 'value'.$i;
10776 $form[$expressionname] = $regex;
10777 $form[$valuename] = $value;
10778 $i++;
10781 return $form;
10785 * Converts the data from admin settings form into a string of regexes
10787 * @see self::prepare_form_data()
10788 * @param array $data array of admin form fields and values
10789 * @return false|string of regexes
10791 protected function process_form_data(array $form) {
10793 $count = count($form); // number of form field values
10795 if ($count % 2) {
10796 // we must get five fields per expression
10797 return false;
10800 $regexes = array();
10801 for ($i = 0; $i < $count / 2; $i++) {
10802 $expressionname = "expression".$i;
10803 $valuename = "value".$i;
10805 $expression = trim($form['expression'.$i]);
10806 $value = trim($form['value'.$i]);
10808 if (empty($expression)){
10809 continue;
10812 $regexes[$value] = $expression;
10815 $regexes = json_encode($regexes);
10817 return $regexes;
10823 * Multiselect for current modules
10825 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10827 class admin_setting_configmultiselect_modules extends admin_setting_configmultiselect {
10828 private $excludesystem;
10831 * Calls parent::__construct - note array $choices is not required
10833 * @param string $name setting name
10834 * @param string $visiblename localised setting name
10835 * @param string $description setting description
10836 * @param array $defaultsetting a plain array of default module ids
10837 * @param bool $excludesystem If true, excludes modules with 'system' archetype
10839 public function __construct($name, $visiblename, $description, $defaultsetting = array(),
10840 $excludesystem = true) {
10841 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
10842 $this->excludesystem = $excludesystem;
10846 * Loads an array of current module choices
10848 * @return bool always return true
10850 public function load_choices() {
10851 if (is_array($this->choices)) {
10852 return true;
10854 $this->choices = array();
10856 global $CFG, $DB;
10857 $records = $DB->get_records('modules', array('visible'=>1), 'name');
10858 foreach ($records as $record) {
10859 // Exclude modules if the code doesn't exist
10860 if (file_exists("$CFG->dirroot/mod/$record->name/lib.php")) {
10861 // Also exclude system modules (if specified)
10862 if (!($this->excludesystem &&
10863 plugin_supports('mod', $record->name, FEATURE_MOD_ARCHETYPE) ===
10864 MOD_ARCHETYPE_SYSTEM)) {
10865 $this->choices[$record->id] = $record->name;
10869 return true;
10874 * Admin setting to show if a php extension is enabled or not.
10876 * @copyright 2013 Damyon Wiese
10877 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10879 class admin_setting_php_extension_enabled extends admin_setting {
10881 /** @var string The name of the extension to check for */
10882 private $extension;
10885 * Calls parent::__construct with specific arguments
10887 public function __construct($name, $visiblename, $description, $extension) {
10888 $this->extension = $extension;
10889 $this->nosave = true;
10890 parent::__construct($name, $visiblename, $description, '');
10894 * Always returns true, does nothing
10896 * @return true
10898 public function get_setting() {
10899 return true;
10903 * Always returns true, does nothing
10905 * @return true
10907 public function get_defaultsetting() {
10908 return true;
10912 * Always returns '', does not write anything
10914 * @return string Always returns ''
10916 public function write_setting($data) {
10917 // Do not write any setting.
10918 return '';
10922 * Outputs the html for this setting.
10923 * @return string Returns an XHTML string
10925 public function output_html($data, $query='') {
10926 global $OUTPUT;
10928 $o = '';
10929 if (!extension_loaded($this->extension)) {
10930 $warning = $OUTPUT->pix_icon('i/warning', '', '', array('role' => 'presentation')) . ' ' . $this->description;
10932 $o .= format_admin_setting($this, $this->visiblename, $warning);
10934 return $o;
10939 * Server timezone setting.
10941 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
10942 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10943 * @author Petr Skoda <petr.skoda@totaralms.com>
10945 class admin_setting_servertimezone extends admin_setting_configselect {
10947 * Constructor.
10949 public function __construct() {
10950 $default = core_date::get_default_php_timezone();
10951 if ($default === 'UTC') {
10952 // Nobody really wants UTC, so instead default selection to the country that is confused by the UTC the most.
10953 $default = 'Europe/London';
10956 parent::__construct('timezone',
10957 new lang_string('timezone', 'core_admin'),
10958 new lang_string('configtimezone', 'core_admin'), $default, null);
10962 * Lazy load timezone options.
10963 * @return bool true if loaded, false if error
10965 public function load_choices() {
10966 global $CFG;
10967 if (is_array($this->choices)) {
10968 return true;
10971 $current = isset($CFG->timezone) ? $CFG->timezone : null;
10972 $this->choices = core_date::get_list_of_timezones($current, false);
10973 if ($current == 99) {
10974 // Do not show 99 unless it is current value, we want to get rid of it over time.
10975 $this->choices['99'] = new lang_string('timezonephpdefault', 'core_admin',
10976 core_date::get_default_php_timezone());
10979 return true;
10984 * Forced user timezone setting.
10986 * @copyright 2015 Totara Learning Solutions Ltd {@link http://www.totaralms.com/}
10987 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
10988 * @author Petr Skoda <petr.skoda@totaralms.com>
10990 class admin_setting_forcetimezone extends admin_setting_configselect {
10992 * Constructor.
10994 public function __construct() {
10995 parent::__construct('forcetimezone',
10996 new lang_string('forcetimezone', 'core_admin'),
10997 new lang_string('helpforcetimezone', 'core_admin'), '99', null);
11001 * Lazy load timezone options.
11002 * @return bool true if loaded, false if error
11004 public function load_choices() {
11005 global $CFG;
11006 if (is_array($this->choices)) {
11007 return true;
11010 $current = isset($CFG->forcetimezone) ? $CFG->forcetimezone : null;
11011 $this->choices = core_date::get_list_of_timezones($current, true);
11012 $this->choices['99'] = new lang_string('timezonenotforced', 'core_admin');
11014 return true;
11020 * Search setup steps info.
11022 * @package core
11023 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
11024 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11026 class admin_setting_searchsetupinfo extends admin_setting {
11029 * Calls parent::__construct with specific arguments
11031 public function __construct() {
11032 $this->nosave = true;
11033 parent::__construct('searchsetupinfo', '', '', '');
11037 * Always returns true, does nothing
11039 * @return true
11041 public function get_setting() {
11042 return true;
11046 * Always returns true, does nothing
11048 * @return true
11050 public function get_defaultsetting() {
11051 return true;
11055 * Always returns '', does not write anything
11057 * @param array $data
11058 * @return string Always returns ''
11060 public function write_setting($data) {
11061 // Do not write any setting.
11062 return '';
11066 * Builds the HTML to display the control
11068 * @param string $data Unused
11069 * @param string $query
11070 * @return string
11072 public function output_html($data, $query='') {
11073 global $CFG, $OUTPUT, $ADMIN;
11075 $return = '';
11076 $brtag = html_writer::empty_tag('br');
11078 $searchareas = \core_search\manager::get_search_areas_list();
11079 $anyenabled = !empty(\core_search\manager::get_search_areas_list(true));
11080 $anyindexed = false;
11081 foreach ($searchareas as $areaid => $searcharea) {
11082 list($componentname, $varname) = $searcharea->get_config_var_name();
11083 if (get_config($componentname, $varname . '_indexingstart')) {
11084 $anyindexed = true;
11085 break;
11089 $return .= $OUTPUT->heading(get_string('searchsetupinfo', 'admin'), 3, 'main');
11091 $table = new html_table();
11092 $table->head = array(get_string('step', 'search'), get_string('status'));
11093 $table->colclasses = array('leftalign step', 'leftalign status');
11094 $table->id = 'searchsetup';
11095 $table->attributes['class'] = 'admintable generaltable';
11096 $table->data = array();
11098 $return .= $brtag . get_string('searchsetupdescription', 'search') . $brtag . $brtag;
11100 // Select a search engine.
11101 $row = array();
11102 $url = new moodle_url('/admin/settings.php?section=manageglobalsearch#admin-searchengine');
11103 $row[0] = '1. ' . html_writer::tag('a', get_string('selectsearchengine', 'admin'),
11104 array('href' => $url));
11106 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11107 if (!empty($CFG->searchengine)) {
11108 $status = html_writer::tag('span', get_string('pluginname', 'search_' . $CFG->searchengine),
11109 array('class' => 'badge badge-success'));
11112 $row[1] = $status;
11113 $table->data[] = $row;
11115 // Available areas.
11116 $row = array();
11117 $url = new moodle_url('/admin/searchareas.php');
11118 $row[0] = '2. ' . html_writer::tag('a', get_string('enablesearchareas', 'admin'),
11119 array('href' => $url));
11121 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11122 if ($anyenabled) {
11123 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11126 $row[1] = $status;
11127 $table->data[] = $row;
11129 // Setup search engine.
11130 $row = array();
11131 if (empty($CFG->searchengine)) {
11132 $row[0] = '3. ' . get_string('setupsearchengine', 'admin');
11133 $row[1] = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11134 } else {
11135 if ($ADMIN->locate('search' . $CFG->searchengine)) {
11136 $url = new moodle_url('/admin/settings.php?section=search' . $CFG->searchengine);
11137 $row[0] = '3. ' . html_writer::link($url, get_string('setupsearchengine', 'core_admin'));
11138 } else {
11139 $row[0] = '3. ' . get_string('setupsearchengine', 'core_admin');
11142 // Check the engine status.
11143 $searchengine = \core_search\manager::search_engine_instance();
11144 try {
11145 $serverstatus = $searchengine->is_server_ready();
11146 } catch (\moodle_exception $e) {
11147 $serverstatus = $e->getMessage();
11149 if ($serverstatus === true) {
11150 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11151 } else {
11152 $status = html_writer::tag('span', $serverstatus, array('class' => 'badge badge-danger'));
11154 $row[1] = $status;
11156 $table->data[] = $row;
11158 // Indexed data.
11159 $row = array();
11160 $url = new moodle_url('/admin/searchareas.php');
11161 $row[0] = '4. ' . html_writer::tag('a', get_string('indexdata', 'admin'), array('href' => $url));
11162 if ($anyindexed) {
11163 $status = html_writer::tag('span', get_string('yes'), array('class' => 'badge badge-success'));
11164 } else {
11165 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11167 $row[1] = $status;
11168 $table->data[] = $row;
11170 // Enable global search.
11171 $row = array();
11172 $url = new moodle_url("/admin/search.php?query=enableglobalsearch");
11173 $row[0] = '5. ' . html_writer::tag('a', get_string('enableglobalsearch', 'admin'),
11174 array('href' => $url));
11175 $status = html_writer::tag('span', get_string('no'), array('class' => 'badge badge-danger'));
11176 if (\core_search\manager::is_global_search_enabled()) {
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) {
11293 // No need to call parent's validation here as we are PARAM_RAW.
11295 if ($this->util->is_whitelisted($data, $this->onlytypes)) {
11296 return true;
11298 } else {
11299 $troublemakers = $this->util->get_not_whitelisted($data, $this->onlytypes);
11300 return get_string('filetypesnotwhitelisted', 'core_form', implode(' ', $troublemakers));
11305 * Return an HTML string for the setting element.
11307 * @param string $data The current setting value
11308 * @param string $query Admin search query to be highlighted
11309 * @return string HTML to be displayed
11311 public function output_html($data, $query='') {
11312 global $OUTPUT, $PAGE;
11314 $default = $this->get_defaultsetting();
11315 $context = (object) [
11316 'id' => $this->get_id(),
11317 'name' => $this->get_full_name(),
11318 'value' => $data,
11319 'descriptions' => $this->util->describe_file_types($data),
11321 $element = $OUTPUT->render_from_template('core_admin/setting_filetypes', $context);
11323 $PAGE->requires->js_call_amd('core_form/filetypes', 'init', [
11324 $this->get_id(),
11325 $this->visiblename->out(),
11326 $this->onlytypes,
11327 $this->allowall,
11330 return format_admin_setting($this, $this->visiblename, $element, $this->description, true, '', $default, $query);
11334 * Should the values be always displayed in LTR mode?
11336 * We always return true here because these values are not RTL compatible.
11338 * @return bool True because these values are not RTL compatible.
11340 public function get_force_ltr() {
11341 return true;
11346 * Used to validate the content and format of the age of digital consent map and ensuring it is parsable.
11348 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11349 * @copyright 2018 Mihail Geshoski <mihail@moodle.com>
11351 class admin_setting_agedigitalconsentmap extends admin_setting_configtextarea {
11354 * Constructor.
11356 * @param string $name
11357 * @param string $visiblename
11358 * @param string $description
11359 * @param mixed $defaultsetting string or array
11360 * @param mixed $paramtype
11361 * @param string $cols
11362 * @param string $rows
11364 public function __construct($name, $visiblename, $description, $defaultsetting, $paramtype = PARAM_RAW,
11365 $cols = '60', $rows = '8') {
11366 parent::__construct($name, $visiblename, $description, $defaultsetting, $paramtype, $cols, $rows);
11367 // Pre-set force LTR to false.
11368 $this->set_force_ltr(false);
11372 * Validate the content and format of the age of digital consent map to ensure it is parsable.
11374 * @param string $data The age of digital consent map from text field.
11375 * @return mixed bool true for success or string:error on failure.
11377 public function validate($data) {
11378 if (empty($data)) {
11379 return true;
11382 try {
11383 \core_auth\digital_consent::parse_age_digital_consent_map($data);
11384 } catch (\moodle_exception $e) {
11385 return get_string('invalidagedigitalconsent', 'admin', $e->getMessage());
11388 return true;
11393 * Selection of plugins that can work as site policy handlers
11395 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11396 * @copyright 2018 Marina Glancy
11398 class admin_settings_sitepolicy_handler_select extends admin_setting_configselect {
11401 * Constructor
11402 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11403 * for ones in config_plugins.
11404 * @param string $visiblename localised
11405 * @param string $description long localised info
11406 * @param string $defaultsetting
11408 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11409 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11413 * Lazy-load the available choices for the select box
11415 public function load_choices() {
11416 if (during_initial_install()) {
11417 return false;
11419 if (is_array($this->choices)) {
11420 return true;
11423 $this->choices = ['' => new lang_string('sitepolicyhandlercore', 'core_admin')];
11424 $manager = new \core_privacy\local\sitepolicy\manager();
11425 $plugins = $manager->get_all_handlers();
11426 foreach ($plugins as $pname => $unused) {
11427 $this->choices[$pname] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11428 ['name' => new lang_string('pluginname', $pname), 'component' => $pname]);
11431 return true;
11436 * Used to validate theme presets code and ensuring they compile well.
11438 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11439 * @copyright 2019 Bas Brands <bas@moodle.com>
11441 class admin_setting_configthemepreset extends admin_setting_configselect {
11443 /** @var string The name of the theme to check for */
11444 private $themename;
11447 * Constructor
11448 * @param string $name unique ascii name, either 'mysetting' for settings that in config,
11449 * or 'myplugin/mysetting' for ones in config_plugins.
11450 * @param string $visiblename localised
11451 * @param string $description long localised info
11452 * @param string|int $defaultsetting
11453 * @param array $choices array of $value=>$label for each selection
11454 * @param string $themename name of theme to check presets for.
11456 public function __construct($name, $visiblename, $description, $defaultsetting, $choices, $themename) {
11457 $this->themename = $themename;
11458 parent::__construct($name, $visiblename, $description, $defaultsetting, $choices);
11462 * Write settings if validated
11464 * @param string $data
11465 * @return string
11467 public function write_setting($data) {
11468 $validated = $this->validate($data);
11469 if ($validated !== true) {
11470 return $validated;
11472 return ($this->config_write($this->name, $data) ? '' : get_string('errorsetting', 'admin'));
11476 * Validate the preset file to ensure its parsable.
11478 * @param string $data The preset file chosen.
11479 * @return mixed bool true for success or string:error on failure.
11481 public function validate($data) {
11483 if (in_array($data, ['default.scss', 'plain.scss'])) {
11484 return true;
11487 $fs = get_file_storage();
11488 $theme = theme_config::load($this->themename);
11489 $context = context_system::instance();
11491 // If the preset has not changed there is no need to validate it.
11492 if ($theme->settings->preset == $data) {
11493 return true;
11496 if ($presetfile = $fs->get_file($context->id, 'theme_' . $this->themename, 'preset', 0, '/', $data)) {
11497 // This operation uses a lot of resources.
11498 raise_memory_limit(MEMORY_EXTRA);
11499 core_php_time_limit::raise(300);
11501 // TODO: MDL-62757 When changing anything in this method please do not forget to check
11502 // if the get_css_content_from_scss() method in class theme_config needs updating too.
11504 $compiler = new core_scss();
11505 $compiler->prepend_raw_scss($theme->get_pre_scss_code());
11506 $compiler->append_raw_scss($presetfile->get_content());
11507 if ($scssproperties = $theme->get_scss_property()) {
11508 $compiler->setImportPaths($scssproperties[0]);
11510 $compiler->append_raw_scss($theme->get_extra_scss_code());
11512 try {
11513 $compiler->to_css();
11514 } catch (Exception $e) {
11515 return get_string('invalidthemepreset', 'admin', $e->getMessage());
11518 // Try to save memory.
11519 $compiler = null;
11520 unset($compiler);
11523 return true;
11528 * Selection of plugins that can work as H5P libraries handlers
11530 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
11531 * @copyright 2020 Sara Arjona <sara@moodle.com>
11533 class admin_settings_h5plib_handler_select extends admin_setting_configselect {
11536 * Constructor
11537 * @param string $name unique ascii name, either 'mysetting' for settings that in config, or 'myplugin/mysetting'
11538 * for ones in config_plugins.
11539 * @param string $visiblename localised
11540 * @param string $description long localised info
11541 * @param string $defaultsetting
11543 public function __construct($name, $visiblename, $description, $defaultsetting = '') {
11544 parent::__construct($name, $visiblename, $description, $defaultsetting, null);
11548 * Lazy-load the available choices for the select box
11550 public function load_choices() {
11551 if (during_initial_install()) {
11552 return false;
11554 if (is_array($this->choices)) {
11555 return true;
11558 $this->choices = \core_h5p\local\library\autoloader::get_all_handlers();
11559 foreach ($this->choices as $name => $class) {
11560 $this->choices[$name] = new lang_string('sitepolicyhandlerplugin', 'core_admin',
11561 ['name' => new lang_string('pluginname', $name), 'component' => $name]);
11564 return true;